如何检查Spring RestController的未知查询参数?

时间:2015-01-22 15:24:33

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

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

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

@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状态代码?

4 个答案:

答案 0 :(得分:10)

您可以按照自己的方式获取所有参数并进行处理。 引用spring文档:

  

在Map< String,String>上使用@RequestParam注释时或MultiValueMap< String,String>参数,地图填充了所有请求参数。

答案 1 :(得分:4)

在您的控制器方法中,您可以包含类型为@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;
    }

}

现在,当用户点击/小部件时?someUnknownField = abc&amp; 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"]
}

答案 2 :(得分:2)

在方法参数中添加HttpServletRequest request,执行

String query = request.getQueryString()
在方法体中

并验证。

答案 3 :(得分:2)

我想分享一种不同的方式,因为我发现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
    }