如何检查REST请求中是否包含不受支持的参数?

时间:2018-12-16 19:48:04

标签: java spring rest

Spring(引导)是否可以检查REST请求是否包含未由调用的REST方法显式声明的参数?

使用required标志,我们可以强制客户端在请求中包含某个参数。我正在寻找一种类似的方式来禁止客户端发送控制器方法的声明中未明确提及的参数:

@RequestMapping("/hello")
public String hello(@RequestParam(value = "name") String name) {
    //throw an exception if a REST client calls this method and 
    //  sends a parameter with a name other than "name"
    //otherwise run this method's logic
}

例如致电

curl "localhost:8080/hello?name=world&city=London"

应该得到 4xx 答案。

一种选择是显式检查意外参数:

@RequestMapping("/hello")
public String hello(@RequestParam Map<String,String> allRequestParams) {
    //throw an exception if allRequestParams contains a key that we cannot process here
    //otherwise run this method's logic
}

但是在保持与第一个示例相同的便捷@RequestParam使用率的同时,还能实现相同的结果吗?

编辑:很抱歉,我没有看到与this question的任何连接。另一个问题是关于运行时的注释处理。我的问题是关于Spring REST引擎的行为。我想念什么吗?


编辑:根据答案,我写了这个HandlerInterceptor

@Component
public class TooManyParamatersHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod m = (HandlerMethod) handler;
        if (m.getMethod().getName().equals("error")) {
            return true;
        }
        List<String> allowedParameters = Stream.of(m.getMethodParameters())
                .flatMap(p -> Stream.of(p.getParameterAnnotation(RequestParam.class)))
                .filter(Objects::nonNull)
                .map(RequestParam::name).collect(Collectors.toList());
        ArrayList<String> actualParameters = Collections.list(request.getParameterNames());
        actualParameters.removeAll(allowedParameters);
        if (!actualParameters.isEmpty()) {
            throw new org.springframework.web.bind.ServletRequestBindingException(
                "unexpected parameter: " + actualParameters);
        }
        return true;
    }
}

3 个答案:

答案 0 :(得分:2)

您可以通过JavaEE 7中新增的ContainerRequestFilter功能来实现此功能,该功能使您可以访问当前请求匹配的资源类和资源方法,并在未匹配的情况下执行所需的操作。

您可以在此处了解更多信息:

https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/ResourceInfo.html

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;

@Provider
public class RequestParamFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        Set<String> acceptedParamList = new HashSet<String>();
        Method method = resourceInfo.getResourceMethod();
        for (Annotation[] annos : method.getParameterAnnotations()) {
            for (Annotation anno : annos) {
                if (anno instanceof QueryParam) {
                    acceptedParamList.add(((QueryParam) anno).value());
                }
            }
        }

        MultivaluedMap<String, String> queryParams = requestContext.getUriInfo().getQueryParameters();
        for (String param : queryParams .keySet()) {
            if (!acceptedParamList.contains(param)) {
                requestContext.abortWith(Response.status(Status.BAD_REQUEST).entity("Unexpected paramter found : "+param).build());
            }
        }
    }

}

P.N:大多数情况下,过滤器是应用程序速度上的代价,特别是如果其中包含复杂的链条!

在这种情况下(或类似情况),我建议使用它,因为大多数这些请求根本都不应该到达服务器应用程序。

希望对您有所帮助,祝您编程愉快! =)

答案 1 :(得分:1)

在这种情况下,您需要使用HandlerInterceptorHandlerInterceptorAdapter,请覆盖preHandle方法

@Override
public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) throws Exception {
           //request param validation validation
            return true; //or throw exception 
}

ServletRequest.getParameterMap()返回请求参数的键值映射。

答案 2 :(得分:1)

据我所知,您不能简单地使用Spring禁止参数。老实说,这个问题相当可疑且没有必要,我认为这是一种反模式。

但是,Spring提供了每个HttpServletRequestHttpServletResponse对象到控制器方法签名的映射。使用方法HttpServletRequest::getParameterMap来接收传递的参数的Map,以进行进一步的迭代和验证。

@RequestMapping("/hello")
public String hello(RequestParam(value = "name") String name, HttpServletRequest request, HttpServletResponse response) {
    final Map<String, String[]> parameterMap = request.getParameterMap();
    // logics
}

仅将那些对象传递给@RequestMapping("/hello")允许仅对所选映射执行验证。如果要全局定义此行为,建议您使用HandlerInterceptor::preHandle作为此处的答案。

如果将hello参数设置为required=true,则只需检查Map的大小是否等于1