How to bind set object to controller in spring - java

I am trying to make a post call to a controller, but the object I am expecting contains a Set datatype and I am unsure how the post data should look.
Models:
public class Notebook{
private string name;
private Set<Todo> todos;
}
public class Todo{
private String name;
}
Controller
#RequestMapping(method = RequestMethod.POST)
public void createNotebook(Notebook q){
questionnaireService.saveOrUpdateNotebook(q);
}
Currently I have tried posting like the example below:
curl --data "name=some notebook&todos[0].name=todo1&todos[1].name=todo2" http://localhost:8080/api/notebook
Doesn't seem to work. Anyone have experience with Sets?

You should qualify Notebook q with #RequestBody annotation so that the request can be mapped to an object of type Notebook. More about the format of the input data and the converters in Spring MVC doc: Mapping the request body with the #RequestBody annotation.
We send data from the front-end in JSON format and use Jackson JSON to convert it to the Java object. If you go that route, you can directly declare the todos as Set<String> and the input would be
{
name: "some notebook",
todos: ["todo1", "todo2"]
}

Related

How to map a request in Spring MVC to a Java Object

I'm new to Spring, and since Spring provides many ways to map an HTTP request to Java objects, I'm hoping someone could advice me how to resolve this:
I have a client that sends a request having
ContentType: application/x-www-form-urlencoded
Some of the request parmeters have names such as
"form_data[orderStatus]", "form_data[orderNumber]", etc'
I have no control over this client!
I have a java class (#Component) called MyOrder which looks as follows:
#component
#Scope("prototpe")
public class MyOrder {
private String orderStatus;
private String orderNumber;
//etc'
public void setOrderStatus(String orderStatus) {
this.orderStatus = orderStatus;
}
//public setter for all other properties, as the above
}
What is the simplest way to create an instance of MyOrder
populated with all values of all "form_data[]", so that I can have a controller method having a signature that includes a MyOrder parameter, such as:
public ModelAndView saveNewOrder( #RequestParam("foo") String foo,
#ModelAttribute("myOrder") MyOrder anOrder) {
//... impl'n here
}
The best solution I could think of was to use a Web Filter which would flaten request params names such as "form_data[attrib1]" to "attrib1", and then Spring would do all the work of populating the MyOrder instance.
One disadvantage of this is that the request may have both "form_data[attrib1]" and "attrib1" parameters. For example:
form_data[orderStatus]=ok
orderStatus=fail
In this case i want MyOrder.orderStatus to have the value "ok".
Any good way of utilizing Spring create MyOrder from the request?
As an alternative, that does not use the class MyOrder, is there a way to have Spring map all the form_data[] parameters and their values to a map, so that i can have the controller method below?
public ModelAndView saveNewOrder( #RequestParam("foo") String foo,
<some annotation> #Map<String,String> formFieldsOfAnOrder) {
//... impl'n here
orderStatus = formFieldsOfAnOrder.get("orderStatus");
//or at least:
orderStatus = formFieldsOfAnOrder.get("form_data[orderStatus]");
}

Extracting parameters from URL with POST method

I have something like this :
#RestController
#RequestMapping("/prop")
public class PropController {
#RequestMapping(method = RequestMethod.POST)
public Prop getProp(#ModelAttribute PropForm propForm) {
//calling methods and stuff using propForm
}
}
My PropForm class :
#Data
public class PropForm {
private String att1;
private String att2;
private String att3;
}
Now I am calling this URL :
http://localhost:8080/prop?att1=blabla&att2=blob&att3=test
I want to extract the parameters from the URL and put them in my propForm.
I've tried replacing #ModelAttribute by #RequestBody and then by #RequestParam. It's still not working, I always get a NullPointerException when running the application.
Please, note that I need to use POST method. I already have it working using GET method
FIRST Make sure you have getters and setters in your PropForm class...
Then, you need to put into your model the Form entity:
model.put("NAME", propForm);
And declare method like this:
#RequestMapping(method = RequestMethod.POST)
public Prop getProp(
#ModelAttribute PropForm propForm
#Valid #ModelAttribute("NAME") PropForm propForm)
// ^ you're missing the name!
{
// do your stuff....
return (Prop) propForm;
}
I think you controller and mapping is ok.
But the problem is you are expecting a post request in the mapping, and you are calling
http://localhost:8080/prop?att1=blabla&att2=blob&att3=test
Now this will generate a GET request Not Post. You cannot send a post request using only url.
If you cant use a form for sending the request then you need to use any 3rd party to generate a POST request
like you can use jquery $.post()
And also att1 att2 will not help unless you bind the object with the model attribute.

Converting JSON to model object fails while using #JsonProperty

The conversion of JSON to a model object fails when #JsonProperty annotation is used as follows:
Controller class snippet:
#RequestMapping( value = "/show", method = RequestMethod.POST)
public String doControl(#ModelAttribute User user, HttpServletRequest request ){
return user.getId();
}
Model class snippet:
public User{
#JsonProperty("user_id")
private id;
#JsonProperty("user_name")
private name;
//getters and setters
}
When I pass an a json {"user_id":1, "user_name":"foo" } with the POST request User fields are null. Will Jsonproperty annotation work while using ModelAttribute annotation?
It will work with #RequestBody. With #RequestBody you specify to Spring MVC that the annotated object is inside HTTP request's body. Spring MVC will then try to decode the object using appropriate HTTPMessageConverter - you want it to use message converter for json, so your POST request should include correct Content-Type header (e.g. Content-Type: application/json).
If you don't specify #RequestBody Spring MVC will try to populate the object using request parameters (e.g. as if you submitted regular HTTP POST form).
Hence:
public String doControl(#RequestBody User user, HttpServletRequest request ){...}

How to tell Jersey Jackson to not serialize one resource?

I have a REST API implemented using Jersey, I am using the Jackson feature for automatically serialize objects to JSON, there is a special case where I need to return a JSON string that represents an script. As the script can have different unknown structures I cannot just serialize it to an Object, that script comes from a column in a db table. At the same time I have a full Script object that contains all the information of the DB including the script string as a String property.
What I want is to tell Jersey-Jackson not to serialize (skip) the endpoint GET /script/{scriptId}, look at the code:
#Path("script")
#Produces(MediaType.APPLICATION_JSON)
public class ScriptResource {
private ScriptService service;
#GET
#Path("{scriptId}")
public String getScript(#NotNull #PathParam("scriptId") Integer scriptId) {
return service.getScript(scriptId); // returns a a valid JSON String
}
#GET
#Path("full/{scriptId}")
public Script getFullScript(#NotNull #PathParam("scriptId") Integer scriptId) {
return service.getFullScript(scriptId); // returns a Script object
}
}
The #Produces annotation is the one that triggers the automatic transformation via Jackson, I would like to configure Jackson and exlude the resource endpoint that I don't want to be converted automatically.
I don't want to:
Use Response as a return type
Change the Produces annotation to avoid Jackson
Use a Map as a return type which I would feed by parsing the String
One more option which you can consider in addition to those mentioned by #wasabi, is having a wrapper class for your string which would customize the Jackson serialization so it would not be converted to JSON. That can be done by using the combination of the #JsonValue and #JsonRawValue annotations on the getter method.
Here is an example wrapper:
public class RawJsonString {
private final String json;
public RawJsonString(String json) {
this.json = json;
}
#JsonRawValue
#JsonValue
public String getJson() {
return json;
}
}
... then your modified resource method would look as follows:
#GET
#Path("{scriptId}")
public JsonRawString getScript(#NotNull #PathParam("scriptId") Integer scriptId) {
return new JsonRawString(service.getScript(scriptId)); // returns a a valid JSON String
}
If you just want to make sure that some fields in Script class would not be serialized by Jackson, you could easily do so by annotating such fields with #JsonIgnore.
If you would like to have more control over the generated JSON, I would implement a custom JsonSerializer, and refer to it using #JsonSerialize annotation in your Script class. See this post for an example.
If you cannot modify Script class, you could also implement a MessageBodyWriter. See this post for an example.

Spring restful webservice returning JSON

I just took the tutorial over at Spring.io http://spring.io/guides/gs/rest-service/ and created a simple rest service. But, does anybody know how I can return multiple objects in JSON format? If I for instance have a person class with a name and an id, how can I add three persons to /persons?
You can use the #ResponseBody annotation and just return whatever you want, providing that those objects can be jsonized.
For example, you can have a bean like this:
#Data
public class SomePojo {
private String someProp;
private List<String> someListOfProps;
}
and then in your controller you can have:
#ResponseBody
#RequestMapping("/someRequestMapping")
public List<SomePojo> getSomePojos(){
return Arrays.<String>asList(new SomePojo("someProp", Arrays.<String>asList("prop1", "prop2"));
}
and Spring by default would use its Jackson mapper to do it, so you'd get a response like:
[{"someProp":"someProp", "someListOfProps": ["prop1", "prop2"]}]
The same way, you can bind to some objects, but this time, using the #RequestBody annotation, where jackson would be used this time to pre-convert the json for you.
what you can do is
#RequestMapping("/someOtherRequestMapping")
public void doStuff(#RequestBody List<SomePojo> somePojos) {
//do stuff with the pojos
}
Try returning a list from the method:
#RequestMapping("/greetings")
public #ResponseBody List<Greeting> greetings(
#RequestParam(value="name", required=false, defaultValue="World") String name) {
return Arrays.asList(new Greeting(counter.incrementAndGet(),String.format(template, name)));
}

Categories