how to bind different POST objects with one request mapping in Spring? - spring

Here is how my url looks like
http://localhost/controller/check
Here is the requestmapping
#RequestMapping(method = RequestMethod.POST)
public Data check(#RequestBody final Check checkRequest, final HttpServletResponse servletResponse) {
// service layer
}
Now I want the Check object to handle both the following POJOS.
public class Check {
String name;
String email;
}
public class Check {
int age;
Location location;
}
The request mapping should handle the following POST body.
{"age" : 23, "location" :{ "region": XXX, "country":"xxx", "zipcode":xxx}
and
{"name": "yyyy", "email":"hello#bar.com"}
what is the recommended approach here?

If it makes sense just combine the attributes into just 1 class. The json mapping will work with the missing atributes will be mapped to null.
If you still need 2 different models create a wrapper called CheckRequest which has 2 memeber variables checkName & checkLocation and post the appropriate json

I will extend the Check class and will put the newly extended class in the request body.

Related

Why does RestTemplate returns ArrayList<LinkedHashMap> instead of real list of model type?

It is a Springboot project. The code snip is as below. At line 59, the desired retrun type for restTemplate.getForEntity is List<Template>. While debugging, I find that the actual return type is an ArrayList contains many LinkedHashMap.
While LinkedHashMap is not sub class of Template. I don't know why the expect result type and the actual result type match.
Could anyone tell why it doesn't return ArrayList<Template>, instead of ArrayList<LinkedHashMap>? Thanks.
Template is an model defined in our project.
public class Template {
private String id;
private String name;
private String content;
xxx getters and setters
}
And it is a controller where the resttemplate is invoked.
#PostMapping(value = "/getTemplatesByGroup", produces = "application/json;charset=UTF-8")
#ResponseBody
public EUDataGrid<Template> getTemplatesByGroup(#RequestParam(defaultValue = "-1") Integer groupId) {
EUDataGrid<Template> grid = new EUDataGrid<>();
xxxx
List<Template> list = restTemplate.getForEntity(urlFullTemplates, ArrayList.class).getBody();
xxxx
return grid;
}
Json result format as below
[
{
"id": 1788,
"name": "xxxx",
"content": "xxxxx."
},
{
"id": 1787,
"name": "xxxxx",
"content": "xxxx"
}
]
Edit:
I googled a lot for this issue again. It is a common problem. There are similar scenarios some guys also encountered. I add the link in the foot of this post.
It seems that this is a bug of RestTemplate to handle generic properly. And there are ways to resolve this.
Here I want to know, why it doesn't throw exception when restTemplate returns ArrayList<LinkedHashMap> and assign it to List<Template>? They are differnt types. This is some kind of like assgin an int to a string.
I guess there is some magic with generic type. Could someone tell more about this? Thanks.
Unable to get a generic ResponseEntity<T> where T is a generic class "SomeClass<SomeGenericType>"
Using Spring RestTemplate in generic method with generic parameter
RestTemplate: how to get generic List response
Here is the problem in JSON response you are getting List<Template> or Array of Template, but in the responseType you just specified ArrayList where jackson doesn't know which type of ArrayList it is
limitation See the limitation
getForEntity(URI url, Class<T> responseType)
This sends a request to the specified URI using the GET verb and converts the response body into the requested Java type. This works great for most classes, but it has a limitation: we cannot get lists of objects.
One way is simple just specify Array type
Template[] list = restTemplate.getForEntity(urlFullTemplates, Template[].class).getBody();
Or use the exchange method

Deserialize separate key-value pair from Json string

I have a class
Entity implements org.joda.beans.Bean {
String name;
double weight;
....
}
I have an endpoint like this:
#RequestMapping(value = "CREATE", method = POST)
public void createEntity(#RequestBody Entity entity) {
logic.createEntity(entity);
}
Frontend sends a Json string to this endpoint:
{"name": "Bob", "weight":"99.7"}
Now I want to have another endpoint to update the entity.
It accepts json strings where only part of the attributes are set:
{"weight":"99.8"}
Its signature could be like this:
#RequestMapping(value = "UPDATE", method = POST)
public void updateCompany(#RequestBody Map<String, String> update1) {
Map<String, Object> update2 = deserialize(Entity.class,update1);
logic.updateEntity(update2);
}
The question is, how to implement the method deserialize which takes pair of Strings ["weight","99.8"] and converts it to the pair String-Object: ["weight", Double.valueOf("99.8")] because it knows, that the type of weight is double as declared in the class Entity. Such conversion was done already while preparation of arguments for the method createEntity(), now I want to extract it as a separate method call.
Deserialize to Map<String,String>
Iterate over each entry
Crete String->Double pair
Put to Map<String,Double>

Bind json value to two object in spring MVC restful

At present what I have is one view in HTML for entering Person's details and Company's details. I am using spring MVC framework restful.
I create json and send request using Ajax to Restcontroller.based on URL pattern create method is called .e.g. json is
{"name":"rohit","address":"Pune","company":"ABC"}
Here above name and address belong to person bean and company belongs to company bean. I want the json value bind to their respective bean. How to do it? I have tried the code below but I know it won't work.
#Requestmapping(value="/createperson",method=method.post)
public #Responsebody String createperson(#Requestbody person,#Requestbody company)
{
//Some code to save
}
I have a form, which will input the person's details and the person's company details.
What I want is that when this form is submitted, some of its fields are bound to Person object properties and some to Company object properties. How can this be done? And how to do validation for json value and send all errors as json responsive again back if there are any errors.
You can only have one #RequestBody. Spring then looks at the content-type header and finds an appropriate HttpMessageConverter which will read the entire http entity body (input stream) into a single object.
What you have basically done is try to merge Person and company into a single JSON object, and thereby flattened the structure. If you want spring to handle that, you need to create a new object with the same (flat) hierarchy. Or you need to create a wrapper class PersonAndCompany which contains both a Person and a Company, and then change the JSON to match the structure, so it looks like this.
{
"person" : {
"name":"rohit",
"address":"Pune"
},
"company" : {
"name":"ABC"
}
}
you should do like this if you are using relationship between Person and Company otherwise it is better to use single bean instead of two.
#ResponseBody
#RequestMapping(value = "/createperson", method=RequestMethod.POST ,consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Person> createperson(#RequestBody Person person) {
if(error found ){
Person p new Person();
p.setError(" error message ");
return new ResponseEntity<Person>(p,HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Person>(person,HttpStatus.OK);
}
public class Person {
private String name;
private String address;
Company company;
String error;
--- setters getters
}
public class Company {
String compName;
--- setters getters
}
input json
{"name":"person name ","address":"person address ","company":{"compName":"company name"}}

Map #CookieValue, #RequestHeader etc. to POJO in Spring Controller

I have a bunch of params in my controller and want to map all of them to a separate POJO to keep readability. There is also a #CookieValue, #RequestHeader I need to evaluate and aim for a solution to also map them to that POJO. But how?
I saw a possible solution on a blog but it doesn't work, the variable stays null.
Controller:
#RequestMapping(path = MAPPING_LANGUAGE + "/category", produces = MediaType.TEXT_HTML_VALUE)
#ResponseBody
public String category(CategoryViewResolveModel model) {
doSomething();
}
And my POJO is this:
public class CategoryViewResolveModel {
private String pageLayoutCookieValue;
public CategoryViewResolveModel() {
}
public CategoryViewResolveModel(
#CookieValue(value = "SOME_COOKIE", required = false) String pageLayoutCookieValue) {
this.pageLayoutCookieValue = pageLayoutCookieValue;
}
... some other RequestParams, PathVariables etc.
}
According to the documentation it's not possible for #CookieValue and #RequestHeader.
This annotation is supported for annotated handler methods in Servlet
and Portlet environments.
Take a look at:
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/
instead of using getParameter to access request parameters you can use getHeader to retrieve the header value and so define your CategoryViewResolveModel just as you were requesting

From request object to the database

I have an app with an AngularJS front-end and a Spring MVC back-end. I'm having some trouble with converting/mapping request objects to domain/dto objects.
On one page you can add a new order to the system, the POST payload would look something like this:
{
memo: "This is some extra info for order",
orderLines: [{productId:3, quantity:4}, {productId:2, quantity:5}, {productId:1, quantity:4}],
shippingDate: "2014-10-08T19:16:19.947Z",
warehouseId: 2
}
The Spring MVC controller method looks like this:
#RequestMapping(value = "/order", method = RequestMethod.POST)
public ResponseEntity<Void> addOrder(#RequestBody #Valid OrderRequest orderRequest, UriComponentsBuilder b) throws Exception {
// the magic
}
Where OrderRequest is filled with the values of the POST request, the OrderRequest and OrderLineRequest look like this:
public class OrderRequest {
private Long id;
private Date shippingDate;
private String memo;
private List<OrderLineRequest> orderLines;
private Long warehouseId;
public OrderRequest() {
}
// getters and setters ommitted
}
public class OrderLineRequest {
private Long id;
private String productCode;
private int quantity;
public OrderLineRequest() {
}
}
My question now is, in order to save an Order object with orderService.add(order) I need to construct the Order object based on the values that were sent in the request. Where/how do I do this?
OPTION 1
The OrderRequest class could have a makeOrder() method with just returns an Order object like so:
public Order makeOrder() {
Order order = new Order();
order.setMemo(this.memo);
order.setShippingDate(this.shippingDate);
...
}
Then I'd have to map the OrderLineRequest which could have their own makeOrderLine method:
public OrderLine makeOrderLine() {
OrderLine orderLine = new OrderLine();
orderLine.setQuantity = this.quantity;
...what to do with only the productId?
}
As you can see I can set the quantity but in the request I only received the productId, but in the database I save the productCode, productName as well, so I need that info from the database, but I don't want to make a database call from the Request object...I also don't want to half of the mapping in the request object and the rest of the mapping in the controller where I do have access to the services.
OPTION 2
I can use Dozer to do the mapping for me, but that would mean injecting the services into the Dozer custom converters which seem equally unclean to me...
OPTION 3
I pass the OrderRequest object to the service layer and let the service layer handle it, but my question would remain, how exactly would the service layer convert it, say you have the method addOrder like this:
public void addOrder(OrderRequest orderRequest) {
}
Would you call another service to convert from one to the other as I don't really want this conversion in a business logic method?
Any help would be appreciated
use the #RequestBody to map your jsonObject that is send with the request , to a DTO .
please refer to the following tutorial .
hope that helps .
and please ask if there is something not clear .

Resources