Reputation: 39424
Given a Spring-MVC controller method:
@RequestMapping(value = "/method")
public void method(ParamModel params) { /*...*/ }
with model class:
public class ParamModel { public int param1; }
The following two results are as expected/desired:
param1=1
: method
completes successfully.param1=blah
: JBWEB000120: The request sent by the client was syntactically incorrect.
However...
nonexistentparam=1
), there is no error.Is there a way to ensure the request is validated and rejected if it includes any parameters that aren't part of this API?
Upvotes: 5
Views: 3076
Reputation: 246
There is a way to override controllers request methods invocations:
@Bean
public WebMvcRegistrations mvcRegistrations() {
return new WebMvcRegistrationsAdapter() {
@Override
public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
return new RequestMappingHandlerAdapter() {
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
@Override
protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
return new ServletInvocableHandlerMethod(handlerMethod) {
Set<String> boundParametersNames = Stream.of(getMethodParameters())
.map(methodParameter -> {
methodParameter.initParameterNameDiscovery(parameterNameDiscoverer);
return methodParameter.getParameterName();
})
.collect(Collectors.toSet());
@Override
public Object invokeForRequest(NativeWebRequest request,
ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
for (Iterator<String> iterator = request.getParameterNames(); iterator.hasNext(); ) {
String parameterName = iterator.next();
if (!boundParametersNames.contains(parameterName)) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
}
return super.invokeForRequest(request, mavContainer, providedArgs);
}
};
}
};
}
};
}
In a InvocableHandlerMethod both request parameters and method parameters can be easily accessed and verified.
Upvotes: 0
Reputation: 93
The most obvious, boring and non-Springish option would be to use:
@RequestParam Map<String,String> allRequestParams
... and check the list of parameters for yourself. It of course requires you to parse (to integer, etc.) and validate the values manually instead of using a DTO and/or javax.validation annotations.
Full example (requires mapping the InvalidParamsException to a status code):
@GetMapping("/my_strict_api")
public void myStrictApi(@RequestParam Map<String,String> allRequestParams) {
Set<String> allowed = new HashSet<>(Arrays.asList("cat", "dog"));
if (!allowed.containsAll(allRequestParams.keySet())) {
throw new InvalidParamsException("We only accept: " + allowed.toString());
}
// You should also validate the parameter values before using them
}
Upvotes: 0
Reputation: 46
Spring's @RequestMapping takes a "params" parameter.
Documentation:
The parameters of the mapped request, narrowing the primary mapping.
Same format for any environment: a sequence of "myParam=myValue" style expressions, with a request only mapped if each such parameter is found to have the given value. Expressions can be negated by using the "!=" operator, as in "myParam!=myValue". "myParam" style expressions are also supported, with such parameters having to be present in the request (allowed to have any value). Finally, "!myParam" style expressions indicate that the specified parameter is not supposed to be present in the request.
Another possiblity is to use PathVariable (always required) or RequestParam with parameter required=true.
Update:
You could create your own request mapping conditions by subclassing RequestMappingHandlerMapping and overriding getCustomMethodCondition/getCustomTypeCondition.
However the XML configuration <mvc:annotation-driven/>
cannot be used as that also declares this Bean and you would end up with 2 handler mappings.
Look at Adding custom RequestCondition's in Spring mvc 3.1 for details.
Upvotes: 0
Reputation: 2819
You can use filter to check for invalid parameters as
web.xml
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>com.mypackage.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
MyFilter Class
import javax.servlet.Filter;
public class MyFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String requestUrl = request.getParameter("param1");
//here I am considering 'param1=1' as valid request rest of all are invalid
if(!requestUrl.equals("1")) {
logger.info("Invalid Request");
//for invalid request redirect to error or login page
response.sendRedirect("/error"");
} else {
logger.info("Valid Request");
}
}
public void init(FilterConfig filterConfig) throws ServletException {
}
}
hope this will solve your problem
Upvotes: 1
Reputation: 10017
A good practice will be Bean-Validation (JSR-303). here is the Document
keep it simple, you need have this in your spring config:
<mvc:annotation-driven />
and you can have this in your code:
@RequestMapping(value = "/method")
public void method(@Valid ParamModel params, BindingResult result) {
if(result.hasErrors()) {...}
else {...}
}
public class ParamModel {
@SomeAnnotation // details see document
private int param1;
}
Upvotes: 0