【问题标题】:How to check spring RestController for unknown query params?如何检查 spring RestController 的未知查询参数?
【发布时间】:2015-03-21 11:04:58
【问题描述】:

我有一个带参数的基本休息控制器。

如果查询字符串包含我未定义的参数,我该如何拒绝连接?

@RestController
@RequestMapping("/")
public class MyRest {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    public String content(@PathVariable id, @RequestParam(value = "page", required = false) int page) {
        return id;
    }
}

localhost:8080/myapp/123?pagggge=1

目前调用这个url时,方法只用id执行,未知的paggge参数就被忽略了。一般情况下这很好,但我如何验证它们并返回 HTTP 状态代码?

【问题讨论】:

  • 我想知道...为什么重要?如果您不接受该参数,让它掉在地板上也无妨。

标签: java spring web-services rest spring-mvc


【解决方案1】:

在方法参数中添加HttpServletRequest request,这样做

String query = request.getQueryString()

在方法体中并对其进行验证。

【讨论】:

  • 不幸的是,这不起作用,因为只有在查询路径包含...?query=testvalue时才会设置查询
  • @membersound 嗯,我可能误解了。在params中使用HttpServletRequest request,在方法中使用String query = request.getQueryString()怎么样?
  • 太棒了。相应地更新了答案。 :)
  • 不是通过编写代码来实际验证,而是可以使用一些 JSR 303 验证吗?或其他一些验证?
  • @Rajas 并非如此,因为您通常只想忽略任何未知参数,只验证您实际使用的参数。对于您使用的那些参数,当然有。
【解决方案2】:

您可以按照您想要的方式获取所有传入和处理的参数。 引用 spring 文档:

当在 Map 或 MultiValueMap 参数上使用 @RequestParam 注释时,映射将填充所有请求参数。

【讨论】:

    【解决方案3】:

    在您的控制器方法中,您可以包含@RequestParam Map<String, String> 类型的参数以访问所有传入的查询参数。通用的 ArgsChecker 服务类可用于检查用户是否传入了无效参数。如果是这样,您可以抛出异常,该异常可以由@ControllerAdvice 类处理。

    @RestController
    @ExposesResourceFor(Widget.class)
    @RequestMapping("/widgets")
    public class WidgetController {
    
    
    @Autowired
    ArgsChecker<Widget> widgetArgsChecker;
    
        @RequestMapping(value = "", method = RequestMethod.GET, produces = {"application/hal+json"})
        public HttpEntity<PagedResources<WidgetResource>> findAll(@RequestParam @ApiIgnore Map<String, String> allRequestParams, Pageable pageable, PagedResourcesAssembler pageAssembler) {
            Set<String> invalidArgs = widgetArgsChecker.getInvalidArgs(allRequestParams.keySet());
            if (invalidArgs.size() > 0) {
                throw new QueryParameterNotSupportedException("The user supplied query parameter(s) that are not supported: " + invalidArgs + " . See below for a list of query paramters that are supported by the widget endpoint.", invalidArgs, widgetArgsChecker.getValidArgs());
    
            }
    

    ArgsChecker 可以定义如下:

    import com.widgetstore.api.annotation.Queryable;
    import lombok.Getter;
    import org.apache.commons.lang3.reflect.FieldUtils;
    
    import java.lang.reflect.Field;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    public class ArgsChecker<T> {
        @Getter
        private Set<String> validArgs;
    
        private ArgsChecker(){};
    
            public ArgsChecker(Class<T> someEntityClass){
        validArgs= FieldUtils.getFieldsListWithAnnotation(someEntityClass,Queryable.class)
                .stream()
                .map(Field::getName)
                .collect(Collectors.toSet());
        validArgs.add("page");
        validArgs.add("size");
    
    }
    
    public Set<String> getInvalidArgs(final Set<String> args){
        Set<String> invalidArgs=new HashSet<>(args);
        invalidArgs.removeAll(validArgs);
        return invalidArgs;
    
    
    
        }
       }
    

    ,它使用反射来查找带​​有“@Queryable”注释的字段:

    package com.widgetstore.api.annotation;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Queryable {
    } 
    

    现在标记您想要查询的域类的字段 注释:

    @Getter
    @Setter
    public class Widget {
        @Queryable 
        private String productGuid;
        @Queryable 
        private String serialNumber;
    
        private String manufacturer;
    

    现在确保在应用程序启动时创建 ArgsChecker bean:

    @SpringBootApplication
    public class StartWidgetApi{
    
    public static void main(String[] args){
        SpringApplication.run(StartWidgetApi.class);
    }
    
    
    @Bean(name="widgetArgsChecker")
    public ArgsChecker<Widget> widgetArgsChecker(){
        return new ArgsChecker<Widget>(Widget.class);
    }
    
    //Other ArgsCheckers of different types may be used by other controllers.
    @Bean(name="fooArgsChecker")
    public ArgsChecker<Foo> fooArgsChecker(){
        return new ArgsChecker<Foo>(Foo.class);
     }
    }
    

    最后,

    定义一个@ControllerAdvice 类,它将监听您的应用程序抛出的异常:

    package com.widgetstore.api.exception;
    
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    @ControllerAdvice
    @RequestMapping(produces = "application/json")
    @ResponseBody
    public class RestControllerAdvice {
    
    
        @ExceptionHandler(QueryParameterNotSupportedException.class)
        public ResponseEntity<Map<String,Object>> unrecogonizedParameter(final QueryParameterNotSupportedException e){
            Map<String,Object> errorInfo = new LinkedHashMap<>();
            errorInfo.put("timestamp",new Date());
            errorInfo.put("errorMessage",e.getMessage());
            errorInfo.put("allowableParameters",e.getValidArgs());
            return new ResponseEntity<Map<String, Object>>(errorInfo,HttpStatus.BAD_REQUEST);
        }
    
    
    
    }
    

    ,并定义QueryParameterNotSupportedException:

    import lombok.Getter;
    
    import java.util.Set;
    
    @Getter
    public class QueryParameterNotSupportedException extends RuntimeException{
        private Set<String> invalidArgs;
    
        private Set<String> validArgs;
    
        public QueryParameterNotSupportedException(String msg, Set<String> invalidArgs, Set<String> validArgs){
            super(msg);
            this.invalidArgs=invalidArgs;
            this.validArgs=validArgs;
        }
    
    }
    

    现在,当用户点击 /widgets?someUnknownField=abc&someOtherField=xyz 时,他将收到类似以下内容的 json 响应

     {"timestamp": 2017-01-10'T'blahblah, "errorMessage": "The user supplied query parameter(s) that are not supported: ["someUnknownField","someOtherField"]. See below for a list of allowed query parameters." ,
    "allowableParameters": ["productGuid","serialNumber"]
    }
    

    【讨论】:

      【解决方案4】:

      我想以不同的方式分享,因为我发现 ametiste 的答案过于手动而且 mancini0 过于冗长。

      假设,您使用 Spring 框架验证 API 将您的请求参数绑定到对象 ApiRequest

      @InitBinder
      public void initBinder(WebDataBinder binder, WebRequest request) {
          binder.setAllowedFields(ApiRequest.getAllowedRequestParameters());
      }
      
      @RequestMapping("/api")
      public String content(@Valid ApiRequest request, BindingResult bindingResult) {
          return request.getId();
      }
      

      具有以下 ApiRequest 定义。

      public class ApiRequest {
          private String id;
      
          public static String[] getAllowedRequestParameters() {
              return new String[]{
                  "id"
              };
          }
      

      }

      然后你可以使用BindingResult来检查是否有任何意外的请求参数,像这样:

          String[] suppressedFields = bindingResult.getSuppressedFields();
          if (suppressedFields.length > 0) {
              // your code here
          }
      

      【讨论】:

      • 有没有办法返回“400 Bad Request”而不必每次都检查bindingResult
      猜你喜欢
      • 1970-01-01
      • 2022-01-15
      • 1970-01-01
      • 2020-04-16
      • 2020-04-13
      • 2014-06-09
      • 2020-08-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多