Request Mapping with Multiple Wildcards - java

I want to have two endpoints that have wild cards in the #RequestMapping
#RequestMapping(value="/**", method = { RequestMethod.GET}, produces = "application/json")
#RequestMapping(value="/**/versions/{versionId}", method = { RequestMethod.GET}, produces = "application/json")
When I perform a request that should go to /**/versions/{versionId} it is preferring the /** endpoint over the /**/versions/{versionId} endpoint even though the request should match.
I am using:
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Brixton.SR2</version>
</parent>

I think you just have to change the order of your #RequestMapping methods.
For me works http://localhost:8080/versions/1 returns version 1.
And for any other request without version/{versionId} it returns index.
#Controller
public class DemoController {
#RequestMapping(value="/**/versions/{versionId}", method = RequestMethod.GET)
#ResponseBody
public String version(#PathVariable String versionId){
return "version " + versionId;
}
#RequestMapping(value="/**", method = RequestMethod.GET)
#ResponseBody
public String index(){
return "index";
}
}

If you want quite complicated request mapping try to overwrite handleRequest as it is here: How to define RequestMapping prioritization
Than you can:
if (urlPath.contains("/versions")) {
/* forward to method with #RequestMapping(value="/get/versions/{versionId}")
}
Instead of:
#RequestMapping(value="/**/versions/{versionId}", method = { RequestMethod.GET}, produces = "application/json")
use:
#RequestMapping(value="/response_for_versions/{versionId}", method = { RequestMethod.GET}, produces = "application/json")
Now all ".../versions/{versionId}" should be forwarded to "/response_for_versions/{versionId}" and all others would be handled by "/**"

Related

How to return json/xml from Spring Boot #Controller?

I'm trying to return JSON/XML from a function of my Controller. First I was using #RestController and it worked good, but now I need to change to #Controller, because I will use also some other functions and pass there some objects for my view.
#Controller
#RequestMapping("/game")
public class ViewController {
#RequestMapping(value = "/statistic", method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public GamerData[] getStatistic() {
RestTemplate restTemplate = new RestTemplate();
try {
String uri_get_statistic = "http://localhost:8081/statistic/";
ResponseEntity<GamerData[]> response = restTemplate.getForEntity(uri_get_statistic, GamerData[].class);
GamerData[] statisticData = response.getBody();
return statisticData;
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
}
After I've changed to Controller I get error 404 not found. With RestController I've got json. (GamerData is just a class with 2 simple fields (int and String), setters, getters, consructor).
UPDATE:
I've added #ResponseBody to my function, but now I have Internal Server error
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class [Lgame.mainservice.mvc.GamerData;] with preset Content-Type 'null']
try to add #ResponseBody like
public #ResponseBody GamerData[] getStatistic....
#RequestMapping(value = "/statistic", method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#ResponseBody // add this one
public GamerData[] getStatistic() {}
Thank you all for answers, I've added #ResponceBody annotation to my function and removed "produces" part from #RequestMapping annotation and my function works great:
#RequestMapping(value = "/statistic", method = RequestMethod.GET)
#ResponseBody
public GamerData[] getStatistic() {
//function code
}

AWS lambda/api gateway does not redirect

I have a very simple spring route that im attempting to run on aws lambda. The route simply returns the text/string "redirect:/upload" instead of redirecting. I have the html file in the /resources/templates folder.
#RequestMapping(path = "/test", method = RequestMethod.POST)
public String UploadPage2() {
return "redirect:/upload";
}
I think the problem is from the return type of method: String.
You can do:
public RedirectView UploadPage2() {
return new RedirectView("/upload");
}
Second question
To return an view on path /test with GET request, you need another method with same path but different method
#RequestMapping(path = "/test", method = RequestMethod.GET)
public ModelAndView testGet(){
return new ModelAndView("uploadview");
}

Zero length part of URL in Spring controller RequestMapping PathVariable breaks resolution

I'm trying to make an app's REST API more RESTful and it feels like I'm not using the Spring RequestMappings in the way intended.
I have a single GET end point for doing reads:
#RequestMapping(value = "thing/{thingName}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThing(
#PathVariable(value = "thingName", required = false)
String thingName,
#RequestParam(value = "findByComponent", required = false)
String findByComponentQuery,
#AuthenticationPrincipal User user) {
...
To be more restful, I want this endpoint to do both:
GET /thing/{thingName} returns a single thing having that name
GET /thing or /thing/ with query params returns lists of things
So in my controller, I can test whether {thingName} is null or zero-length and if so, check the query params for known query names.
However calling this with http://localhost:8080/thing/?findByComponent=component123 returns a 404 from Spring with this logging:
12:45:18.485 PageNotFound : No mapping found for HTTP request with URI [/thing/] in DispatcherServlet with name 'dispatcher' : WARN : XNIO-1 task-3 : org.springframework.web.servlet.DispatcherServlet
Spring does not allow path variables ({thingName}) to be mapped to an empty String. In practice, this means that the URL /thing/?findByComponent=component123 does not map to thing/{thingName} with an empty {thingName}, but rather, it expects there to be some mapping for thing. Since there is no endpoint that maps to the path thing (without the path variable), a 404 error is returned.
To solve this issue, you can break this single endpoint into two separate endpoints:
#RequestMapping(value = "thing/{thingName}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThing(
#PathVariable(value = "thingName") String thingName,
#AuthenticationPrincipal User user) {
// ...
}
#RequestMapping(value = "thing",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThings(,
#RequestParam(value = "findByComponent", required = false) String findByComponentQuery,
#AuthenticationPrincipal User user) {
// ...
}
For more information, see With Spring 3.0, can I make an optional path variable?.
The required=false flag allows for two types of requests:
/thing
/thing/<some_value>
Strictly speaking, including a trailing slash at the end of the URL (i.e. /thing/) means that a decision was made to include a value for the path variable, but none was provided. In the context of REST APIs, /thing and /thing/ are two different endpoints, where the latter means that a value after the trailing slash was expected.
A workaround for not having to create three separate request mappings (one for each case above) is to set the #RequestMapping value for the controller to the base path and then have a "" and "/{thingName} request mapping for the two endpoints:
#RestController
#RequestMapping("thing")
public class ThingController {
#RequestMapping(value = "/{thingName}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThing(
#PathVariable(value = "thingName") String thingName) {
return "foo";
}
#RequestMapping(value = "",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public String getThings(
#RequestParam(value = "findByComponent", required = false) String findByComponentQuery) {
return "bar";
}
}
In this case, the following mappings will occur:
/thing: getThings
/thing/: getThings
/thing/foo: getThing
An example of this workaround, including test cases can be found here.

Java - Pass multiple parameters to ajax post

I have this simple controller from ajax request. It works but I wanted to return many stuff, not only List TestFlow.getFlow(flowName);
#RequestMapping(value = "/execute-flow/getFlow" , method = RequestMethod.POST)
public #ResponseBody List<String> getFlow(#RequestParam("flowName") String flowName) {
return TestFlow.getFlow(flowName);
}
Can I return multiple things to the ajax post?
For example:
#RequestMapping(value = "/execute-flow/getFlow" , method = RequestMethod.POST)
public #ResponseBody List<String> getFlow(#RequestParam("flowName") String flowName) {
return TestFlow.getFlow(flowName);
return TestFlow.getInputs(flowName);
return TestFlow.getCode(flowName);
}
Not sure what exactly you're after, but
return Arrays.asList(
TestFlow.getFlow(flowName),
TestFlow.getInputs(flowName),
TestFlow.getCode(flowName));
should work, provided that all of these are of the same type (String).

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