How are MessageBodyReaders used - java

Given something like:
e.g.
class User{
String name;
String someField1;
}
#Consumes("some/media-type")
class ResourceA{
public Response test(#FormParam("u") User u, #FormParam("f) String someField){
}
}
Couple of questions:
Will a single MessageBodyReader used to de-serialize User or will each field in user be de-serialized by a different reader?
Is #context required on any/all of these?
Is #FormParam required on the fields in the User class?
I'm trying to understand if the server will take the list of readers available and for each param in test, check if ANY of the readers can de-serialize that type. Or if the first reader which matches the media type consumed is expected to de-serialize all the params.
If the server is iterating through each parameter and for each parameter finding the most appropriate reader, it kind of makes sense that the input stream being passed to readFrom is the same instance, and each reader is advancing through the input stream. Is this the case or am I totally misunderstanding how the MessageBodyReader is meant to be used?

Have a look at this documentation on how entity providers are selected. In particular:
Procedure 7.2. MessageBodyReader Selection Algorithm
Obtain the media type of the request. If the request does not contain
a Content-Type header then use application/octet-stream media type.
Identify the Java type of the parameter whose value will be mapped
from the entity body. The Java type on the server is the type of the
entity parameter of the resource method. On the client it is the Class
passed to readFrom method.
Select the set of available MessageBodyReader providers that
support the media type of the request.
Iterate through the selected MessageBodyReader classes and,
utilizing their isReadable method, choose the first
MessageBodyReader provider that supports the desired combination of
Java type/media type/annotations parameters.
If Step 4 locates a suitable MessageBodyReader, then use its
readFrom method to map the entity body to the desired Java type.
Otherwise, the server runtime MUST generate a NotSupportedException
(HTTP 415 status code) and no entity and the client runtime MUST
generate an instance of ProcessingException.
#Context is not required and #FormParam does not need to be added to your bean - just to the REST resource method.

It appears it works how I suspected.
Looking at the source for RESTEasy, the MethodInjectorImpl class uses the same request instance which has an input stream. For each parameter the injector discovers the most appropriate reader.
The input stream is not touched and is advanced by each reader which parses a value from the request.

Related

Java Spring #ResponseBody return XML with produce attribute

Recently, I'm trying to get the xml file under the resources file. Then all the questions come out.
I read so many articles say something like:
#ResponseBody tells a controller that the object returned is
automatically serialized into JSON and passed back into the
HttpResponse object.
So my question is: the produces attribute is used to narrow the mapping by the media types that can be produced by the mapped handler, not to declare the media types the mapped handler will produce, right? Then how should I return xml content in the condition of automatically serialized into JSON format? I guess I mess up the meaning of the content showing in the view with HTTP response body... Please someone explain to me nicely and patiently :"(

How to pass list of parameters in rest get call (like filter object in eCommerce )

In my application, there is a requirement of getting data based on some parameters.
I just want to what is the better way to do.
The one way is, I can pass the list of parameters as a path variable.
The second way is, I can pass the request body, I think it is vague and I am not sure it is possible or not.
You can find the code below:
#GetMapping("/cities/{cityName}/latitude/{latitude}/longitude/{longitude}/cityId/{cityId}/street/{street}")
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude() {
}
I just want to know how can I achieve the same.
There is one more question, E-commerce companies have big filter criteria so how they are achieving.
Although there is no hard & fast rule but I generally avoid sending a body in GET request because it's a bad design. You should also refer to this SO Post which contains discussion about using body in GET request. It's an opinionated post and there is no clear YES or NO, but you will get an idea.
HTTP GET with request body
You can either use Path params or query params depending on what those field represent.
Regarding the difference or which to use when I am quoting this answer, which mentions that although there is no hard rule but generally it's better to use params which can uniquely identify the resource as Path param (e.g. id, name etc) and if your param is supposed to do something like filtering/sorting e.g. records after Jan 1 2019 , then go for query param.
Also personally in one of my APIs (which performs filtering), I am using a generic query param, where I pass on JSON object in my query. So basically my API needs to search an object based on variable/multiple attributes. E.g. I have in my db , objects which have certain voltage, current, size etc. values. So, request might come with a combination of 1 or more. So to keep my API flexible, I have provided a query param which can accept JSON object.
So I make a request like this:
{{SERVER}}/api/search?query={voltage:12V,size:10}
And in my API, I can convert this json object to corresponding POJO:
#GET
#Path("/search")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response search(#QueryParam("query") String queryParam) throws Exception
{
Myobj obj = new Gson().fromJson(queryParam, Myobj.class);
// rest of code
By passing the parameters in the path, you are restricting yourself to extend your API. If you want to extend your API, for example, if you want to filter with criteria as Street1 (or) Street2 then your path wouldnot support it and it will force you to update your API. It is better to pass criteria objects in the body or url parameter. Amazon India is passing criteria like below. I have choosen mobiles with criteria as Manufacturer = Samsung or MI, Storage as 8gb or 4gb and they simply appended the criteria in the query parameters.
There is a third way, Request Params.
#GetMapping
public ResponseEntity<ResponseContainer<CityDto>> getCityByCityNameOrLatitudeAndLongitude(#RequestParam("cityName") String cityName, #RequestParam("latitude") String latitude, #RequestParam("longitude") String longitude){
// Your code
}
For more: 16.3.3.3 Binding request parameters to method parameters with #RequestParam
Parameters using this annotation are required by default, but you can specify that a parameter is optional by setting #RequestParam's required attribute to false (e.g., #RequestParam(value="id", required=false)).
https://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#mvc-ann-requestparam

only Response entity shown in Postman

I have a response which produces #Produces(MediaType.APPLICATION_JSON) like this:
return Response.status(200).entity(product).entity("some message").build();
why postman shows me unexpected s in json view result and some message in xml view
For the Response.ResponseBuilder.entity(Object)` you can see
public abstract Response.ResponseBuilder entity(Object entity)
Set the response entity in the builder.
Any Java type instance for a response entity, that is supported by the runtime can be passed. It is the callers responsibility to wrap the actual entity with GenericEntity if preservation of its generic type is required. Note that the entity can be also set as an input stream.
A specific entity media type can be set using one of the type(...) methods.
Since you call this method twice, only the last value is saved.
Since "some message" is not at all a JSON syntax, it is saying that the s (from some) is unexpected, it is expecting a {

Override HttpMessageConverter for Spring RequestBody

I have the following code:
#RequestMapping(value="/mobile/device", method = RequestMethod.PUT)
public ResponseEntity<Void> flagDevice (#RequestBody List<MobileDeviceData> devicedataList, #RequestHeader(value="special_code") String specialCode) {
// Implementation details
}
Each instance of MobileDeviceData that gets created needs to have a param field filled in with the RequestHeader special_code.
How would I go about doing this so that it is fully populated by the time the flagDevice method body gets called?
Thanks in advance.
This is non trivial.
An HttpMessageConverter is already provided that deserializes the JSON, that's the MappingJackson2HttpMessageConverter. It has access to request headers. You could extend that class to also use the headers for deserialization (this is extremely difficult to do generically, as opposed to only for MobileDeviceData).
You could use Spring AOP, intercept the method, retrieve the arguments, cast to the appropriate types, and assign the value yourself.
The solution I would go for is the simplest: do it yourself in the handler method. Loop the the List and use a corresponding setter to set the specialCode for each MobileDeviceData.
Another option is to define your own HandlerMethodArgumentResolver specifically for List<MobileDeviceData> parameters that need to be constructed from header vales.

Restlet response type

How can I return Restlet response in desired format?
I am using the method:
#Get ("json")
public Address sendResponse(){
Address add = getAddress();
return add;
}
Right now I have to explicitly convert java object to a json string as a response to browser. Can't it be taken care by Restlet framework itself?
Spring MVC's Restful implementation can do it. I am looking similar implementation in Restlet too.
In fact, there are two ways to do that with Restlet:
the explicit one using JSON representations. The JSONRepresentation if you use objects from org.json or the JacksonRepresentation if you want JSON / Object mapping. You can find below an example:
#Get ("json")
public Representation sendResponse(){
Address add = getAddress();
return new JacksonRepresentation<Address>(address);
}
the implicit one using converter. In this case, it's the code you gave. You must have in your classpath an appropriate converter such as the one provided by the org.restlet.ext.jackson extension. It will detect that a JSON content needs to be returned and implicitly convert your Address object to a JSON content.
Just for hint, the json media specified in the GET annotation tells Restlet to use the associated method to handle the request when application/json is defined for conneg (content negociation) with the accept header.
Hope it helps you.
Thierry
Try setting the response type to application/json instead of just json. Normally, you need to specify the correct MIME type. As you say, if you set the MIME type correctly, other frameworks will do the conversion automatically.

Categories