Why do we need the annotation "RequestParam()" - java

I'm learning Spring Boot right now, I don't really understand why we need this annotation. In my opinion, this annotation is used to rename.
public String home(#RequestParam("name") String othername, HttpSession session){
session.setAttribute("name", othername);
return "home.jsp"
}
So if the above code is running, I can pass my name like this: http://127.0.0.1:8080/home?name=bob
But if I don't have the annotation, I can only do http://127.0.0.1:8080/home?othername=bob, why we don't just change the name of the parameter? I think I might misunderstand the concept of RequestParam.

If you only have a handful of request parameters with default behaviour and you can easily name them as you wish, then you are right, the RequestParam annotation is not strictly necessary. However, as soon as you add PathVariables in the mix, or a request parameter e.g. is not required anymore, but optional, you have to add the annotation there again. Handling the parameter names is just one small aspect of that annotation. Additionally, you might have to implement a legacy API, where the parameter names on the ReST API level cannot be changed, but you want to have a better name in your code for readability, clean code or other reasons.

You did not misunderstand the concept but your knowledge about it is limited.
It is correct that #RequestParam can be used to rename the request parameter. Why we don't just change the name of the parameter? What if you want to have a parameter name as "package", you cannot just change the variable name to package as it is java reserved keyword, but you can have it as #RequestParam(name = "package") String othername.
Renaming is just one use case of #RequestParam some of the other uses are:
Differentiates between #RequestParam and #PathVariable variables
Make a parameter optional with required attribute #RequestParam(required = false).
Add a default value for the request parameter #RequestParam(defaultValue = "test").
Better code readability.

Related

custom annotation on RequestParam and PathVariable

I have created a custom annotation
annotation class UserControl(
val userIdentifier: String
)
I wan to apply this annotation on query parameters, and path variables in different controllers.
fun userWithMobile(
#UserControl("PhoneNumber")
#RequestParam mobile: String
): RegisteredUser {
return userManager.getUserWithPhoneNumber(mobile))
}
How can i check if the query parameters have the UserControl annotation or not, and do some processing on that. Is there standard way to write a global handler , or a processor for that?
Would appreciate any help
AspectJ can directly match parameter annotations, but not bind them to advice method values like class or method annotations. So if you only want to match them, a simple pointcut is enough. If you want to access the annotations and maybe their parameter values, you need a little bit of reflection magic. I have answered related questions many times already, which is why I am going to close this one as a duplicate. But first, here are the resources you want to read. They all related to your question, showing examples of how to handle different specific situations:
https://stackoverflow.com/a/38162279/1082681
https://stackoverflow.com/a/10595712/1082681
https://stackoverflow.com/a/50540489/1082681
https://stackoverflow.com/a/49872132/1082681
https://stackoverflow.com/a/61284425/1082681
https://stackoverflow.com/a/16624766/1082681
Basically, the pointcut you want the following or some variation of it:
execution(* *(.., #my.package.UserControl (*), ..))
The naive, less efficient approach without matching the parameter in the pointcut, using only relfection:
https://stackoverflow.com/a/27784714/1082681
https://stackoverflow.com/a/42561014/1082681

Optional parameter in #PathParam annotation

We are facing issue related with making a path parameter optional.
original URL /expire/{token}
What we are trying to do is have the same service work for the URL's below.
1. /expire/{token}
2. /expire
Ex:- #Path("/expire/{token}")
We have already applied other solutions from SO,but no luck so far.
What about adding another method annotated with only:
#Path("/expire")
And let this method pass a null value into the original method.
Logically, it doesn't seem to make sense to have it optional. Your URI should handle the type of request it's supposed to do. But, I came across a post to make the #PathParam to be optional with a small hack using regular expressions.
http://www.nakov.com/blog/2009/07/15/jax-rs-path-pathparam-and-optional-parameters/
I would go with having separate endpoint method in Controller where it can pass the call to your services with optional parameter.
We can use regular expressions,
"#Path("/add/{a:([0-9.]*)}/{b:([0-9.]*)}{p:/?}{c:([0-9.]*)}")"
here path can be
add/2/3
add/2/3/4
my case I am using regular expressions for allowing only numbers

Spring #RequestMapping value explicit/implicit

In some cases I have seen two different ways to specify uri in #RequestMapping:
#RequestMapping(value="/somepath")
#RequestMapping("/somepath")
The latter is often used at class level. So, is it always legal omitting the value attribute?
The Java Language Spec states
It is legal to use single-element annotations for annotation types with multiple elements, so long as one element is named value, and all other elements have default values.
Since #RequestMapping has a defined value attribute and all other attributes have default values (empty arrays), it is always legal to omit spelling the name value if you don't specify any other attributes for the annotation:
#RequestMapping("/somepath") // valid
#RequestMapping("/somepath", method = RequestMethod.GET) // INVALID!
For #RequestMapping annotation value is the primary attribute, it is mandatory.
1)#RequestMapping(value="/somepath"):
Here you can mention remaining attributes, for ex: method,produces
2) #RequestMapping("/somepath"):
Here you can not mention rest of the attributes, as by default "/somepath" will be taken as value attribute.
Check this

Get value from #RequestParam without specified name

I know that if a parameter looks like this:
#RequestParam("userId") String userId
I can get the value by calling this:
requestParam.value()
But if I don't specify a name, Spring automatically uses the variable name. Like this:
#RequestParam String userId
If the param name isn't specified, how can I access it? I know its possible because Spring does it somehow, but requestParam.value() is blank in this case.
Spring doesn't populate the request based on the #RequestParam. Rather it populates the method argument annotated with #RequestParam (in a method annotated with #RequestMapping). The parameter given to the #RequestParam annotation tells Spring the name of the request parameter you want it to use as that argument. If you don't provide one, Spring defaults to using the name of the argument (so the 2 examples you give in your question are equivalent).
If you are trying to access a request parameter, you need to know the name of it, regardless of the framework you are using.
Recommend you not to do it that way.Spring use asm
to analysis the java bytecode file,and extract parameter name of a method.But some time this does not work,because some .class file does not comprise parameter name,just parameter types,it depends on compiler options.
In case of Java 8, Spring uses StandardReflectionParameterNameDiscoverer, which relies on Java 8 Parameters API.
For details how to set up parameters option and use it via reflection see this answer.
Just call userId, as in doSomething(userId). Spring binds everything up for you.

Spring 3 MVC - how to turn a form into a query string?

I have a simple Spring form that gets bound to a form object on post. The http POST handler does some work, and then needs to redirect to a new URL, passing the form data as querystring parameters.
So, assuming I have a form backing object with properties "param1" and "param2", I want to build a string that looks something like this:
redirect:/app/new/page?param1=value;param2=value
Now, Spring will automatically bind values FROM a querystring or a form post into my form object, but I want to GENERATE a querystring with values taken from the form object.
Obviously it's trivial to do this manually but since I'm going to have lots of different form backing objects, is there some built-in facility in Spring to generate a query string from a form object, suitable for building into a URL?
Thanks.
OK, after some tracing into the Spring source code, I think I have some definitive answers.
1) Manually adding query parameters to a URL that you pass back in a "redirect:..." string is A VERY BAD IDEA. Spring caches the views those urls refer to, so if you specify lots of different parameters, you effectively leak memory by making the cache hold lots of values it doesn't need to.
Note that the PetClinic example code in the Spring distribution does exactly this, and apparently there has been a Jira issue raised to correct it :-)
2) The model object is still held by Spring when the controller returns, and Spring WILL automatically add any values in it onto the URL you specify (as query parameters) - PROVIDED that the model values are plain String or primitive objects. Now, since the form backing object is an Object stored as a single attribute, Spring doesn't do anything with it.
So - from an annotated Controller, the best approach seems to be to add a ModelMap parameter to the handler method. When you're ready to return, do something like this:
Assuming the form backing object "formObject" has been passed as a parameter to the controller handler method (via the ModelAttribute annotation) and has properties "param1" and "param2" that are Strings -
modelMap.clear();
modelMap.addAttribute("param1", formObject.getParam1());
modelMap.addAttribute("param2", formObject.getParam2());
return "redirect:/my/url";
And Spring will issue a redirect to /my/url?param1=value;param2=value
There doesn't appear to be a built-in mechanism (in Spring) to turn a bean into a list of key/value pairs for adding into the map, but if you really need that, the Apache BeanUtils library will do nicely. Of course, you don't want to do this for big objects otherwise you might exceed the allowable length of a URL.
Finding all that out seemed to be much harder than it needed to be :-)
Map<String, Object> model = new HashMap<String, Object();
model.put("stuff", "here");
ModelAndView mv = new ModelAndView("redirect:/somewhere.htm", model);
return mv;
Produces
http://.../somewhere.htm?stuff=here
I think, you should use method setExposeModelAttributes of RedirectView class, but it maybe difficult to get access to this method if you use Spring 3.0 annotations on your controllers.

Categories