5

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 (e.g. 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?

6
  • I haven't seen the need to do this myself, and I couldn't find any documentation directly addressing it, but it seems that you may need to configure (or subclass) the container's RequestMappingHandlerAdapter. Commented Jan 14, 2014 at 12:37
  • 2
    How to distinguish between request params that are invalid from valid ones? The fact that a request parameter is not mapped to a controller param does not mean that it is an invalid request param, because there might be other components that use it, e.g. servlet filters or it is simply passed through and rendered in a hidden field again. Commented Jan 14, 2014 at 12:41
  • @chrylis The need to do this is driven by a split client/server development. I'm working on an API on the server side that is subject to change and would like the client requests to break if for example a parameter is renamed or deprecated (rather than silently ignoring them). Commented Jan 14, 2014 at 14:32
  • @SteveChambers If this is REST-based, a simpler and more reliable way to handle API changes is to version the media types that you're using. Commented Jan 14, 2014 at 14:54
  • @chrylis Not sure I understand exactly what you mean but let's say the above ParamModel was somehow versioned as 1.0. If a client called version 1.0 but erroneously passed in a parameter that isn't part of ParamModel, I'd like it to report an error. Not sure how versioning solves this? Commented Jan 14, 2014 at 15:29

5 Answers 5

1

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

Sign up to request clarification or add additional context in comments.

1 Comment

This is a generic "do something" answer that doesn't at all address how to configure Spring to raise an error if there are extra parameters not defined in the model class.
0

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

1 Comment

While Bean Validation is useful, this doesn't answer this question, which is how to get Spring to raise an error if extra HTTP parameters are sent. Bean Validation happens after they're set onto a model object.
0

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.

1 Comment

He wants to reject all unmatched parameters. The mapping conditions allow you to specify parameters that will prevent a match.
0

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
}

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.