what is the dif between requestMapping on controller and method - java

If I have:
#RequestMapping("/user")
public class RegistrationController {
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String getRegisterPage(Model model) {
What is the difference? I mean what will happen if I remove the /user mapping, will my /register mapping still work?

A #RequestMapping on the class level is not required. Without it, all paths are simply absolute, and not relative.
see 15.3.2 Mapping requests with #RequestMapping
This means if you specify the classlevel annotations, the url shall be relative, so for register it shall be /user/register(URL to Handler mapping) and likewise.

As described here you can also use Type level mapping and relative path mappings on method level to be dry and don't duplicate root at every methods.
#Controller
#RequestMapping("/employee/*")
public class Employee {
#RequestMapping("add")
public ModelAndView add(
#RequestParam(value = "firstName") String firstName,
#RequestParam(value = "surName") String surName) {
//....
}
#RequestMapping(value={"remove","delete"})
public ModelAndView delete(
//....
}
}
Spring doc: At the method level, relative paths (e.g. "edit.do") are supported within the primary mapping expressed at the type level.

Related

Is it possible to map a nested mapping in spring-mvc to a global path?

I have something like this:
#RestController
#RequestMapping("/{id}")
public class MyController {
#GetMapping
public String get(#PathVariable String id) {
...
}
#PostMapping
public String post(#PathVariable String id, Payload payload) {
...
}
#GetMapping("/deeper/{id}")
public String getDeeper(#PathVariable String id) {
....
}
}
This gives 3 mappings:
/{id} (GET)
/{id} (POST)
/{id}/deeper/{id} (GET)
I would like the third of them to be just /deeper/{id} (GET).
Is it possible to do this leaving the method in the same controller and leaving that controller-wise #RequestMapping annotation?
What you request is not possible because you cannot avoid a requestMapping on a class level which makes no sense because being on class level means that you want that path to affect to all your methods.
Keep in mind that a RestController is RESTful and a class level requestMapping is used to avoid adding the same resource path to every method, so it does not make sense to have a method that can't fit in within that resource (you should move it to another controller instead).
This being said, there are a few things you can try:
1 This is not recommended. Use more than one path value on your class #ResquestMapping, in your case:
#RestController
#RequestMapping("/{id}", "/")
public class MyController{...}
You can kinda achieve what you want with this but this is extremely
discouraged and a code smell because basically means that all your methods will accept url paths either starting with id or with /, think carefully if you want to use this approach.
2 The recommended one, Remove the #RequestMapping in the class level and just update the path on every method, in your case:
#RestController
public class MyController {
#GetMapping(value = /{id})
public String get(#PathVariable String id) {
...
}
#PostMapping(value = "/{id}")
public String post(#PathVariable String id, Payload payload) {
...
}
#GetMapping("/deeper/{id}")
public String getDeeper(#PathVariable String id) {
....
}
}
3 also a recommended one Just move the method that does not fit in your controller "general logic" to another controller class which make's sense since that method is not affected by the controller general logic.

Difference between path and value attributes in #RequestMapping annotation

What is the difference between below two attributes and which one to use when?
#GetMapping(path = "/usr/{userId}")
public String findDBUserGetMapping(#PathVariable("userId") String userId) {
return "Test User";
}
#RequestMapping(value = "/usr/{userId}", method = RequestMethod.GET)
public String findDBUserReqMapping(#PathVariable("userId") String userId) {
return "Test User";
}
As mentioned in the comments (and the documentation), value is an alias to path. Spring often declares the value element as an alias to a commonly used element. In the case of #RequestMapping (and #GetMapping, ...) this is the path property:
This is an alias for path(). For example #RequestMapping("/foo") is equivalent to #RequestMapping(path="/foo").
The reasoning behind this is that the value element is the default when it comes to annotations, so it allows you to write code in a more concise way.
Other examples of this are:
#RequestParam (value → name)
#PathVariable (value → name)
...
However, aliases aren't limited to annotation elements only, because as you demonstrated in your example, #GetMapping is an alias for #RequestMapping(method = RequestMethod.GET).
Just looking for references of AliasFor in their code allows you to see that they do this quite often.
#GetMapping is a shorthand for #RequestMapping(method = RequestMethod.GET).
In your case.
#GetMapping(path = "/usr/{userId}") is a shorthand for #RequestMapping(value = "/usr/{userId}", method = RequestMethod.GET).
Both are equivalent. Prefer using shorthand #GetMapping over the more verbose alternative. One thing that you can do with #RequestMapping which you can't with #GetMapping is to provide multiple request methods.
#RequestMapping(value = "/path", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT)
public void handleRequet() {
}
Use #RequestMapping when you need to provide multiple Http verbs.
Another usage of #RequestMapping is when you need to provide a top level path for a controller. For e.g.
#RestController
#RequestMapping("/users")
public class UserController {
#PostMapping
public void createUser(Request request) {
// POST /users
// create a user
}
#GetMapping
public Users getUsers(Request request) {
// GET /users
// get users
}
#GetMapping("/{id}")
public Users getUserById(#PathVariable long id) {
// GET /users/1
// get user by id
}
}
#GetMapping is an alias for #RequestMapping
#GetMapping is a composed annotation that acts as a shortcut for #RequestMapping(method = RequestMethod.GET).
value method is an alias for path method.
This is an alias for path(). For example #RequestMapping("/foo") is equivalent to #RequestMapping(path="/foo").
So both methods are similar in that sense.

Spring path params with multiple slash

I have a Spring boot app where I have an API that takes other urls as path params. For example:
host:port/{eid} is my base path and after this I can have URLs like
host:port/{eid}/abc
host:port/{eid}/abc/pqr/
host:port/{eid}/abc/pqr/b=2
host:port/{eid}/abc/pqr/xyz
host:port/{eid}/abc/pqr/xyz?a=1
...and so on...
I would like to define a controller that I can map to all the above URLs and that should work something like
#RequestMapping(value = "/{eid}/{urlParts}", method = RequestMethod.GET)
public ResponseEntity<Object> share(
#PathVariable String eid,
#PathVariable String urlParts) {
......
}
I tried using #PathVariable Map<String, String> path and also #RequestMapping(value = "/{eid}/{urlParts:.+}"
but couldn't get the expected result.
Is there any solution to receive path slash(/) in path param.
Note: I can not URL encode the slash(/) in the URL. That's not an option for me.
I know the query is too old but still it's useful and this answer can help others.
You can get the full url parts using request attribute as below.
#RequestMapping(value = "/{eid}/**", method = RequestMethod.GET)
public ResponseEntity<Object> share(#PathVariable String eid, HttpServletRequest request) {
Object uriObject = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (null != uriObject) {
String urlParts = uriObject.toString().replaceFirst("^/" eid + "/", "");
}
....
}
why don't you try #RequestParam to take url if you working with jsp or other stuff..
#PathVariable means that the annotated method argument should be extracted from the path of the invoked URL. #RequestParam means that the annotated method argument must be extracted from the request parameters. None of these annotations cause the annotated arguments to be put in the request, session or application scope.
so you use your map also...
${username} means "write the value of the username attribute (found in page, or request, or session, or application scope) in the response". Since you didn't include any username attribute in any of those scopes, it doesn't write anything.
The code would work if the method returned a ModelAndView object, and the model contained a username attribute and a studentid attribute.
you can refer below code and link :
First URL : localhost:8080/projectName/test?firstname=john
Second URL :localhost:8080/projectName/test?firstname=john&secondname=roy
#Controller
#RequestMapping("/test")
public class TestController {
#RequestMapping(value = { "/test/{firstname}/test" }, method = { RequestMethod.GET })
public String someMethod(#PathVariable("firstname") String firstname){
return someMethod(firstValue )
}
#RequestMapping(value = { "/test/{firstname}/{otherString}/test" }, method = { RequestMethod.GET })
public String someOtherMethod(#PathVariable("firstname") String firstname, #PathVariable("secondname") String secondValue) {
return someMethod(firstValue + "/" + secondValue)
}
}
so I am not sure if there is a direct spring implementation to doing this however, you could us a mixture of things.
#RequestParam - returns a map of the URL params (succeeding the ?)
#PathVariable - return the eid
HttpServletRequest - use the request to return the URI and strip host:port/{eid} and anything after ? , then use Arrays.asList(str.split("/")); (remember this is a wrapper of an array use new ArrayList<Sting>(Arrays.asList(str.split("/"))) )
#RequestMapping(value = "/{eid}", method = RequestMethod.GET)
public ResponseEntity<Object> share(
#PathVariable String eid,
#RequestParam Map<String,String> allRequestParams,
HttpServletRequest request) {
......
}

Spring MVC session Attribute set intially

I have used the Spring MVC. I set the Session Attribute value like
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String initHome(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
return "homeMenu";
}
It is working fine if i click the home menu url(/home). but if i did not go the
home means it says error as 'session attribute clientObject is required'
so i decided to set sessionattibutes in constructor of controller
#Autowired
public MyController(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
}
it also says error
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myController'
I tried to set using the RequestMapping also like
#RequestMapping(value = "/", method = RequestMethod.GET)
public void initController(Model model) {
if (!model.containsAttribute("clientObject")) {
model.addAttribute("clientObject", createDefaultClient());
}
}
this method is also not called intially
my cointroller look like
#RequestMapping("/sample")
public class MyController {
..
..
is it possible to set the sessionAttribute value in the constructor of controller? or any other way to set the session Attribute initially?
Thanks in advance for your help.
Assuming your createDefaultClient is in the controller add a #ModelAttribute annotation to it.
#ModelAttribute("clientObject")
public ClientObject createDefaultClient() { ... }
This method will be called before any request handling method (as explained in the reference guide)
If you combine that with a #SessionAttribute annotation on your class (which you might already have). You should be able to achieve what you want.
In your request handling methods (methods annotated with #RequestMapping) you can now simply inject the client object as a method argument.
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String initHome(#ModelAttribute("clientObject") ClientObject clientObject) {
// Do something with the clientObject
return "homeMenu";
}
This will only work consistenly within the same controller, so if you need the ClientObject to be used somewhere else (another controller for instance), this isn't going to work (nor is #SessionAttributes designed for that).

Can #PathVariable return null if it's not found?

Is it possible to make the #PathVariable to return null if the path variable is not in the url? Otherwise I need to make two handlers. One for /simple and another for /simple/{game}, but both do the same just if there is no game defined i pick first one from a list however if there is a game param defined then i use it.
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable("example") String example,
HttpServletRequest request) {
And this is what I get when trying to open page /simple:
Caused by: java.lang.IllegalStateException: Could not find #PathVariable [example] in #RequestMapping
They cannot be optional, no. If you need that, you need two methods to handle them.
This reflects the nature of path variables - it doesn't really make sense for them to be null. REST-style URLs always need the full URL path. If you have an optional component, consider making it a request parameter instead (i.e. using #RequestParam). This is much better suited to optional arguments.
As others have already mentioned No you cannot expect them to be null when you have explicitly mentioned the path parameters. However you can do something like below as a workaround -
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable Map<String, String> pathVariablesMap,
HttpServletRequest request) {
if (pathVariablesMap.containsKey("game")) {
//corresponds to path "/simple/{game}"
} else {
//corresponds to path "/simple"
}
}
If you are using Spring 4.1 and Java 8 you can use java.util.Optional which is supported in #RequestParam, #PathVariable, #RequestHeader and #MatrixVariable in Spring MVC
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable Optional<String> game,
HttpServletRequest request) {
if (game.isPresent()) {
//game.get()
//corresponds to path "/simple/{game}"
} else {
//corresponds to path "/simple"
}
}
You could always just do this:
#RequestMapping(value = "/simple", method = RequestMethod.GET)
public ModelAndView gameHandler(HttpServletRequest request) {
gameHandler2(null, request)
}
#RequestMapping(value = "/simple/{game}", method = RequestMethod.GET)
public ModelAndView gameHandler2(#PathVariable("game") String game,
HttpServletRequest request) {
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable(value="example",required = false) final String example)
Try this approach, it worked for me.
I just tested this just now, but by combining the above solution i got this:
#RequestMapping(value = {"/simple", "/simple/{game}"}, method = RequestMethod.GET)
public ModelAndView gameHandler(#PathVariable(value = "game", required = false) String example,
HttpServletRequest request) {
if (example != null) {
//...
} else {
//pick first, ...
}
}
Now when you use "/simple", String example will be null instead of throwing Exception.
Short solution, no fancy Optional<> or Map<>
We can write multiple methods in controllers with explicit mapping with the path variable combination to exclude the optional variables (if using old version of Spring)
In my scenario wanted to develop an API to get recycle value for old device where parameters could be brand, model and network however network is an option one.
One option to handle this was use network as a request parameter instead of pathVariable.
for e.g. /value/LG/g3?network=vodafone however I didn't like this approach.
for me the more cleaner one was to use below
/refurbValue/LG/g3
/refurbValue/LG/g3/vodafone
#RequestMapping(value = "/refurbValue/{make}/{model}/{network}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
def getRefurbValueByMakeAndModelAndNetwork(#PathVariable String make, #PathVariable String model, #PathVariable String network ) throws Exception {
//logic here
}
#RequestMapping(value = "/refurbValue/{make}/{model}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
def getRefurbValueByMakeAndModel(#PathVariable String make, #PathVariable String model) throws Exception {
//logic here
}
In the above example, both controller can use the same service method and handling of the parameter can be done. In my case I was using Groovy so it was easy to use with optional parameter like
Map getRefurbValue(String brand, String model, String network="")

Categories