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

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.

Related

Spring MVC controller parameters binding: replace parameters with POJO

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.

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.

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.

how to reduce the code of constructor overloading

In my one class I have many constructors like this..
public MyData(int position,String songName,String duration, boolean e) {
//initialization of above variable like int, string,string and boolean
}
public MyData(String songName, String artistName, String duration,String downloadPath, String songSize, String albumName,String url,String trackId, boolean e)
{
//initialization of above variable like String,String,String,String,String,String,String,String and boolean
}
and some more like above.
Now the calling time, I'm calling that constructor only that I require data. but I don't think my flow is good so I need some help to reduce my code as well as creation of good flow.
If anybody have a good flow to achieve this, then please share.
Thanks in advance.
Assuming you're effectively applying defaults, usually the best approach is to have one "full" constructor and make the others call it. For example:
public Foo(String name)
{
// Default the description to null
this(name, null);
}
public Foo(String name, String description)
{
this.name = name;
this.description = description;
}
You still end up with quite a lot of cruft in terms of overloaded constructors, but at least each of those "extra" constructors contains no actual code - just a call to another constructor. If possible, chain the constructors together so that the default for any particular value is only specified in one place - or use a constant. That way you get consistency.
Another option is to use a "parameter object" following the builder pattern - create another class whose sole purpose is to hold the data for the constructor parameters. This should be mutable, with setters for all of the different values. Often it's useful to make the setters return the builder, so you can use:
FooParameters parameters = new FooParameters()
.setName("some name")
.setDescription("some description");
// Either a constructor call at the end, or give FooParameters
// a build() or create() method
Foo foo = new Foo(parameters);
This is particularly useful if the main type you're constructing is an immutable type - it means you can apply conditional logic in the calling code to set some parameters but not others. The Java framework itself uses this approach in ProcessBuilder, although personally I'm not keen on the way it overloads method names to either return a value or set a value based on whether you provide an argument :(
Note the comment above the constructor call in the final snippet - if your helper class is only ever helpful for creating objects of a single type, you can give it an extra method (build, create, start, whatever is most appropriate) to take the place of the constructor call. This allows you to build the whole final object in a fluent way.
One option in the Java implementation of the builder pattern is to use a nested type, e.g.
Foo foo = new Foo.Builder().setName(...).setDescription(...).build();
That avoids polluting your package with another class which is only useful for building instances of Foo.
You may want to have another object that is responsible for creating the object through the builder pattern. For example, you could define an object like this:
public class SongBuilder {
private String artistName;
private String songTitle;
/* ... everything else ... */
public SongBuilder setArtistName(String name) {
this.artistName = name;
return this;
}
public SongBuilder setSongTitle(String title) {
this.songTitle = title;
return this;
}
/* ... everything else ... */
public Song create() {
return new Song(artistName, songTitle, /* ... everything else ... */);
}
}
You could then define a single constructor for Song that takes in all the data. To make a Song, you could then write
Song s = new SongBuilder().setSongTitle("Still Alive").setArtistName("GLaDOS").create();
The advantage of this approach is that you can set a reasonable default for all the parameters, then just call the appropriate set functions for parameters that you actually use. It also allows you to add new parameters easily without having to go back and rewrite important code.
Alternatively, as Jon Skeet points out, you can have multiple constructors that all call one another. The advantage of the builder pattern over this approach is that if you have n different parameters, there are 2n combinations of constructors you'd need to write, whereas you only need one builder.
Hope this helps!

Categories