I have a rest webservice (with jersey) which returns json list, if i call it directly it returns exactly this :
[{"success":false,"uri":"foo:22","message":"Unknown host : foo"},{"success":true,"uri":"localhost:8082","message":null}]
generated by this snippet :
#GET
#Path("/opening/")
public List<OpeningResult> testOpenings(#QueryParam("uri") List<String> uris) {
LOG.debug("testOpenings request uris :[" + uris + "]");
List<OpeningResult> openingResults = infoService.testOpenings(uris);
return openingResults;
}
It's a Collection of Pojo which look like this :
#XmlRootElement(name = "OpeningResult")
public class OpeningResult {
attributes
...
getter/setter
}
this Pojo is shared through a common jar between the server and the client.
i call the web service with this snippet :
Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/scheduler/rest/opening");
MultivaluedMap<String, String> params = new MultivaluedMapImpl();
for (String uri : uris) {
params.add("uri", uri);
}
List<OpeningResult> results = newArrayList(resource.queryParams(params).get(OpeningResult[].class));
I add some trace on the server side, i see that my rest service is called with the good parameters, buth on client side, i have this error :
Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"success"). Expected elements are <{}OpeningResult>
I don't find where it comes from ?
Modify your code to set up your client like this:
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, true);
Client client = Client.create(clientConfig);
I had the exact same problem until this question and its answers pointed me in the right direction.
The situation is caused by the default jersey-json module used for serialization to and from JSON, which does not handle certain JSON constructs properly.
You can set the FEATURE_POJO_MAPPING flag to use the Jackson library's JacksonJsonProvider for JSON serialization instead.
Check out the Jersey Client side doc on using JSON. It looks like you're at least missing the annotation:
#Produces("application/json")
But you could also be missing the POJO Mapping feature filters for both client and server side. These all seem to be minor configuration changes.
Related
I created a series of REST services in Java using Restlets. The majority of these services use JSON, and I have no problem accessing them using SOAP UI via a GET request. However, when I try to access POST based services using SOAP UI, the Representation entity parameter is always null. I have searched Stack Overflow as well as the web, but could find nothing which I either haven't already done, or which addresses my problem.
Here is the code for a POST resource which always seems to receive a null entity:
public class CreateAccountResource extends ServerResource {
#Post("json")
public Representation createAccount(Representation entity) throws IOException {
String message = null;
boolean result = true;
try {
String post = entity.getText();
Object obj = new JSONParser().parse(post);
JSONObject jsonObject = (JSONObject) obj;
String username = (String) jsonObject.get("username");
String password = (String) jsonObject.get("password");
String email = (String) jsonObject.get("email");
// more code
}
catch (Exception e) {
// handle exception here
}
}
}
And here is a screen shot from my SOAP UI showing the configuration I used when sending the request:
In case you are wondering, I am using IntelliJ in debug mode to inspect the value of the entity, and the project uses Maven.
I never use Restlet however I think that since you specify #Post("json") annotation for your createAccount method; the method is waiting for a json in the POST body instead of passing the values as a query parameters.
So probably you must change your actual POST with the query parameters to a POST call to your URL http://localhost:8080/MyApp/service/createAccount passing the parameters in the body as json:
{
"username" : "tim",
"password" : "password",
"email" : "tim#me.com"
}
In SOAPUI could be something like:
Hope it helps,
I have a shared API and services are annotated
#Produces({"application/json","application/x-jackson-smile"})
#Consumes({"application/json","application/x-jackson-smile"})
public class AServiceClass {
So default is JSON - this will be preferred when using browser AJAX calls.
However I have a RestEasy client which I create using
ProxyFactory.create(AServiceClass.class, url)
And I want this client to use SMILE for both inbound and aoutbound communication. Of course it picks first item from #Consumes and tries marshalling to JSON.
I'm using RestEasy 2.3.5
How to force the client to use SMILE marshalling?
How to force the client to accept SMILE instead of JSON?
Actually it turns out that you ( I mean I :-) ) can't do this.
Checking MediaTypeHelper.getConsumes() shows that always first annotation value is picked to determine marshalling media type.
return MediaType.valueOf(consume.value()[0]);
The same happens when it comes to figuring out accept header. The code uses MediaTypeHelper.getProduces()
It can be done by specifying the value for the header Accept
Response response = client.target(host + "/yourpath").request()
.header(HttpHeaders.ACCEPT, "application/x-jackson-smile")
.get();
You can also achieve this with a ClientRequestFilter, which is more usful if you are using proxies of your JAX-RS annotated classes. For example
public class AcceptedHeaderFilter implements ClientRequestFilter
{
private final MediaType acceptedType;
public AcceptedHeaderFilter(final MediaType acceptedType)
{
super();
this.acceptedType = acceptedType;
}
#Override
public void filter(final ClientRequestContext requestContext) throws IOException
{
requestContext.getHeaders().get(HttpHeaders.ACCEPT).clear();
requestContext.getHeaders().get(HttpHeaders.ACCEPT).add(acceptedType.toString());
}
}
If you are using Resteasy, you can register on your ResteasyWebTarget
MediaType contentType = MediaType.APPLICATION_XML_TYPE;
final ResteasyWebTarget target = buildTarget();
target.getResteasyClient().register(new AcceptedHeaderFilter(contentType));
CXF or Jersey will let you register the filter, but will require you to do it in a slightly different way.
I am getting a 415 Error when sending a form entry to another client resource via JSON. The target URI in my code below ("/message") works when not using the form (i.e. hit "/message" with a test mock object).
Here is my code to get the values of the form and do the post to the target resource. Am I missing something that needs to be done?
I am using the following:
Restlet: 2.1 RC5
GAE: 1.6.1
Form Restlet:
#Post
public void handlePost(Representation entity) {
final Form webForm = new Form(entity);
MessageEntity newMessage = new MessageEntity();
String subject = webForm.getFirstValue("subject");
String sendto = webForm.getFirstValue("email");
String message = webForm.getFirstValue("message");
newMessage.setCategoryID(subject);
newMessage.setAccountID(sendto);
newMessage.setMessageText(message);
ClientResource cr = new ClientResource(getRootRef()+ "/message");
cr.post(newMessage, MediaType.APPLICATION_JSON);
}
Target Resource ("/message")
#Post("json")
public void HandleRequest(MessageEntity messageEntity) {
// Logic here
}
Please let me know if you need more information
Thanks!
I have code that is very similar to yours that works fine. I am also running similar versions of Restlet and GAE. First question I have is are there other #Post methods in your Target Resource as sometimes the ordering matters.
Here are two versions of code that I have that work....
1)
public Representation postHandler() {
Reference commitsRef = new Reference(Consts.RESOURCE_BASE + "commitments/");
ClientResource commitsResource = new ClientResource(getContext(), commitsRef);
....
Representation commitsRep = commitsResource.post(commitForm);
That is posting a form to a Target resource that handles both #Post("json") and #Post("form")
2)
public Representation doPostFromGet() {
Reference takeActRef = new Reference(Consts.RESOURCE_BASE + "commitment/"
+ commitmentId + "/userActs/");
ClientResource takeActResource = new ClientResource(getContext(), takeActRef);
...
Representation takeActRep = takeActResource.post(newAct);
That is posting a Java object to a form that uses what I call the "Peierls magic". See:
http://tembrel.blogspot.com/2012/03/converting-forms-in-restlet-to-pojos.html
It allows you to have one post() in the Target and accept both forms and pojos.
On a minor note, if you are doing a post to add a new message, should the url be "/messages/" (plural) - and perhaps there is a typo somewhere? (An unlikely possibility, but I thought I would mention it).
Good luck,
RB
For some reason, I haven't found any normal way to do the following:
I want to Post a json object, and add additional parameters to the call (in this case, an authentication token).
This is a simple RESTful server in myUrl/server, which should give access to different resources of a "person" in the url myUrl/server/person/personCode/resourceName.
GET is easy, and requires no object, only parameters.
The problem arrises when I get to POST - how do I attach the JSON, and keep the other parameters as well?
The class (much has been removed for clarity and proprietary reasons...):
//Handles the person's resources
#Path("/person/{personCode}/{resourceName}")
public class PersonResourceProvider {
#GET
#Produces("application/json")
public String getPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Authenticates the user in some way, throwing an exception when needed...
authenticate(personCode, auth_token, resourceName);
//Returns the resource somehow...
}
#POST
#Produces("application/json")
public String postPersonResource(#PathParam("personCode") String personCode, #PathParam("resourceName") String resourceName, #DefaultValue("") #QueryParam("resourceData") String resourceData, #DefaultValue("") #QueryParam("auth_token") String auth_token) throws UnhandledResourceException, UnauthorizedAccessException {
//Again, authenticating
authenticate(personCode, auth_token, resourceName);
//Post the given resource
}
}
Now, the GET method works perfectly, when you go to
myUrl/person/personCode/resourceName, it gives me the correct resource.
The auth_token is used with every single call to the server (for now, authentication is done by comparing with a predefined string), so it's needed. All the other parameters are provided through the path, except for the authentication token, which should not be in the path as it does not relate to the identity of the required resource.
When I get to POST, it's a problem.
I know there's a way to tell the method it consumes a JSON, but in that case, what will happen to the other parameters (auth_token is one of them)?
Should I use Multipart?
Another related question, this is the first time I've designed such a server, is this design correct?
Thanks!
I am not sure I understand what you are trying to achieve. Let me try explain a few things - hope it will be relevant to your question:
#QueryParam injects parameters which are part of your path - i.e. the part of the URL that goes after "?".
E.g. if you have a URL like this:
http://yourserver.com/person/personCode/resourceName?resourceData=abc&token=1234
Then there would be 2 query params - one named resourceData with value "abc" and the other one named token with value "1234".
If you are passing an entity in the POST request, and that entity is of application/json type, you can simply annotate your post method using #Consumes("application/json") annotation and add another parameter to your method, which does not need to be annotated at all.
That parameter can be either a String (in that case Jersey would pass a raw JSON string and you would have to parse it yourself) or it can be a java bean annotated with #XmlRootElement annotation - in that case (if you also include jersey-json module on your classpath) Jersey will try to unmarshall the json string into that object using JAXB. You can also use Jackson or Jettison libraries to do that - see this section of Jersey User Guide for more info: http://jersey.java.net/nonav/documentation/latest/json.html
Found!
Client side:
Client c = Client.create();
WebResource service = c.resource("www.yourserver.com/");
String s = service.path("test/personCode/resourceName")
.queryParam("auth_token", "auth")
.type("text/plain")
.post(String.class, jsonString);
Server side:
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
#Path("/test/{personCode}/{resourceName}")
public class TestResourceProvider {
#POST
#Consumes("text/plain")
#Produces("application/json")
public String postUserResource(String jsonString,
#PathParam("personCode") String personCode,
#PathParam("resourceName") String resourceName,
#QueryParam("auth_token") String auth_token)
throws UnhandledResourceException {
//Do whatever...
}
}
In my case, I will parse the json I get in the server depending on the resource name, but you can also pass the object itself, and make the server consume an "application/json".
Would appreciate any code examples of how to call a SpringWS endpoint intrceptor from a Junit test class. Particularly on how to prepare a SOAP message context and endpoint object. The SOAP message in the context will need to have a custom SOAP header included.
Something like....
public class MyInterceptorTest
private static String "... my XML SOAP test message ...";
#Test
public testMyInterceptor() {
myMessageContext = ... Build a MessageContext with the XML message string;
myEndPointObject = ... Build an endpoint object;
boolean result = MyInterceptorClass.handleRequest(myMessageContext, myEndPointObject);
... Check results;
}
Any examples would be appreciated.
The MessageContext can be created by instantiating a DefaultMessageContext object. The request WebServiceMessage can created using the test support class PayloadMessageCreator, but this only appeared in Spring-WS 2.x.
The endpoint object can be anything - it depends what your interceptor does with it. If it doesn't actually use it, then you can just pass in null.
I had the same issue and was able to figure it out in part using #skaffman's suggestion.
Basically, I had a custom EndpointInterceptor that I wanted to test with real data so that I would know I had everything correct.
You will have to upgrade spring-ws-test and other spring-ws dependencies to version 2.0 or higher. I ended up using something different than PayloadMessageCreator.
final Source payload = new StreamSource(new StringReader(soapPayload));
SaajSoapMessageFactory saajSoapMessageFactory = new SaajSoapMessageFactory(MessageFactory.newInstance());
WebServiceMessage requestPayload = new SoapEnvelopeMessageCreator(payload).createMessage(saajSoapMessageFactory);
MessageContext messageContext = new DefaultMessageContext(requestPayload, saajSoapMessageFactory);
soapPayload is the string value of an entire soap envelope.
Something similar to this:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
...fill in your custom headers here
</soapenv:Header>
<soapenv:Body><someRequest>...</someRequest></soapenv:Body>
</soapenv:Envelope>
You will obviously need to fill in your request payload, any namespaces, as well as your custom headers.
I set the endpoint object to null as I was not doing anything with it as part of my interceptor.