In Spring MVC, it is easy to bind request parameter to method paramaters handling the request. I just use #RequestParam
#Controller
public class ConfirmOrderAction {
public String toConfirmOrder(#RequestParam String itemIds){
}
}
but i hope like this
#Controller
public class ConfirmOrderAction {
#RequestParam
private String itemIds;
getItemIds(){}
setItemIds(){}
public String toConfirmOrder(){}
}
like struts2
This is not possible with Spring's #RequestParam annotation:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface RequestParam {
...
}
This means #RequestParam can only be used on method parameters.
Do not forget that Spring controllers are singletons by default (one instance is shared by multiple requests). So it would be a bad idea to bind request parameters to instance fields.
Related
I know there are validators in spring. However, these validators can only be bound to a single object. Say a Pojo in request body. However, I have a scenario where I have a get request and I want to validate a date range: I have a start date and the end date as #requestparams. How should I validate these?
Also there is a validator applied for the same #restcontroller: for post request, say Employeevalidtor. Can I invoke multiple validators for different objects in the same #restcontroller?
You can use separate validators but they have to me manually instantiated by passing the corresponding objects to be validated.
I assume you are talking about request binding validations. The same validations can be obtained with Spring Validators for #RequestParam and #PathVariables as mentioned in this post
Adding the relevant piece here. The controller will look something like this:
#RestController
#Validated
public class RegistrationController {
#RequestMapping(method = RequestMethod.GET,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
#ResponseStatus(HttpStatus.OK)
public Map search(#Email #RequestParam("email") String email) {
return emailMessage(email);
}
}
Note the #Validated method at the class level (which can also be declared at the method level).
Let Spring MVC will map your request parameters to a pojo encapsulating all the related inputs and then add a validator for that.
#RestController
#RequestMapping("/myUrl")
public class MytController {
private final MyIntervalValidator validator;
#InitBinder
public void initBinder(WebDataBinder binder){
binder.setValidator(validator);
}
#GetMapping
public void doSomthing(#Valid #RequestParam MyInterval interval){...}
class MyInterval implements Serializable{
private Date startDate;
private Date endDate;
}
import org.springframework.validation.Validator;
class MyIntervalValidator implements Validator{
#Override
public boolean supports(Class<?> clazz) {
return MyInterval.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
final MyInterval params = (MyInterval) target;
....
}
}
i have a controller like following
#RestController
class TestController{
private String country;
public String m1(){}
public String m2(){}
public String m3(){}
public String m4(){}
}
All the methods m1 to m4 needs the country that is taken from the header.
is there a method to do that for all the methods of this controller.
I tried using Interceptor but, i am not able to get country after the interceptor execution is over. that is i always get country a null
Spring MVC provides #RequestHeader which
indicates that a method parameter should be bound to a web request header
Annotate your handler method parameter appropriately
#RequestMapping("/somePath")
public ReturnType example(#RequestHeader("country-header") String country) {
// use it
}
The Spring MVC stack will extract the header value and use it to invoke your handler method.
How come this code just works? I didn't specify any custom converter or annotation (like #RequestBody or #ModelAttribute) before argument ? Request is filled correctly from this GET call:
http://localhost:8080/WS/foo?token=C124EBD7-D9A5-4E21-9C0F-3402A1EE5E9B&lastSync=2001-01-01T00:00:00&pageNo=1
Code:
#RestController
#RequestMapping(value = "/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public Result<Foo> excursions(Request request) {
// ...
}
}
Request is just POJO with getters and setters. I use it to shorten argument code because plenty methods uses those same arguments ...
public class Request {
private String token;
#DateTimeFormat(pattern = IsoDateTime.DATETIME)
private Date lastSync;
private Integer pageNo;
// getters and setters
}
This was my original method before introducing Request.
#RestController
#RequestMapping(value = "/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public Result<Foo> excursions(#RequestParam String token, #RequestParam #DateTimeFormat(pattern = IsoDateTime.DATETIME) Date lastSync, #RequestParam Integer pageNo) {
// ...
}
}
Request parameters will be mapped to POJOs, as it is happening in your case, by default. Additionally, if you use #ModelAttribute, an attribute in the Model will be created. That attribute can be then used in views, e.g. JSPs, to access the object.
#RequestBody annotation tells that the body of the request is NOT a set of form parameters like
token=C124EBD7-D9A5-4E21-9C0F-3402A1EE5E9B&lastSync=2001-01-01T00:00:00&pageNo=1
but is in some other format, such as JSON.
This is a feature provided by Spring MVC:
Customizable binding and validation. Type mismatches as application-level validation errors that keep the offending value, localized date and number binding, and so on instead of String-only form objects with manual parsing and conversion to business objects.
You can see it in the doc: http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/htmlsingle/
In Jersey 2 it is possible to do this:
#GET
#PATH("user/{email}")
public IDto getUser(#NotNull #Email #PathParam("email") String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
But I cannot make something similar to work in Spring MVC, it seems that the validation is only done when providing an object in #RequestBody or using an SpringMVC Form, for example the following won't work:
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
There are other similar questions, but those seem to be oriented to Spring MVC UI applications, in my case it is only a REST API which returns JSON response so I don't have any View to map/bind to the controller.
Seems it is possible, using #Validated.
Here's an example.
Based on OP's question, this should work:
#RestController
#Validated
public class MyController {
#GetMapping(value="/user/{email}")
public #ResponseBody IDto getUser(#NotNull #Email #PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
}
In plain Spring implementations, it may be required to manually register the validator bean:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
1- Simply add #Validated annotation at the top of your class.
2- Put whatever annotations for validations (#NotBlank, Min(1), etc.) before the #RequestParam annotation in your method signature.
The validated annotation from the org.springframework.validation.annotation.Validated package to validate a #PathVariable. Make sure the class annotated with #Validated.
#GetMapping("/name-for-day/{dayOfWeek}")
public String getNameOfDay(#PathVariable("dayOfWeek") #Min(1) #Max(7) Integer dayOfWeek) {
return dayOfWeek + "";
}
As far as I can tell, you cannot do this out-of-the-box with Spring.
Options:
Use a regular expression:
#RequestMapping(value="/user/{email:SOME_REXEXP}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
Use Hibernate Validator to validate the method. Either call the validator manually, or make Spring call it for you using AOP. See https://github.com/gunnarmorling/methodvalidation-integration
Controller should be annotated with spring's #Validated
So update your code with
#Validated
#RequestMapping(value="/user/{email}", method = RequestMethod.GET)
public #ResponseBody IDto getUser(
#NotNull
#Email
#PathVariable String validEmail) {
return userManagementService.findUserByEmail(validEmail);
}
I can't find a solution to this, and it's driving me crazy. I have #Controller mapped that responds to several methods using #RequestMapping. I'd like to tag one of those methods as default when nothing more specific is specified. For example:
#Controller
#RequestMapping("/user/*")
public class UserController {
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
So /user/login -> login method, /user/logout -> logout, etc. I'd like to make it so that if someone goes to /user then it routes to one of these methods. However, I don't see anything on #RequestMapping that would allow me to specify one of these methods as a default handler. I also don't see any other annotations that might be used on the class either to do this. I'm beginning to suspect it doesn't exist.
I'm using Spring 2.5.6. Is this solved in 3.0.0? I might just hack Spring to make it work because it's tremendously annoying this isn't more straightforward.
Thanks in Advance.
Take a look at this answer:
Spring MVC and annotated controllers issue
What if you annotate a method with:
#RequestMapping(method = RequestMethod.GET)
You can see an example here:
Spring 3.0 MVC + Hibernate : Simplified with Annotations – Tutorial
The same behavior can be seen here:
Spring Framework 3.0 MVC by Aaron Schram (look at page 21)
Short answer: I do not know how to simply specify one method as default with a simple tag.
But there is this ...
I do not know in which version of Spring this was implemented, but you can provide multiple values to #RequestMapping in 3.1.2. So I do this:
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = {"", "/", "/list"}, method = RequestMethod.GET)
public String listUsers(ModelMap model) { }
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ModelAndView add(HttpServletRequest request, ModelMap model) { }
}
The following URLs then map to listUsers():
http://example.com/user
http://example.com/user/
http://example.com/user/list
I would create one default method without RequestMapping's value in there. Please see method defaultCall() below. You can then simply call them with URL: [yourhostname]/user
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method = RequestMethod.GET)
public String defaultCall( MapModel model ) {
//Call the default method directly, or use the 'forward' String. Example:
return authenticate( model );
}
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
Ref: Spring Framework Request Mapping
Simply using #RequestMapping("**") on your default method should work. Any more specific mappings should still pick up their requests. I use this method for setting up default methods sometimes. Currently using Spring v4.3.8.RELEASE.