Decompose incoming JSON to Objects by fields with Spring MVC - spring

I need to decompose my incoming JSON by fields in me REST Controller with Spring Boot.
My request body:
{
"text": "my text",
"myEnum": "VALUE1"
}
And my controller:
#PatchMapping("/{id}")
Object updateEntity(#PathVariable Long id, String text, MyEnum myEnum) {
/* ... */
}
#RequestParam doesn't work because it's just for query string params, #RequestBody doesn't work too because it handle whole body. But I need decompose incoming body by fields and inject into controller. I know what I can use Map <String, String> for this, but I would like validate my incoming fields, and I have the fields with difference types. And I don't want to create one class by incoming body for each controller.

If I haven't misunderstood your requirement, the usual way to deal with incoming JSON is to define a class that reflects your expected input, and make that the controller method parameter annotated as RequestBody.
Spring Boot, by default, uses Jackson to deserialize to your class, and so if you use matching property names then you won't need any special annotations or setup. I think enums will be handled by default, as are other types (though you may need to provide some guidance for strings representing dates or timestamps). Any bad value will fail deserialisation, which I think you can handle in ControllerAdvice (though you'll want to double check that)

Related

Passing exact parameters to Web API in ASP.NET Core

I have written a Web API in ASP.NET Core, for which I need to pass 2 parameters; of them one is a string with grade, the other is of type list of studentInfo as shown here:
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, [FromBody] List<StudentsInfo> lststudents)
{
try
{
// My Logic
}
catch(Exception ex)
{
resp.flag = false;
resp.message = ex.Message;
}
return resp;
}
To test this API, I used ARC (Advanced Rest Client). I passed the data as like this in a POST request:
{
"grade": "B",
"lststudents": [
{ "StudentName": "abcdef", "RollNo": "user1"},
{ "StudentName": "abcdef", "RollNo": "user1"}
]
}
It throws a HTTP 400 status error with the following message :
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[SchoolHub.Model.StudentList]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'lststudents', line 2, position 13.
I'm unaware of this exception.
You have this issue because you are not sending the data in the format at which ASP.Net Web API expects. ASP.net Web API needs some special format when dealing with a value like string and value type (int, bool, etc) parameter are marked with FromBody attribute.
Just remove FromBody it will work. For better understanding go with this link.
Why do we have to specify FromBody and FromUri?
In Web API, general parameter binding rules for POST method are as follows -
Query string -> Primitive type
Request body -> Complex type
Now if you want to use POST method with Mixed parameters i.e in your case you are passing primitive (string) and complex (List), Web API will get the grade parameter from query string and student parameter from the request body.
Possible solutions to try -
In the ARC request it seems you are passing grade in request body instead of as a query string parameter. Try passing grade as a query string parameter.
Also add a class viz. StudentInfoRequest to wrap List<StudentsInfo> lststudents and then use StudentInfoRequest object to pass as parameter to UpdateActiveStudents method.
You dont need to mention [FromBody] in UpdateActiveStudents method as by default complex parameters are read from the request body by Web API.
Hope this helps!
You must add [ApiController] in the controller level. Code is as follows
[ApiController]
[Route("[controller]")]
public class studentController : ControllerBase
{
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, List<StudentsInfo>
lststudents)
{
//code
}
}

Validate when fields depend on one another

I am passing a request object name Person to controller. Lets say the object has 2 two fields. The following business rule apply:
If field age has a value < 18, the field sin should be left blank;
If not, it will produce exception with message the sin should be blank with age < 18 or another way is to set the field sin to empty string("").
What is the best way for me to validate those inputs when they depend on each other. My way to deal with them is to validate them inside the controller method. So it should look something like that
#GetMapping("/..."
public ResponseEntity<PersonResponse> getPersonResult(GetPersonRequest request)
{
if (request.getAge() < 18)
{
if (request.getSin.length > 0)
request.setSin("")
}
PersonResponse response = callThirdPartyAPIToRetrieveInformationAboutThatPerson(request)
return response ;
}
Is there any more elegant way to code ? Is it ok for the controller method to contain any validation logic like that ? am i violating the Single Responsibility in SOLID design ?
Yes, of course! And this is a good approach: single responsibility of classes - a controller is responsible for handling data, validator - for validation of data; open-closed principle - validated data is unchangeable by controller's method; Liskov principle correlates with the base OOP principles - a validator is separated entity and can be changed to another one without any additional manipulations; Interface Segregation is clear without any description (fully separated classes); Depency Inversion is also understandable - using annotation interface, controller does not know anything about its implementation. So, it's a really good approach from ideology and language syntax.
Implementation.
Create class-level #interface. All fields are accessible.
Create ConstraintValidator class with validation logic.
Set this annotation for #RequestBody in the controller method.
Add validation functionality for controller: #Validated for controller class and #Valid for #RequestBody entity in controller method.
If you need to handle validation exceptions, just throw a new exception and handle it in #ControllerAdvise class, no handling code in validation or controller classes.
Example of creation class-level validator in the official resource.

right way to retrieve query parameters in Spring Boot rest?

I am developing REST api using Spring Boot. I've a controller which accepts POST requests.
http://localhost:8085/carride/end-ride
In the above request i want to access the parameter ride_transection_id for finding particular transection object and also some other value as well.
So basically i have 3 way to do that.
1. i can use #PathVariable
#RequestMapping(value = "/end-ride", method = RequestMethod.POST)
public ResponseEntity<?> endRide(#PathVariable("ride_transection_id") long ride_transection_id,#RequestBody
SomeDTORequest someDTORequest ) {
//find transaction using path varibale
}
2.i can use #RequestParam
#RequestMapping(value = "/end-ride", method = RequestMethod.POST
public #ResponseBody item getitem(#RequestParam("ride_transection_id")
long ride_transection_id,#RequestBody SomeDTORequest someDTORequest ){
//find transaction using RequestParam varibale
}
i can use DTO Object SomeDTORequest and accept ride_transection_id into that with other value as well.
#RequestMapping(value = "/end-ride", method = RequestMethod.POST)
public ResponseEntity<?> endRide(#RequestBody SomeDTORequest someDTORequest ) {
//find transaction using path someDTORequest .getID()
}
i am little bit confuses.just want ask which is safest and right way to access the ride_transection_id ?
thanks
You can use any of them but every way is designed for a certain use.
Path variable:
is used when you need to access an entity using a certain field for example i want to access an order and this order is defined by id so to access this order i need the following request Get /order/{id}
Request Parameter:
when you want to send a specific variable or flag for a certain method
for example Get /orders?is_shipped=true, so this will get all shipped orders or you may need orders at certain page Get /orders?page=1
Request body:
when you need to update the entity by the put or patch request as you will update the entity using the entity's json representation which can be send through the request body
for example PUT /orders/{id}
body: {"title": "order_1"}
then the order with id {id} will be updated with the new title
Spring data rest
See also
Basically, all these 3 methods are fine. But if you want to develop or design RESTful services with best practices, I strongly recommend you should provide the querying service with #PathVariable and GET method such as GET /tickets/12. Otherwise, to digest request body with #RequestBody annotation to retrieve querying criteria for POST method is the second suggestion.
Because POST method is usually to be used for creating something. And for querying something, both #PathVariable and #RequestParam annotations are suitable for GET method. More specifically, #RequestParam is often to be used in Filtering, Sorting and Searching results. For example:
Filtering: GET /tickets?state=open - Here, state is a query parameter that implements a filter.
Sorting: GET /tickets?sort=-priority,created_at - Retrieves a list of tickets in descending order of priority. Within a specific priority, older tickets are ordered first.
Searching: GET /tickets?state=closed&sort=-updated_at - Retrieve recently closed tickets.
Please also refer to this article Best Practices for Designing a Pragmatic RESTful API.
Hope this helps you! :)

Passing json "data" array in Retrofit 2

I'm trying retrofit 2 for the first time and I have no idea how to tell it to get "Category" objects from an jsonarray named "data".
Method 1
If I do it like this it fails:
#GET("category")
Call<List<Category>> listCategories();
Method 2
But when I make a new model, called "Categories", which holds a List and is annotated with #SerializedName("data"), it works flawlessly.
#GET("category")
Call<Categories> listCategories();
My Question
Should I annotate something in the interface, like this
#GET("category")
#Annotation to look inside "data"
Call<List<Category>> listCategories();
Or should I annotate my "Category" model to tell Retrofit (or GSON)
that it lives inside the json array "data"?
JSON
{"data":[{"id":1,"name":"Fist Name","parent":0},{"id":2,"name":"Second Name","parent":1}]}
Method 2 Is correct and we use it when we dont want to use/define the json response object/arrays key names(field names). instead provide our own. Eg. In below code List object name is items but while Serialization and Deserialization it uses, what you have defined in #SerializedName annotation that is data.
public class Categories {
//solution 1
List<Category> data;//object name must match with the json response
//solution 2
#SerializedName("data")
List<Category> items;
}
Should I annotate something in the interface
No. There is no such annotation available and everything you can do is only in Response type class.

In Spring MVC 3, how do I bind an object to a query string when the query string parameters don't match up with the object fields?

A 3rd party is sending me part of the data to fill in my domain object via a query string. I need to partially fill in my domain object, and then have the user fill in the rest via a form. I don't have any control over the query string parameters coming in, so I can't change those, but I'd really like to be able to use Spring MVC's data binding abilities, rather than doing it by hand.
How can I do this?
To add some complication to this, some of the parameters will require extensive processing because they map to other objects (such as mapping to a user from just a name) that may not even exist yet and will need to be created. This aspect, I assume, can be handled using property editors. If I run into trouble with this, I will ask another question.
Once I have a partially filled domain object, passing it on to the edit view, etc. is no problem, but I don't know how to properly deal with the initial domain object population.
The only thing I have been able to come up with so far is to have an extra class that has it's properties named to match the inbound query parameters and a function to convert from this intermediary class to my domain class.
This seems like a lot of overhead though just to map between variable names.
Can you not just have the getter named differently from the setter, or have 2 getters and 2 setters if necessary?
private int spn;
// Standard getter/setter
public int getSpn() {
return spn;
}
public void setSpn(int spn) {
this.spn = spn;
}
// More descriptively named getter/setter
public int getShortParameterName() {
return spn;
}
public void setShortParameterName(int spn) {
this.spn = spn;
}
Maybe that is not standard bean convention, but surely would work?

Resources