简体   繁体   English

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

[英]How to check if unsupported parameter is contained in REST request?

Is there with Spring (boot) a way to check if a REST request contains a parameter not explicitly declared by the called REST method? Spring(引导)是否有一种方法来检查 REST 请求是否包含被调用的 REST 方法未明确声明的参数?

With the required flag we can force the client to include a certain parameter in the request.使用required标志,我们可以强制客户端在请求中包含某个参数。 I am looking for a similar way to disallow the client to send a parameter that is not explicity mentioned in the declaration of the controller method:我正在寻找一种类似的方法来禁止客户端发送控制器方法声明中未明确提及的参数:

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

For example calling例如调用

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

should result in a 4xx answer.应该导致4xx答案。

One option would be to explicitly check for unexpected parameters:一种选择是显式检查意外参数:

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

But is it also possible to achieve the same result while keeping the same convenient @RequestParam usage as in the first example?但是,是否也可以在保持与第一个示例相同的@RequestParam使用方便的同时获得相同的结果?

EDIT : Sorry, I do not see any connection to this question .编辑:对不起,我没有看到与这个问题有任何联系。 The other question is about annotation processing at runtime.另一个问题是关于运行时的注解处理。 My question is about the behaviour of Spring's REST engine.我的问题是关于 Spring 的 REST 引擎的行为。 Am I missing something?我错过了什么吗?


EDIT : Based on the answers, I have written this HandlerInterceptor : 编辑:根据答案,我写了这个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;
    }
}

You can do it by ContainerRequestFilter feature which is added from JavaEE 7 that lets you access the resource class and resource method matched by the current request and make you to do your desire action when that have not been matched.您可以通过从 JavaEE 7 添加的ContainerRequestFilter特性来实现,它允许您访问与当前请求匹配的资源类和资源方法,并让您在没有匹配时执行您想要的操作。

You can read more here:你可以在这里阅读更多:

https://docs.oracle.com/javaee/7/api/javax/ws/rs/container/ResourceInfo.html 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());
            }
        }
    }

}

PN: Filters are cost in your application speed most of the times, Specially if you have complex chains in it! PN:大多数情况下,过滤器会影响您的应用程序速度,特别是如果您有复杂的链!

I recommend to use it in this case (and similar cases) because of most of the those requests should not be reached to the server application at all.我建议在这种情况(和类似情况)中使用它,因为大多数请求根本不应该到达服务器应用程序。

I hope this helps you and Happy coding!我希望这对你有帮助,祝你编码愉快! =) =)

In this case you required HandlerInterceptor or HandlerInterceptorAdapter , override the preHandle method在这种情况下,您需要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() returns a map of key-values of the request parameters. ServletRequest.getParameterMap() 返回请求参数的键值映射。

As far as I know, you cannot simply disallow parameters using Spring.据我所知,您不能简单地禁止使用 Spring 的参数。 Honestly, this issue is rather questionable and unnecessary and I think it's an antipattern.老实说,这个问题相当值得怀疑和不必要,我认为这是一个反模式。

However, Spring provides with each mapping the HttpServletRequest and HttpServletResponse objects to the controller method signature.但是,Spring 为每个映射提供了HttpServletRequestHttpServletResponse对象到控制器方法签名。 Use the method HttpServletRequest::getParameterMap to receive the Map of the passed parameters for the further iteration and validation.使用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
}

Passing those object to only to the @RequestMapping("/hello") allows performing the validation only to the selected mapping.将这些对象仅传递给@RequestMapping("/hello")允许仅对选定的映射执行验证。 If you want to define this behavior globally, I suggest you use HandlerInterceptor::preHandle as answered here.如果您想全局定义此行为,我建议您使用HandlerInterceptor::preHandle作为此处的回答。

If you make the hello parameter required=true , then you can just check the size of the Map whether is equal to 1 or not.如果你让hello参数required=true ,那么你可以只检查 Map 的大小是否等于1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM