简体   繁体   中英

How to check for unbound request parameters in a Spring MVC controller method?

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:

  • Request with param1=1 : method completes successfully.
  • Request with param1=blah : JBWEB000120: The request sent by the client was syntactically incorrect.

However...

  • If a request is made with an additional parameter (eg 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?

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

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; 
}

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.

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
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM