I just found that even if I omit the #RequestParam annotation on the organization parameter, Spring is still able to bind it.
#RequestMapping(value="", method = RequestMethod.POST)
#ResponseBody
public String save(String organization){
logger.info(organization); // it works
}
Can anyone points to the documentation that clarifies this behaviour? I have always though that #RequestParam was mandatory for binding to work.
Thanks
Take a look at https://reversecoding.net/spring-mvc-requestparam-binding-request-parameters/ There is an explanation:
Examples without #RequestParam
Based on the list of
HandlerMethodArgumentResolver configured in your application,
#RequestParam can also be omitted. If you have a look at the code of
method getDefaultArgumentResolvers() of RequestMappingHandlerAdapter
there is the following piece of code at the end:
// Catch-all resolvers.add(new
RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
// Catch-all resolvers.add(new
RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
Basically, it’s added to the resolvers a
RequestParamMethodArgumentResolver with useDefaultResolution set to
true. Looking at the documentation we can see that this means that
method argument that is a simple type, as defined in
BeanUtils.isSimpleProperty(java.lang.Class), is treated as a
request parameter even if it isn’t annotated. The request parameter
name is derived from the method parameter name.
Your resolvers do it automatically. When you pass the HandlerMethodArgumentResolver bean to your resolver, the BeanUtil checks if the parameter is a primitive value or a simple String. If so, it does the binding itself.
#RequestMapping(value = "/rest")
#ResponseBody
public String save(String username, String password) {
return String.format("username=%s password=%s", username, password);
}
Hit the service http://localhost:8080/rest?username=mypwd&password=uname
You will be able to see the result given below.
Output: username=pwd password=uname
here I have some examples for the #RequestParam to provide you,hope they can help you:
#RequestMapping(value = "/selection/findByField", method = RequestMethod.POST)
public #ResponseBody List<selectionsDO> add(#RequestParam(value = "field", required = true) String field,#RequestParam(value = "value", required = true) String value)
{ return mongoService.findByField(field,value);
}
The words "required = true" means that this field must submit at request.
Related
i am trying to pass a regular object to an api, via the MockMvc library.
Here is the example (Partiucalarly the FilterProperties properties object):
API:
#GetMapping("/test-api")
public PageResponse<SomeDto> getAllObjects(
FilterProperties properties,
#RequestParam(value = "searchPhrase", defaultValue = "") String searchPhrase,
#RequestParam(value = "actionType") ActionType actionType) {
System.out.println(searchPhrase);
return null;
}
I managed to successfully pass the #RequestParams with .param("searchPhrase", "SomePhrase"), however i cannot seem to find a way to pass the FilterProperties object, since its just a plain object and is not annotated as param, request body or some sort of attribute.
TEST:
final MvcResult mvcResult = restServiceMockMvc
.perform(get(CONTROLLER_BASE_PATH + "/test-api")
.param("searchPhrase", "SomePhrase")
.param("actionType", String.valueOf(ActionType.EDIT))
.requestAttr("properties", filterProperties)
.contentType(APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
I tried with requestAttr, flashAttr, sessionAttr and it does not break the call, however the api receives an empty object for filterProperties.
I appreciate any insights!
FilterProperties will be treated as there are #ModelAttribute annotated on it (mentioned by the rules in this table).
And #ModelAttribute will enable data binding which will try to bind the value from query parameters , or field name of the form data or others (see this for more details) to FilterProperties .
So that means assuming FilterProperties has the following fields :
public class FilterProperties {
private String prop1;
private String prop2;
}
Then you can configure the MockMvc as
mockMvc.perform(get(CONTROLLER_BASE_PATH + "/test-api")
.param("searchPhrase", "SomePhrase")
.param("actionType", String.valueOf(ActionType.EDIT))
.param("prop1", "prop1-value")
.param("prop2", "prop2-value"))
in order to pass the following values to the fields of the FilterProperties :
prop1 = prop1-value
prop2 = prop2-value
whenever you pass an object to an api, it has to be part of the api signature in one way or another; i guess here you want to pass a body for the GET api(which is highly discouraged),since GET calls are idempotent by nature.
if though if its for testing try passing it as a requestbody and then mock it
I have a web service written in Spring MVC. It can be used by 3rd party developers.
Our methods have a lot of optional parameters (passed in the query string).
I want to make sure that all the query string parameters are spelled correctly and there is no typos.
Is there an easy way to do it? Method signature example:
#RequestMapping(value = {"/filter"}, method = RequestMethod.GET)
#ResponseBody
public List<MetricType> getMetricTypes(
#RequestParam(value = "subject", required = false) Long subjectId,
#RequestParam(value = "area", required = false) Long areaId,
#RequestParam(value = "onlyImmediateChildren", required = false) Boolean onlyImmediateChildren,
#RequestParam(value = "componentGroup", required = false) Long componentGroupId
) throws Exception
{
//Some code
}
If somebody calls this method with "onlyImediateChildren=true" parameter (a typo) instead of "onlyImmediateChildren=true", Spring MVC will ignore the typoed parameter and will assume "onlyImmediateChildren" is null. Developer will get slightly incorrect list of results and will not notice the error. Such issues could be widespread and difficult to diagnose. I want to check there is no typoed params in query string to prevent such issues.
UPDATE
It is possible to extract the list of actual parameters from the query string. Then it could be compared with the list of the allowed parameters. If I hardcode the allowed parameter list, it will duplicate the method signature. I wonder if it is easy to extract a list of allowed parameters from the method signature (e.g. by #RequestParam annotation)?
Many thanks
Maxim
You could implement your own HandlerInterceptor. In preHandle method you can obtain all HandlerMethod's parameters annotated with #RequestParameter. These will be all allowed parameters in request.
Here is my implementation of an HandlerInterceptor which will only accept the parameters which are explicitely defined by a parameter annotation:
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerInterceptor
/**
* Interceptor which assures that only expected [RequestParam]s are send.
*/
#Component
class UnexpectedParameterHandler : HandlerInterceptor {
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler is HandlerMethod) {
val queryParams = request.parameterNames.toList()
val expectedParams = handler.methodParameters
.map { methodParameter ->
val requestParamName = methodParameter.getParameterAnnotation(RequestParam::class.java)?.name
val parameterName = methodParameter.parameter.name
requestParamName ?: parameterName
}
val unknownParameters = queryParams.minus(expectedParams)
if (unknownParameters.isNotEmpty()) {
response.writer.write("unexpected parameter $unknownParameters")
response.status = HttpStatus.BAD_REQUEST.value()
return false
}
}
return super.preHandle(request, response, handler)
}
}
You could use the getParameterMap method of the request to get a Map of all the submitted parameters, and validate the keys against a list of all allowed parameters. You should be able to get the request object by simply adding it to the method signature, e.g.:
public List<MetricType> getMetricTypes(
HttpServletRequest request,
#RequestParam(value = "subject", required = false) Long subjectId,
...
) throws Exception {
Spring will inject all the query parameters present in the url string through the argument of type
#RequestParam Map<String,String> in your controller method, if present.
#RequestMapping(value = "", method = RequestMethod.GET, produces = {"application/json"})
public HttpEntity<PagedResources<WebProductResource>> findAll(#RequestParam Map<String, String> allRequestParams){
...
}
You can then validate the keys of the map yourself. For an "enterprisey" way to do that generically, see my answer here: How to check spring RestController for unknown query params?
I would like to use different validation groups for a method's parameters and its return value.
I am trying to Spring's #Validated annotation to achieve this. For example:
public interface UserOperations {
#Validated({ResponseGroup.class})
#Valid
User createUser(#Validated({RequestGroup.class}) #Valid User user);
}
It appears that return value is in fact getting validated against ResponseGroup, however the method argument user is getting validated against both ResponseGroup and RequestGroup. I see why this is happening when I look at: org.springframework.validation.beanvalidation.MethodValidationInterceptor#invoke
Is there an elegant way to apply one validation group to the method argument and different validation group to the return value?
The problem is that the method MethodValidationInterceptor.invoke looks for validation groups defined in an #Validated annotation on each method, or on the class, but not on individual parameters. The validation groups that are specified will be applied to the parameters as well as the return value.
In order to have different validation groups get applied to parameters and return values, you don't even need to specify groups inside the #Validated annotation; instead you can use #javax.validation.groups.ConvertGroup.
#Validated
public interface UserOperations {
#NotNull
#Valid
#ConvertGroup(from = Default.class, to = User.Existing.class)
User create(#NotNull #Valid #ConvertGroup(from = Default.class, to = User.New.class) User user);
}
public interface User {
#Email(groups = {New.class, Existing.class})
String getEmail();
#Null(groups = {Existing.class})
#NotNull(groups = {New.class})
String getPassword();
interface Existing {
}
interface New {
}
}
Given the above, when UserOperations.create is called, the argument for user will get validated against the New group (must have a non-null password), while the return value will get validated against the Existing group (must have a null password).
As a side note, I realized Spring's method validation doesn't behave exactly in the same way as Spring MVC's automatic validation of #Valid #RequestBody. Additionally, If method validation is turned on for a Controller (has an #Validated annotation on the class), then the same #RequestBody method argument will get validated twice: once by standard method interceptor validation and once by MVC. To avoid MVC's validation on #RequestBody arguments of a given controller, I did the following:
#RestController
public class UserController implements UserOperations {
...
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(null);
}
...
}
I have a web service written in Spring MVC. It can be used by 3rd party developers.
Our methods have a lot of optional parameters (passed in the query string).
I want to make sure that all the query string parameters are spelled correctly and there is no typos.
Is there an easy way to do it? Method signature example:
#RequestMapping(value = {"/filter"}, method = RequestMethod.GET)
#ResponseBody
public List<MetricType> getMetricTypes(
#RequestParam(value = "subject", required = false) Long subjectId,
#RequestParam(value = "area", required = false) Long areaId,
#RequestParam(value = "onlyImmediateChildren", required = false) Boolean onlyImmediateChildren,
#RequestParam(value = "componentGroup", required = false) Long componentGroupId
) throws Exception
{
//Some code
}
If somebody calls this method with "onlyImediateChildren=true" parameter (a typo) instead of "onlyImmediateChildren=true", Spring MVC will ignore the typoed parameter and will assume "onlyImmediateChildren" is null. Developer will get slightly incorrect list of results and will not notice the error. Such issues could be widespread and difficult to diagnose. I want to check there is no typoed params in query string to prevent such issues.
UPDATE
It is possible to extract the list of actual parameters from the query string. Then it could be compared with the list of the allowed parameters. If I hardcode the allowed parameter list, it will duplicate the method signature. I wonder if it is easy to extract a list of allowed parameters from the method signature (e.g. by #RequestParam annotation)?
Many thanks
Maxim
You could implement your own HandlerInterceptor. In preHandle method you can obtain all HandlerMethod's parameters annotated with #RequestParameter. These will be all allowed parameters in request.
Here is my implementation of an HandlerInterceptor which will only accept the parameters which are explicitely defined by a parameter annotation:
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerInterceptor
/**
* Interceptor which assures that only expected [RequestParam]s are send.
*/
#Component
class UnexpectedParameterHandler : HandlerInterceptor {
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
if (handler is HandlerMethod) {
val queryParams = request.parameterNames.toList()
val expectedParams = handler.methodParameters
.map { methodParameter ->
val requestParamName = methodParameter.getParameterAnnotation(RequestParam::class.java)?.name
val parameterName = methodParameter.parameter.name
requestParamName ?: parameterName
}
val unknownParameters = queryParams.minus(expectedParams)
if (unknownParameters.isNotEmpty()) {
response.writer.write("unexpected parameter $unknownParameters")
response.status = HttpStatus.BAD_REQUEST.value()
return false
}
}
return super.preHandle(request, response, handler)
}
}
You could use the getParameterMap method of the request to get a Map of all the submitted parameters, and validate the keys against a list of all allowed parameters. You should be able to get the request object by simply adding it to the method signature, e.g.:
public List<MetricType> getMetricTypes(
HttpServletRequest request,
#RequestParam(value = "subject", required = false) Long subjectId,
...
) throws Exception {
Spring will inject all the query parameters present in the url string through the argument of type
#RequestParam Map<String,String> in your controller method, if present.
#RequestMapping(value = "", method = RequestMethod.GET, produces = {"application/json"})
public HttpEntity<PagedResources<WebProductResource>> findAll(#RequestParam Map<String, String> allRequestParams){
...
}
You can then validate the keys of the map yourself. For an "enterprisey" way to do that generically, see my answer here: How to check spring RestController for unknown query params?
I'm running a webapp in Spring Web MVC 3.0 and I have a number of controller methods whose signatures are roughly as follows:
#RequestMapping(value = "/{level1}/{level2}/foo", method = RequestMethod.POST)
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#RequestParam("foo_name") String fooname,
#RequestParam(value = "description", required = false) String description);
I'd like to add some validation - for example, description should be limited to a certain length or fooname should only contain certain characters. If this validation fails, I want to return a message to the user rather than just throw some unchecked exception (which would happen anyway if I let the data percolate down to the DAO layer). I'm aware of JSR303 but have not worked with it and don't quite understand how to apply it in a Spring context.
From what I understand, another option would be to bind the #RequestBody to an entire domain object and add validation constraints there, but currently my code is set up to accept individual parameters as shown above.
What is the most straightforward way to apply validation to input parameters using this approach?
This seems to be possible now (tried with Spring 4.1.2), see https://raymondhlee.wordpress.com/2015/08/29/validating-spring-mvc-request-mapping-method-parameters/
Extract from above page:
Add MethodValidationPostProcessor to Spring #Configuration class:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
Add #Validated to controller class
Use #Size just before #RequestParam
#RequestMapping("/hi")
public String sayHi(#Size(max = 10, message = "name should at most 10 characters long") #RequestParam("name") String name) {
return "Hi " + name;
}
Handle ConstraintViolationException in an #ExceptionHandler method
There's nothing built in to do that, not yet anyway. With the current release versions you will still need to use the WebDataBinder to bind your parameters onto an object if you want automagic validation. It's worth learning to do if you're using SpringMVC, even if it's not your first choice for this task.
It looks something like this:
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#Valid #ModelAttribute() FooWrapper fooWrapper,
BindingResult errors) {
if (errors.hasErrors() {
//handle errors, can just return if using Spring form:error tags.
}
}
public static class FooWrapper {
#NotNull
#Size(max=32)
private String fooName;
private String description;
//getset
}
If you have Hibernate Validator 4 or later on your classpath and use the default dispatcher setup it should "Just work."
Editing since the comments were getting kind of large:
Any Object that's in your method signature that's not one of the 'expected' ones Spring knows how to inject, such as HttpRequest, ModelMap, etc, will get data bound. This is accomplished for simple cases just by matching the request param names against bean property names and calling setters. The #ModelAttribute there is just a personal style thing, in this case it isn't doing anything. The JSR-303 integration with the #Valid on a method parameter wires in through the WebDataBinder. If you use #RequestBody, you're using an object marshaller based on the content type spring determines for the request body (usually just from the http header.) The dispatcher servlet (AnnotationMethodHandlerAdapter really) doesn't have a way to 'flip the validation switch' for any arbitrary marshaller. It just passes the web request content along to the message converter and gets back a Object. No BindingResult object is generated, so there's nowhere to set the Errors anyway.
You can still just inject your validator into the controller and run it on the object you get, it just doesn't have the magic integration with the #Valid on the request parameter populating the BindingResult for you.
If you have multiple request parameters that need to be validated (with Http GET or POST). You might as well create a custom model class and use #Valid along with #ModelAttribute to validate the parameters. This way you can use Hibernate Validator or javax.validator api to validate the params. It goes something like this:
Request Method:
#RequestMapping(value="/doSomething", method=RequestMethod.GET)
public Model dosomething(#Valid #ModelAttribute ModelRequest modelRequest, BindingResult result, Model model) {
if (result.hasErrors()) {
throw new SomeException("invalid request params");
}
//to access the request params
modelRequest.getFirstParam();
modelRequest.getSecondParam();
...
}
ModelRequest class:
class ModelRequest {
#NotNull
private String firstParam;
#Size(min = 1, max = 10, message = "You messed up!")
private String secondParam;
//Setters and getters
public void setFirstParam (String firstParam) {
this.firstParam = firstParam;
}
public String getFirstParam() {
return firstParam;
}
...
}
Hope that helps.