I have a controller method as this:
#PostMapping("/view/{location}")
public ModelAndView view(#PathVariable("location") String location) {
ModelAndView modelAndView = new ModelAndView();
return modelAndView;
}
This method is capable of receiving requests like
"/view/a" or "/view/b" such that pathVariable location becomes a or b.
But I want this same method to receive all the requests having /view in their beginning, such that the pathVariable "location" holds the rest of the data.
for example
for a request as /view/a/b/c, the pathVariable location will become a/b/c.
like a file system hierarchy.
Please let me know if such a thing is possible in Spring MVC, and I am very new at this.
Check out this article
The idea is to map all the paths which start with /view to a single controller method by adding **, but you'll have to use HttpServletRequest instead of #PathVariable.
So, in your case, it'll be something like this:
#PostMapping("/view/**")
public ModelAndView view(HttpServletRequest request) {
String pathVariable = extractId(request);
ModelAndView modelAndView = new ModelAndView();
return modelAndView;
}
private String extractId(HttpServletRequest request) {
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
}
Also, check out this question
You could go by the approach shared earlier,
#GetMapping(value = "blog/**")
public Blog show(HttpServletRequest request){
String id = (String)
request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
System.out.println(id);
int blogId = Integer.parseInt(id);
return blogMockedData.getBlogById(blogId);
}
Second way is to use RequestParam instead of Path variable.
you will call the api using :
http://localhost:8080/blog?input=nabcd/2/nr/dje/jfir/dye
controller will look like :
#GetMapping(value = "blog")
public Blog show(#RequestParam("input") String input){
If you are certain about the number of slash in your input, you could go with any approach mentioned here help
Related
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) {
......
}
I'm looking for a way to use multiple path parameters as one string.
The following mapping is quite clear: it defines 3 static parameters as path variables:
#RequestMapping(value = "/rest/{language}/{country}/{term}?someparams=test&...", method = RequestMethod.GET)
But I want to have anything between /rest and {term} to be written into one single #PathVariable`.
Example: I could call localhost:8080/rest/this/is/my/dynamic/customterm?someparams)...
Here I'd like to get /this/is/my/dynamic as one single path variable.
The following does not work:
#RequestMapping(value = "/rest/{multiplePathParams}/{term}someparams=test&...", method = RequestMethod.GET)
public void test(#PathVariable String multiplePathParams, #PathVariableString term) {
Assert.assertEquals(multiplePathParams, "/this/is/my/dynamic");
Assert.assertEquals(term, "customterm");
}
Is it possible at all?
Spring url mapping is good. but this question your url is bad. your controller url is only get pattern. point is #RequestParam.
sample code
#RequestMapping(value = "/rest/{multiplePathParams}/{term}", method = RequestMethod.GET)
public void test(#PathVariable String multiplePathParams, #PathVariableString term, #RequestParam String someparams) {
Assert.assertEquals(multiplePathParams, "/this/is/my/dynamic");
Assert.assertEquals(term, "customterm");
}
It turned out not being possible to retrieve a substring inside the rest/get query.
But it's possible to extract the path matched against a wildcard:
#GetMapping(value = "/rest/**",...
public Rsp test(HttpServletReq req) {
private String getSqlTemplateKey(HttpServletRequest req) {
String pattern = (String) req.getAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE);
String urlpart = PATH_MATCHER.extractPathWithinPattern(pattern, req.getServletPath());
}
In order to access the redirect attributes in the redirected method, we utilize the model's map, like this :
#Controller
#RequestMapping("/foo")
public class FooController {
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(Model map) {
String some = (String) map.asMap().get("some");
}
#RequestMapping(value = "/bar", method = RequestMethod.POST)
public ModelAndView handlePost(RedirectAttributes redirectAttrs) {
redirectAttrs.addFlashAttributes("some", "thing");
return new ModelAndView().setViewName("redirect:/foo/bar");
}
}
But, why can't we access them in this way :
#RequestMapping(value = "/bar", method = RequestMethod.GET)
public ModelAndView handleGet(RedirectAttributes redAttr) {
String some = redAttr.getFlashAttributes().get("some");
}
If the only purpose of adding flashAttributes is that they become available to the model in the redirected method, what's the purpose of getFlashAttributes() ?
RedirectAttributes are for setting flash attributes before redirection. They are merged into model after the redirection so there is no reason to access them again via RedirectAttributes again as you have suggested.
Being able to work with the attributes just like with a map might be useful. You can check what have you set (containsKey, isEmpty, ...). However the use of the wildcard generic parameter Map<String, ?> getFlashAttributes() prevents writing into map and it is strange why they have used it instead of a plain Object parameter.
I am trying to retrieve some JSON data in my javascript by making a call to the controller. The controller returns a MappingJacksonJsonView ModelandView, but the .getJSON is always reporting a 404 at .../handhygiene.json.
Is there a problem with the way I am returning the ModelandView from the controller?
Controller
#RequestMapping(value = "/{room}/handhygiene.json", method = RequestMethod.GET)
public ModelAndView getHandHygienePageAsync(
#PathVariable(value = "room") String roomCode) {
ModelAndView mav = new ModelAndView(new MappingJacksonJsonView());
mav.getModelMap().addAttribute(blahblahblah); //adds some attributes
...
return mav;
}
Javascript
var currentURL = window.location;
$.getJSON(currentURL + ".json",
function(data) {
... //does stuff with data
}
If you're trying to get only an JSON object from Ajax request, you need to add #ResponseBody to your method, and make you result object as return from your method.
The #ResponseBody tells to Spring that he need to serialize your object to return to the client as content-type. By default, Spring uses JSON Serialization. ModelAndView will try to return an .JSP page. Maybe you don't have this jsp page on your resources so, the server return 404 error.
I Think this code should work for you:
#RequestMapping(value = "/{room}/handhygiene.json", method = RequestMethod.GET)
public #ResponseBody Room getHandHygienePageAsync(#PathVariable(value = "room") String roomCode) {
Room room = myService.findRoomByRoomCode(roomCode);
return room;
}
I'm assuming you're using the Room as your result object, but it may be another object or ArrayList, for example.
You cant take a look here for Spring example, and here for example and configuration.
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="")