【问题标题】:How to check for unbound request parameters in a Spring MVC controller method?如何在 Spring MVC 控制器方法中检查未绑定的请求参数?
【发布时间】:2014-02-02 11:46:05
【问题描述】:

给定一个 Spring-MVC 控制器方法:

@RequestMapping(value = "/method")
public void method(ParamModel params) { /*...*/ }

带模型类:

public class ParamModel { public int param1; }

以下两个结果符合预期/期望:

  • 使用param1=1:method 的请求成功完成。
  • 请求param1=blah: JBWEB000120: The request sent by the client was syntactically incorrect.

不过……

  • 如果使用附加参数(例如nonexistentparam=1)发出请求,没有错误

如果请求包含不属于此 API 的任何参数,是否有办法确保请求得到验证并被拒绝?

【问题讨论】:

  • 我没有看到自己需要这样做,也找不到任何直接解决它的文档,但似乎您可能需要配置(或子类化)容器的RequestMappingHandlerAdapter .
  • 如何区分无效请求参数和有效请求参数?请求参数未映射到控制器参数这一事实并不意味着它是无效的请求参数,因为可能有其他组件使用它,例如servlet 过滤器,或者它只是简单地通过并再次呈现在隐藏字段中。
  • @chrylis 这样做的需要是由分离的客户端/服务器开发驱动的。我正在处理服务器端的 API,该 API 可能会发生变化,并且希望客户端请求中断,例如,如果参数被重命名或弃用(而不是默默地忽略它们)。
  • @SteveChambers 如果这是基于 REST 的,处理 API 更改的更简单、更可靠的方法是对您正在使用的媒体类型进行版本控制。
  • @chrylis 不确定我完全理解你的意思,但假设上面的 ParamModel 以某种方式被版本化为 1.0。如果客户端调用了 1.0 版,但错误地传入了不属于 ParamModel 的参数,我希望它报告错误。不确定版本控制如何解决这个问题?

标签: java spring spring-mvc parameters controller


【解决方案1】:

有一种方法可以覆盖控制器请求方法调用:

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

在 InvocableHandlerMethod 中,请求参数和方法参数都可以轻松访问和验证。

【讨论】:

    【解决方案2】:

    最明显、无聊和非弹性的选择是使用:

    @RequestParam Map<String,String> allRequestParams
    

    ...并自己检查参数列表。它当然需要您手动解析(到整数等)和验证值,而不是使用 DTO 和/或 javax.validation 注释。

    完整示例(需要将 InvalidParamsException 映射到状态码):

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

    【讨论】:

      【解决方案3】:

      Spring 的 @RequestMapping 采用“params”参数。

      文档:

      映射请求的参数,缩小主映射。

      任何环境的相同格式:“myParam=myValue”样式的序列 表达式,只有在每个这样的参数都被映射时才映射请求 发现有给定的值。表达式可以通过使用 "!=" 运算符,如 "myParam!=myValue"。 “myParam”样式表达式 也支持,这些参数必须存在于 请求(允许有任何值)。最后,“!myParam”样式 表达式表明指定的参数不应该 出现在请求中。

      另一种可能性是使用PathVariable(始终需要)或RequestParam 和参数required=true

      更新:

      您可以通过继承 RequestMappingHandlerMapping 并覆盖 getCustomMethodCondition/getCustomTypeCondition 来创建自己的请求映射条件。

      但是 XML 配置 &lt;mvc:annotation-driven/&gt; 不能使用,因为它也声明了这个 Bean,你最终会得到 2 个处理程序映射。 详情请看Adding custom RequestCondition's in Spring mvc 3.1

      【讨论】:

      • 他想拒绝所有个不匹配的参数。映射条件允许您指定将阻止匹配的参数。
      【解决方案4】:

      您可以使用过滤器来检查无效参数,如

      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 类

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

      希望这能解决您的问题

      【讨论】:

      • 这是一个通用的“做某事”答案,根本没有解决如何配置 Spring 以在模型类中未定义额外参数时引发错误。
      【解决方案5】:

      一个好的做法是 Bean-Validation (JSR-303)。这是Document

      保持简单,你需要在你的 spring 配置中有这个:

      <mvc:annotation-driven />
      

      你可以在你的代码中包含这个:

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

      【讨论】:

      • 虽然 Bean Validation 很有用,但这并不能回答这个问题,即如果发送了额外的 HTTP 参数,如何让 Spring 引发错误。 Bean Validation 在它们被设置到模型对象之后发生。
      猜你喜欢
      • 2013-02-08
      • 2014-07-02
      • 1970-01-01
      • 2012-11-06
      • 1970-01-01
      • 2011-11-10
      • 1970-01-01
      • 2012-11-06
      相关资源
      最近更新 更多