I've created a POJO like this.
public class Param(){
#NotNull
private Sring paraA;
#NotNull
private Sring paraB;
//Setter and getter
}
And I want to use JSR303 to check it.
#RequestMapping(value = "/test/{paraA}/{paraB}")
#ResponseBody
public BaseJsonRsp test(#PathVariable #Valid Param param) {
//doSomething
}
But this code it doesn't work and I've got following error
HTTP Status 500 - Missing URI template variable 'value' for method parameter of type PatchValue!
Why? How to fix it? Thanks.
Have a look at the Spring Refererence Docs on #PathVariable:
A #PathVariable argument can be of any simple type such as int, long, Date, etc.
If you want to use your Param type as the controller method argument, you won't be able to do so using #PathVariable.
Alternatively, you can map your parameters to individual String variables (using #PathVariable), and then manually construct your Param after that. Note that #PathVariable assumes that the placeholder in your URL is the same as the variable name by default. If your variable name doesn't match, you'd do something like
#RequestMapping(value = "/test/{paraA}/{paraB}")
#ResponseBody
public BaseJsonRsp test(#PathVariable("paraA") String myParam,
#PathVariable("paraB") String otherParam) {
//doSomething
}
You need to explicity set the variable values from the URL into the Param object.
#RequestMapping(value = "/test/{paraA}/{paraB}")
#ResponseBody
public BaseJsonRsp test(#PathVariable("paraA") String paramA, #PathVariable("paraB") String paramB) {
Param param = new Param(paramA, paramB);
}
#PathVariable assumes that the placeholder in your URL is the same as the variable name by default. You need to do this
#RequestMapping(value = "/test/{paraA}/{paraB}")
#ResponseBody
public BaseJsonRsp test(#PathVariable("paraA") String paramA,
#PathVariable("paraB") String otherparam) {
Param param = new Param(paramA, paramB);
}
Related
I try to bind an object in Spring controller so it can be used as #PathVariable. I want to do so, since there are some #PathVariable that I want to pass. I have tried the solution from Bind Path variables to a custom model object in spring and also Is it possible to bind path variable and request param into a single object?. But both are not working.
I have created something like this in my controller.
#RestController
#RequestMapping("/buildings")
#RequiredArgsConstructor
public class BuildingController {
private final BuildingService buildingService;
#GetMapping("/{buildingId}/floors/{floorId}/rooms/{roomId}/sections")
public Flux<SectionDTO> getRoomSections(BuildingRequestBean request) {
return this.buildingService.getRoomSections(request);
}
}
and BuildingRequestBean.java like this
#Getter
#Setter
public class BuildingRequestBean {
private String buildingId;
private String floorId;
private String roomId;
}
When I check BuildingRequestBean, the attributes is null when I call it with GET localhost:8080/buildings/a/floors/b/rooms/c/sections.
However, it will not null if I call it as #RequestParam, something like this GET localhost:8080/buildings/{buildingId}/floors/{floorId}/rooms/{roomId}/sections?buildingId=a&floorId=b&roomId=c
How to fix it so it will behave like #PathVariable rather than behave like #RequestParam?
U can get it by using #ModelAttribute
Try with this:
#GetMapping("/{buildingId}/floors/{floorId}/rooms/{roomId}/sections")
public Flux<SectionDTO> getRoomSections(#ModelAttribute BuildingRequestBean request) {
return this.buildingService.getRoomSections(request);
}
PathVariable must be added to the function parameter
Try this :
#GetMapping("/{buildingId}/floors/{floorId}/rooms/{roomId}/sections")
public Flux<SectionDTO> getRoomSections(#PathVariable String buildingId,#PathVariable String floorId ,#PathVariable String roomId) {
What is the difference between #RequestParam and #PathVariable while handling special characters?
+ was accepted by #RequestParam as space.
In the case of #PathVariable, + was accepted as +.
#PathVariable is to obtain some placeholder from the URI (Spring call it an URI Template)
— see Spring Reference Chapter 16.3.2.2 URI Template Patterns
#RequestParam is to obtain a parameter from the URI as well — see Spring Reference Chapter 16.3.3.3 Binding request parameters to method parameters with #RequestParam
If the URL http://localhost:8080/MyApp/user/1234/invoices?date=12-05-2013 gets the invoices for user 1234 on December 5th, 2013, the controller method would look like:
#RequestMapping(value="/user/{userId}/invoices", method = RequestMethod.GET)
public List<Invoice> listUsersInvoices(
#PathVariable("userId") int user,
#RequestParam(value = "date", required = false) Date dateOrNull) {
...
}
Also, request parameters can be optional, and as of Spring 4.3.3 path variables can be optional as well. Beware though, this might change the URL path hierarchy and introduce request mapping conflicts. For example, would /user/invoices provide the invoices for user null or details about a user with ID "invoices"?
#RequestParam annotation used for accessing the query parameter values from the request. Look at the following request URL:
http://localhost:8080/springmvc/hello/101?param1=10¶m2=20
In the above URL request, the values for param1 and param2 can be accessed as below:
public String getDetails(
#RequestParam(value="param1", required=true) String param1,
#RequestParam(value="param2", required=false) String param2){
...
}
The following are the list of parameters supported by the #RequestParam annotation:
defaultValue – This is the default value as a fallback mechanism if request is not having the value or it is empty.
name – Name of the parameter to bind
required – Whether the parameter is mandatory or not. If it is true, failing to send that parameter will fail.
value – This is an alias for the name attribute
#PathVariable
#PathVariable identifies the pattern that is used in the URI for the incoming request. Let’s look at the below request URL:
http://localhost:8080/springmvc/hello/101?param1=10¶m2=20
The above URL request can be written in your Spring MVC as below:
#RequestMapping("/hello/{id}") public String getDetails(#PathVariable(value="id") String id,
#RequestParam(value="param1", required=true) String param1,
#RequestParam(value="param2", required=false) String param2){
.......
}
The #PathVariable annotation has only one attribute value for binding the request URI template. It is allowed to use the multiple #PathVariable annotation in the single method. But, ensure that no more than one method has the same pattern.
Also there is one more interesting annotation:
#MatrixVariable
http://localhost:8080/spring_3_2/matrixvars/stocks;BT.A=276.70,+10.40,+3.91;AZN=236.00,+103.00,+3.29;SBRY=375.50,+7.60,+2.07
And the Controller method for it
#RequestMapping(value = "/{stocks}", method = RequestMethod.GET)
public String showPortfolioValues(#MatrixVariable Map<String, List<String>> matrixVars, Model model) {
logger.info("Storing {} Values which are: {}", new Object[] { matrixVars.size(), matrixVars });
List<List<String>> outlist = map2List(matrixVars);
model.addAttribute("stocks", outlist);
return "stocks";
}
But you must enable:
<mvc:annotation-driven enableMatrixVariables="true" >
#RequestParam is use for query parameter(static values) like: http://localhost:8080/calculation/pow?base=2&ext=4
#PathVariable is use for dynamic values like : http://localhost:8080/calculation/sqrt/8
#RequestMapping(value="/pow", method=RequestMethod.GET)
public int pow(#RequestParam(value="base") int base1, #RequestParam(value="ext") int ext1){
int pow = (int) Math.pow(base1, ext1);
return pow;
}
#RequestMapping("/sqrt/{num}")
public double sqrt(#PathVariable(value="num") int num1){
double sqrtnum=Math.sqrt(num1);
return sqrtnum;
}
1) #RequestParam is used to extract query parameters
http://localhost:3000/api/group/test?id=4
#GetMapping("/group/test")
public ResponseEntity<?> test(#RequestParam Long id) {
System.out.println("This is test");
return ResponseEntity.ok().body(id);
}
while #PathVariable is used to extract data right from the URI:
http://localhost:3000/api/group/test/4
#GetMapping("/group/test/{id}")
public ResponseEntity<?> test(#PathVariable Long id) {
System.out.println("This is test");
return ResponseEntity.ok().body(id);
}
2) #RequestParam is more useful on a traditional web application where data is mostly passed in the query parameters while #PathVariable is more suitable for RESTful web services where URL contains values.
3) #RequestParam annotation can specify default values if a query parameter is not present or empty by using a defaultValue attribute, provided the required attribute is false:
#RestController
#RequestMapping("/home")
public class IndexController {
#RequestMapping(value = "/name")
String getName(#RequestParam(value = "person", defaultValue = "John") String personName) {
return "Required element of request param";
}
}
it may be that the application/x-www-form-urlencoded midia type convert space to +, and the reciever will decode the data by converting the + to space.check the url for more info.http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
#PathVariable - must be placed in the endpoint uri and access the query parameter value from the request
#RequestParam - must be passed as method parameter (optional based on the required property)
http://localhost:8080/employee/call/7865467
#RequestMapping(value=“/call/{callId}", method = RequestMethod.GET)
public List<Calls> getAgentCallById(
#PathVariable(“callId") int callId,
#RequestParam(value = “status", required = false) String callStatus) {
}
http://localhost:8080/app/call/7865467?status=Cancelled
#RequestMapping(value=“/call/{callId}", method = RequestMethod.GET)
public List<Calls> getAgentCallById(
#PathVariable(“callId") int callId,
#RequestParam(value = “status", required = true) String callStatus) {
}
Both the annotations behave exactly in same manner.
Only 2 special characters '!' and '#' are accepted by the annotations #PathVariable and #RequestParam.
To check and confirm the behavior I have created a spring boot application that contains only 1 controller.
#RestController
public class Controller
{
#GetMapping("/pvar/{pdata}")
public #ResponseBody String testPathVariable(#PathVariable(name="pdata") String pathdata)
{
return pathdata;
}
#GetMapping("/rpvar")
public #ResponseBody String testRequestParam(#RequestParam("param") String paramdata)
{
return paramdata;
}
}
Hitting following Requests I got the same response:
localhost:7000/pvar/!##$%^&*()_+-=[]{}|;':",./<>?
localhost:7000/rpvar?param=!##$%^&*()_+-=[]{}|;':",./<>?
!# was received as response in both the requests
#RequestParam:We can say it is query param like a key value pair
#PathVariable:-It is came from URI
Is it possible to differenciate those two request URIs in one controller handler :
http://my-uri/
http://my-uri/?with_empty_param
HttpServletRequest object has a ParameterMap object that maps parameter name and its value.
With this map we can check if a parameter was passed in servlet request.
// Check if with_empty_param parameter exists
if (request.getParameterMap().containsKey("with_empty_param ")) {
String with_empty_param = request.getParameter("with_empty_param ");
}
If you want to use Spring way you could do:
#RequestMapping(value = {"/init"}, method = RequestMethod.GET)
public String methodName(
#RequestParam Map<String,String> allParams, ModelMap model) {
if (allParams.containsKey("with_empty_param ")) {
...
}
The #RequestMapping annotation has a params argument which you can use for that.
#RequestMapping
public void method1() {}
And with a check on the param.
#RequestMapping(params={"param"})
public void method2() {}
You can also use the ! to negate the check, so if that param isn't present.
#RequestMapping(params={"!param"})
public void method3() {}
#RequestMapping(value = {"/userDetails", "/userDetails/edit/{id}"}, method = RequestMethod.GET)
public String userDetails(Map Model,****) {
//what goes here?
}
What will be my arguments to the userDetails method? And how do I differentiate /userDetails and /userDetails/edit/9 within the method?
Ideally we can get pathvariable by using annotation #PathVariable in method argument but here you have used array of url {"/userDetails", "/userDetails/edit/{id}"} so this will give error while supply request like localhost:8080/domain_name/userDetails , in this case no id will be supplied to #PathVariable.
So you can get the difference (which request is comming through) by using argument HttpServletRequest request in method and use this request object as below -
String uri = request.getRequestURI();
Code is like this -
#RequestMapping(value = {"/userDetails", "/userDetails/edit/{id}"}, method=RequestMethod.GET)
public String userDetails(Map Model,HttpServletRequest request) {
String uri = request.getRequestURI();
//put the condition based on uri
}
When I map multiple values to #RequestMapping(like Multiple Spring #RequestMapping annotations), can I get the requested value(URL)?
Like this:
#RequestMapping(value={"/center", "/left"}, method=RequestMethod.GET)
public String getCenter(Model model) throws Exception {
String requestedValue = getRequestedValue(); // I want this.
// I want to do something like this with requested value.
String result;
if (requestedValue.equals("center")
result = "center";
else if (requestedValue.equals("left")
result = "left";
return result;
}
You can have the Request (HttpServletRequest) itself as an parameter of the handler method. So you can then inspect the request url to get the "value".
#RequestMapping(value={"/center", "/left"}, method=RequestMethod.GET)
public String getCenter(Model model, HttpServletRequest request) throws Exception {
String whatYouCallValue = request.getServletPath();
....
Javadoc: https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getServletPath--
Btw: if I understand you right, you want to have different urls, not different values.
From Spring 3.1.0, you can use URI Template Patterns with Regular Expressions.
#RequestMapping(value={"/{path:[a-z-]+}"}, method=RequestMethod.GET)
public String getCenter(#PathVariable String path) throws Exception {
// "path" is what I want
}
From Spring 3.1.0, you can use ServletUriComponentsBuilder
#RequestMapping(value={"/center", "/left"}, method=RequestMethod.GET)
public String getCenter(Model model) throws Exception {
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentRequest();
String requestedValue = builder.buildAndExpand().getPath(); // I want this.
System.out.println(requestedValue);
// I want to do something like this with requested value.
String result="fail";
if (requestedValue.equals("center"))
result = "center";
else if (requestedValue.equals("left"))
result = "left";
return result;
}
Use RequestParam annotation. You can also add a parameter of type HttpServletRequest to your method and then getParameters from that.
Addition to the best answer #Hugh_Lee:
This method will work for all not mapped requests. If you want to use this method just for two (or several) cases only, e.g. "/center" and "/left", you may do following. Rename "center" to "positionCenter", "left" to "positionLeft" (or add another common word). So the code would be like this:
#RequestMapping(value={"/{path:position+[A-Za-z-]+}"}, method=RequestMethod.GET)
public String getCenter(#PathVariable String path) throws Exception {
// "path" is what I want
}
Following regex will make your method to be executed only for the urls /center and /left. And you can get the value with #PathVariable annotation.
#GetMapping("/{path:^center$|^left$}")
public ResponseEntity<?> whatIsThePath(#PathVariable String path){
// path is either "center" or "left"
}