Spring MVC controller parameters binding: replace parameters with POJO - java

I have a simple controller method with more than 7 parameters and would like to refactor it using a model object instead, i.e. to extract a parameter object:
#RequestMapping(value="/{one}")
public String controllerMethod(#PathVariable(value="one") String one, #RequestParam String two, ... #RequestBody body) {
...
}
I tried to extract an object with setters and getters and pathVariable and requestParameters are mapped by name. However I have troubles making the same for #RequestBody, it doesn't work for me even if I put #RequestBody into setter...
public class Parameters {
private String one; // pathVariable
private String two; // requestParameter
private String body;// requestBody - always NULL!
// other fields definition
public setBody(#RequestBody String body) {this.body = body}
//other setters/getters
}
How to keep #RequestBody parameter in extracted POJO?
Another question is how to control name of parameters, i.e. if
parameter name differs from the field name in POJO, is there any
annotation? This one doesn't work:
public void setOne(#RequestParameter(value="o") String one) {this.one = one}
How to mark the fields as required or give a default value like in the #RequestParameter annotation?

For the (1) I would simply keep #RequestBody as a separate parameter though I don't like it much.
Ok, looks like the only way of doing (2) and (3) is through customizing data binding: the similar question
Feel free to post another easy solution if you know about it.

Related

Pass value to controller in Spring MVC, what is the difference?

When I read some codes written with Spring MVC in an old project, I notice that the author using two similar method to pass value to controller as follows:
#RequestMapping(value="/print")
public String PrintInfo(#RequestParam("name") String name,#RequestParam("age") int age) {
.......
}
Or
#RequestMapping(value="/print")
public String PrintInfo(String name,int age) {
.......
}
Both them work well.
The difference is whether it use the #RequestParam.
So what's the main difference between them? Which one is better, and why?
This basically sounds to me like you're asking "what is a RequestParam and why should I use it?"
The RequestParam allows you to bind the method parameter argument to the web request parameter. Without any other attributes, your example tells Spring that a name and age parameter are required and Spring will know to associate those two parameters against the incoming request. You can optionally set required to false to, well, make the argument optional:
public String PrintInfo(#RequestParam("name", required = false) String name,
#RequestParam("age") int age) {
As an extremely useful feature, you can also provide a defaultValue in case you receive an empty value from the request. So you can do:
public String PrintInfo(#RequestParam("name", defaultValue="John Doe") String name,
#RequestParam("age") int age) {
...and you'll never deal with a null name.
Finally, using it also does some magic type conversions, like for example automatically using an Integer type. In your example, you could have used:
public String PrintInfo(#RequestParam("name") String name,
#RequestParam("age") Integer age) {
...and Spring would have boxed it automatically without you doing any extra work.
There's nothing inherently wrong with leaving off the RequestParam annotation, but you're essentially saying no to Spring enabling these features as you have in your second case.
Aside:
#RequestMapping(value="/print") can be more simply written as #RequestMapping("/print")
If the name of request parameter and the name of method arguments will be equal, then Spring will bind parameters automatically by names. For example, you have incoming GET request:
http://localhost:8080/print?name=somename&age=30
And controller method:
#RequestMapping(value="/print")
public String PrintInfo(String name,int age) {
...
}
In this case you don't need to specify #RequestParam annotation for parameter. Because names in request and names of methods args are equals.
But when names are not equals, then you need to specify the correspondence of names explicitly with #RequestParam. For example:
http://localhost:8080/print?user_name=somename&user_age=30
And controller method:
#RequestMapping(value="/print")
public String PrintInfo(#RequestParam("user_name") String userName, #RequestParam("user_age")int userAge) {
...
}
So #RequestParam needed to help the Spring make bindings properly, when request param names and method args names are different.
Acutally, many developers always use #RequestParam even when names are equal. For example empty #RequestParam:
#RequestMapping(value="/print")
public String PrintInfo(#RequestParam() String name, #RequestParam() int age) {
...
}
Because this annotation shows that argument comes from request and makes your code more clear and readable.

java jersey 2 how to process regular html form array with post?

I have a jersey endpoint
#Path(value = "/testfields")
#POST
#Consumes({"application/x-www-form-urlencoded"})
public Response acceptFields(#BeanParam MyWrapper initialWrapper,String entity) {
//.......
}
MyWrapper is:
class MyWrapper{
#FormParam("param1")
private String param1;
#FormParam("inners")
private List<MyInnerWrapper> inners;
//..getters setters
}
class MyInnerWrapper{
#FormParam("innerParam1")
private String innerParam1;
//.... getters setters
}
I have request POST entity string:
param1=aaa&inners[0]["innerParam"]=bbb&inners[1]["innerParam"]=nnn
The issue is I can obtain parent`s field param1 with #BeanParam annotation, but I can't obtain inner list of custom classes, why? How to process regular html form array? I need to get my class evaluated with its inner classes in my endpoint. I tried array, list of objects, nothing works. Thanks
AFAIK, that type of syntax is not supported in Jersey. If you want to use custom types for #FormParam, then you need to meet the requirements of at least one those listed in the Javadoc. One options is to just have a constructor with a String argument. For example
public class MyInnerWrapper {
private final String innerParam;
public MyInnerWrapper(String param) {
this.innerParam = param;
}
public String getInnerParam() {
return innerParam;
}
}
Then all you need to do is send this request
param1=blahparam&inners=first&inners=second&inners=third
Then you can have
#FormParam("inners")
private List<MyInnerWrapper> inners;
There's no need for an [] to specify indices. Duplicates are allowed. That's why we are allowed to have lists and arrays for the #FormParam types.
Now if you want to have more than one property and then try to map them like you currently are, I don't think that will work. At least not in Jersey. I would recommend maybe looking into using JSON if you need more complex types. Otherwise just stick to simple keys.

Entity properties serialized twice

In Spring 3.3 I have an entity which is mapped to a database table. In this entity class I have all properies annotated with #JsonProperty, for instance #JsonProperty("ID").
Stepping into the controller a service is called to get such an entity by using a DAO/repository. This works well but when I send this entity back to the requestor using #ResponseBody all properties are sent twice. Once as demanded but one more time beginning lowercase until the first camel case letter occurs.
An example...
public class MyEntity {
#JsonProperty("MYSpecialSuperId")
private String MYSpecialSuperId;
...
public String getMYSpecialSsuperId() {
return this.MYSpecialSuperId;
}
}
After JSON stringifying the result is:
{ "MYSpecialSuperId":""9", "myspecialSuperId":"9" }
Why is the property twice in the result and why is the lettering different???
BTW:
It was not my idea to let Java properties begin with an uppercase letter even yet with more than one uppercase letter.
Jackson's ObjectMapper uses the Java bean pattern. In other words, it expects the following
public class Foo {
public Object bar;
public Object getBar() {...}
public void setBar(Object bar) {...}
}
The getters and setters start with get and set, respectively, followed by the corresponding field name with its first letter capitalized. If you change your code to
public class MyEntity {
#JsonProperty("MYSpecialSuperId")
private String mySpecialSuperId;
...
public String getMySpecialSuperId() {
return this.mySpecialSuperId;
}
}
Note that the field starts with lowercase my instead of uppercase (regardless of the #JsonProperty value), I removed the extra s in getMYSpecialSsuperId and used a lowercase y. So now the field name matches the getter name and jackson knows that the property is the same and doesn't need to serialize twice.
If you have no choice, you can follow what Katona posted in the comments and use
#JsonAutoDetect(getterVisibility=Visibility.NONE)
to make jackson ignore the getters completely and only use the fields to serialize your JSON.
I think you have a typo in your accessor; if it has "SsuperId" it does not match name of the field; and as such field and getter are taken to mean different logical properties.

Multiple parameters into one Spring controller argument

I have a class setup like the following...
public class Person {
protected String firstName;
protected String lastName;
protected Integer age;
...
}
A controller that looks like this...
#RequestMapping(value={"/person"}, method = RequestMethod.GET)
public void returnPerson(Person person, ModelMap model, HttpServletResponse response) {
....
}
And I am passing in a straight URL such as...
Link
Is it possible to pass all of these into the "Person" argument in my controller rather than making numerous #RequestParam arguments for each one? Especially if I am going to be passing in a good amount of params?
Yes you can do that in the exact way you are describing as long as you are following the property naming convention for Person.
What problem do you get when you try it this way?
You can do exactly what you're asking for using model binding:
#ModelAttribute("person")
public Person getPerson() {
return new Person();
}
#RequestMapping(value="/person", method=RequestMethod.GET)
public void handle(#ModelAttribute("person") Person person, BindingResult errors, ModelMap model, HttpServletRequest req) {
...
}
The model attribute will be initialised by the getPerson method and when the handle method fires, any parameters from the request will be automatically bound to corresponding properties in the new Person object. The BindingResult holds any errors as a result of the binding e.g. if you passed "XYZ" as the age field (an integer).
More information here http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html
This question is also nicely answered here:
Passing parameters from JSP to Controller in Spring MVC
read the answer carefully, hopefully you'll get your answer correctly.

Handling Multiple Query Parameters in Jersey

In the web service I'm working on, I need to implement a URI with query parameters which look like /stats?store=A&store=B&item=C&item=D
To break it down, I need to be able to use query parameters to specify data from multiple/all stores and data for multiple/all items from those stores. So far I have been able to implement one query argument just fine in order to pull item data, but I'm lost as far as to how to implement more queries, and can't seem to find the resources I had seen before which deal with this implementation.
What I have so far in my method is along the lines of
#GET
#Path("stats")
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "item") final String item)
{
/**Run data using item as variable**/
return someStringOfData
}
which works well for one item, and will return all data if I don't type the parameter in the URI. However, I am unsure how to handle any more parameters than this.
Update:
I have figured out how to use 2 different parameters by simply adding a second argument to the method like so:
public String methodImCalling(#DefaultValue("All") #QueryParam(value = "store") final String store,
#DefaultValue("All") #QueryParam(value = "item") final String item)
The question remains of how to implement multiple values of the same parameter.
If you change the type of your item method parameter from String to a collection such as List<String>, you should get a collection that holds all the values you are looking for.
#GET
#Path("/foo")
#Produces("text/plain")
public String methodImCalling(#DefaultValue("All")
#QueryParam(value = "item")
final List<String> item) {
return "values are " + item;
}
The JAX-RS specification (section 3.2) says the following regarding the #QueryParam annotation:
The following types are supported:
Primitive Types
Types that have a constructor that accepts a single String argument.
Types that have a static method named valueOf with a single String argument.
List<T>, Set<T>, or SortedSet<T> where T satisfies 2 or 3 above.
List<String> items=ui.getQueryParameters().get("item");
where ui is declared as a member in the rest resource like so :
#Context UriInfo ui;
the downside is that it doesn't appear in the methods arguments at all.
Some libs like axios js use the square brackets notation when sending a multi-value param request: /stats?store[]=A&store[]=B&item[]=C&item[]=D
To handle all cases (with or without square brackets) you can add another param like this:
public String methodImCalling(
#QueryParam(value = "store") final List<String> store,
#QueryParam(value = "store[]") final List<String> storeWithBrackets,
#QueryParam(value = "item") final List<String> item,
#QueryParam(value = "item[]") final List<String> itemWithBrackets) {
...
}
Inspecting each of the arguments checking for null.

Categories