How is this JAX-RS webservice working? - java

I'm going over few pieces of code and came across something like this
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces({ MediaType.APPLICATION_JSON , MediaType.APPLICATION_XML })
public Response getMedia(
#HeaderParam("X-META") String metaToken,
#QueryParam("provider") String provider, MultivaluedMap<String, String> formContext ,
#Context UriInfo uriInfo) {
Map<String, String> context = APIUtils.buildContext(formContext);
return getMediaInternal(metaToken, provider, context, uriInfo);
}
I know that Annotated vars are injected by jersey but I'm clueless as how the formContext is being injected. It's not annotated. What all values are put in here by jersey ? All the post parameters ? Whats a general rule to deduce whats being populated when not annotated ? Any pointers to reference material or a brief description of whats happening here is helpful

According to the Jersey User Guide, it seems like jersey will inject the MultiValuedMap<> type on a #POST request because form parameters are part of the message entity. This is the example:
Example 3.13. Obtaining general map of form parameters
#POST
#Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
// Store the message
}

Related

RestEasy rest client format query parameters

I need to call a 3rd party rest service from my Java application with a formatted URI:
.../rest/v1/search?filter[first_name]=john&filter[last_name]=smith
The reason for this format is, that there are several query fields (20+) and I can't create a #QueryParam parameter for every fieldname.
#POST
#Path("/rest/v1/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
Response searchCustomer(#QueryParam("filter") Map<String, String> filter);
My example with a map results in
/rest/v1/search?filter={first_name=John,+last_name=Smith}
How do I achieve the URI form with square brackets?
You can use #Context UriInfo to get a map of all the query params instead of hardcoding every param
example:
#POST
#Path("/rest/v1/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response searchCustomer(#Context UriInfo info){
//here are some different ways you can use the uriinfo
MultivaluedMap<String,String> map = info.getQueryParameters();
String firstName = map.getFirst("filter");
String lastName = map.getFirst("lastName");
}

How to pass List of hash map as query param in jersey

I tried something like below, M getting error
[[FATAL] No injection source found for a parameter of type public Response
#context UriInfo wont work as i need different data types as query param,like it may be integers and date.Kindly Help.
#GET
#Path("/getdetails")
#Produces({ "application/json", "application/xml" })
public Response getDetails(#QueryParam("field1") String fieldOne,#QueryParam("field2") List<HasMap<String,String>> fieldTwo){
//Processing
}
You will have to use POST and attach the list inside the request body
If the list your passing is json, you should also add the appropriate #Consumes value.
#POST
#Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
#Consumes(MediaType.APPLICATION_JSON)
public void getDetails(List<HasMap<String,String>> listFromClient) {
// do something with your list..
}

Best practices incoming object message inheritance using JAX-RS

I was wondering if there is a good solution to my problem, or is there good practices, on handling different messages from an object hierarchy.
So in short: I have an object hierarchy, lets say:
interface IMessage
abstract Message implements IMessage
class SimpleMessage extends Message
class ReportMessage extends SimpleMessage
class CostReportMessage extends ReportMessage
class IncomeReportMessage extends ReportMessage
... (like 3 other types, similar to the CostReportMessage)
So what I would like is to have one incoming JAX-RS endpoint method, since most of the handler code is the same for the classes, but we need some conditional parts in the code.
E.g:
#POST
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML + "; charset=UTF-8", MediaType.APPLICATION_JSON + "; charset=UTF-8"})
public Response createReport( final ReportMessage, #Context HttpHeaders headers ) {
...
...
}
So we have some methods like the one above, but I would like to make it one, like receive an incoming IMessage, and later handle it as the object's class needs it.
Do you have any advice on this problem? Or do you know any best practices, on how to solve this kind of problem?
You can use the parent class as the consumed object then copy all the properties using BeanUtils.copyProperties() to the new dedicated message object based on certain conditions, eg: you have messageTypeID variable in SimpleMessage to tell what kind of message it should be.
#POST
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML + "; charset=UTF-8", MediaType.APPLICATION_JSON + "; charset=UTF-8"})
public Response createReport( SimpleMessage message, #Context HttpHeaders headers ) {
SimpleMessage convertedMessage;
if(//condition//) {
convertedMessage = new ReportMessage();
BeanUtils.copyProperties(convertedMessage, message);
} else { ... }
...
}
Although I don't find the following solution "nice", or "elegant", but this seems to work:
step 1. create a function
private IMessage parseRequest(String request) throws IOException, JAXBException{
StringReader reader = new StringReader(request);
// unmarshaller here is a simple JAXB Unmarshaller for the message package
IMessage message = (SimpleMessage) unmarshaller.unmarshal(reader);
return message;
}
step 2. modify jaxb entry point
#POST
#Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_XML + "; charset=UTF-8", MediaType.APPLICATION_JSON + "; charset=UTF-8"})
public Response createReport( final String request, #Context HttpHeaders headers ) {
IMessage message = parseRequest(request);
...
...
return response;
}
So I think, this is because of the following (and this is only my theory, but I don't know where to look for the truth):
JAX-RS simply checks for the type, and creates an Unmarshaller correspondent to the type given in the method signature.
JAX-RS will fail on giving IMessage in method param (since the interface doesn't have a constructor)
Since the Unmarshaller created by JAX-RS will only create a context, that is aware of SimpleMessage, it won't be able to unmarshal any child class, e.g. CostReportMessage
The good solution would be providing a custom MessageBodyReader (#Provider annotation), but I couldn't get it to work
This may be because of how the jersey finds a content provider for a given type (goes from the most generic to the most specific, and because it finds a more generic content provider, than mine, it won't use it)

Injection of HttpServletRequest in Jersey POST handler

Consider this case:-
I am injecting HttpServletRequest in a Rest Service like
#Context
HttpServletRequest request;
And use it in a method like:-
#GET
#Path("/simple")
public Response handleSimple() {
System.out.println(request.getParameter("myname"));
return Response.status(200).entity("hello.....").build();
}
This works fine but when I try to send it through POST method and replace the #GET by #POST annotation, I get the parameter value null.
Please suggest me where I am mistaking.
You do not need to get your parameters etc out of the request. The JAX-RS impl. handles that for you.
You have to use the parameter annotations to map your parameters to method parameters. Casting converting etc. is done automaticly.
Here your method using three differnt ways to map your parameter:
// As Pathparameter
#POST
#Path("/simple/{myname}")
public Response handleSimple(#PathParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
// As query parameter
#POST
#Path("/simple")
public Response handleSimple(#QueryParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
// As form parameter
#POST
#Path("/simple")
public Response handleSimple(#FormParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
Documentation about JAX-RS Annotations from Jersey you can find here:
https://jersey.java.net/documentation/latest/jaxrs-resources.html

How to get a Map parameter in a REST service for different mime types?

How can I specify a map as one of the parameters of a REST service e.g
#Path("/servicepath")
#Consumes(MediaType.APPLICATION_XML)
public class MyResource {
#POST
public Response getMap(Map<String, List<Object>>) {
//code here
}
}
or
#Path("/servicepath")
#Consumes(MediaType.APPLICATION_JSON)
public class MyResource {
#POST
public Response getMap(Map<String, List<Object>>) {
//code here
}
}
I am using Jersey. Should I implement a MessageBodyReader for that? But implementing a reader for a generic type like Map seems a bad way for me. Maybe I should write a wrapper class on top of the Map object.
What is your ideas? Thanks.
The JAX-RS specification (section 4.2.4) does require that implementors (like jersey) provide a MessageBodyReader implementation for MultivaluedMap<String, String> which is used to consume application/x-www-form-urlencoded mime types. So for example, you can do something like this:
#Path("/servicepath")
#POST
#Consumes("application/x-www-form-urlencoded")
#Produces("text/plain")
public String doTheFormThing(MultivaluedMap<String, String> formdata) {
return formdata.toString();
}
Is this not sufficient for what you're trying to do?

Categories