Spring过滤器:获取servlet url-pattern

mor*_*bio 3 java mapping rest spring servlets

假设我已经定义了这些 REST 端点:

@RequestMapping(path = "/user")
@RestController
public class ConfigUserController {

    [...]
    
    @GetMapping(path = "/variables/")
    public ResponseEntity<List<Variable>> getAllVariables(...)
        [...]
    }
    
    @GetMapping(path = "/variables/{name}")
    public ResponseEntity<Variable> getVariableByName(...)
        [...]
    }
    
    @PutMapping(path = "/variables/{name}/{value}")
    public ResponseEntity<Variable> setVariableByName(...)
        [...]
    }
}
Run Code Online (Sandbox Code Playgroud)

我定义了两个过滤器(日志记录和授权),在过滤器内我想获取与当前请求匹配的 url-patter。使用上面的例子:

  • 如果请求是对 /variables 的 GET,我想要“/variables”
  • 如果请求是对 /variables/myfancyname 的 GET,我想要“/variables/{name}”
  • 如果请求是对 /variables/myfancyname/myvalue 的 PUT,我想要“/variables/{name}/{value}”

基本上我需要的是获取过滤器的 doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 方法内的映射注释中定义的路径。

理想情况下,我希望能够像这样访问过滤器中的 url-pattern 值:

@Component
@Order(3)
public class MyAuthorizationRequestFilter extends GenericFilterBean {
    [...]
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // here I get the value
        String urlPattern = request.useSomeMethod().toGetUrlPattern();
        [...]
        boolean auth = false;
        // here I do stuff
        [...]
        // finally let's see if the user is authorized
        if (auth) {
            chain.doFilter(request, responde);
        } else {
            throw SomeAuthorizationError();
        }
    }
    [...]
}
Run Code Online (Sandbox Code Playgroud)

我一直在寻找,但找不到有效的解决方案。

如果可能的话,我宁愿不扫描集合或 web.xml 文件(我没有),也不使用反射,因为每次触发过滤器时都会执行代码,并且我不想影响性能。

欢迎提供链接或建议。

编辑:添加详细信息,添加过滤器示例 cose

Sib*_*med 6

1. 使用Servlet过滤器的解决方案(在Filter doFilter之后) ## 这里,不推荐这种解决方案。

过滤器在请求到达 DispatcherServlet 之前拦截请求,这使得它们非常适合粗粒度任务,例如:

  • 验证
  • 日志记录和审计
  • 图像和数据压缩
  • 我们希望与 Spring MVC 解耦的任何功能

注意*在尝试调用getAttribute之前先执行doFilter

你的控制器

@RequestMapping(path = "/user")
@RestController
public class ConfigUserController {

    @GetMapping(path = "/variables/")
    public ResponseEntity<List<Variable>> getAllVariables() {
        return null;
    }

    @GetMapping(path = "/variables/{name}")
    public ResponseEntity<Variable> getVariableByName(@PathVariable("name") String name) {
        return new ResponseEntity<Variable>(new Variable(name), HttpStatus.OK);
    }

    @PutMapping(path = "/variables/{name}/{value}")
    public ResponseEntity<Variable> setVariableByName() {
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

自定义过滤器

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO
        try {
            chain.doFilter(request, response);
        } catch (Exception e) {
        } finally {
            String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
            System.out.println("Request template is, " + pattern);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

输出:

 Request template is, /user/variables/{name}
Run Code Online (Sandbox Code Playgroud)

注意* 在尝试调用 getAttribute 之前先执行 chain.doFilter


2. 使用Interceptor的解决方案

HandlerIntercepors 拦截 DispatcherServlet 和我们的控制器之间的请求。这是在 Spring MVC 框架内完成的,提供对 Handler 和 ModelAndView 对象的访问。

自定义拦截器

@Component
public class LoggerInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3)
            throws Exception {

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView model)
            throws Exception {

    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        String path = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        System.out.println("path : " + path);
        return true;
    }}
Run Code Online (Sandbox Code Playgroud)

使用 InterceptorRegistry 注册它

@Component
public class CustomServiceInterceptorAppConfig implements WebMvcConfigurer {
   @Autowired
   LoggerInterceptor loggerInterceptor;

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(loggerInterceptor);
   }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

3. 使用Java Reflection API 的解决方案。

单例范围并设置为预实例化(默认)的 Bean 是在创建容器时创建的。这是一次性过程,您不必担心性能。

您可以预先扫描所有@RequestMapping,@GetMapping,@PostMApping等注释,保留它,然后在过滤器中匹配模式以获得所需的结果。

这是样本

@Component
public class Config {

    public List<String> getPatterns() throws ClassNotFoundException {

        List<String> str = new ArrayList<String>();
        Class clazz = Class.forName("com.example.demo.controller.ConfigUserController"); // or list of controllers,
                                                                                            // //you can iterate that
                                                                                            // list
        for (Method method : clazz.getMethods()) {
            for (Annotation annotation : method.getDeclaredAnnotations()) {
                if (annotation.toString().contains("GetMapping")) {
                    str.add(annotation.toString());
                    System.out.println("Annotation is..." + annotation.toString());
                }
                if (annotation.toString().contains("PutMapping")) {
                    str.add(annotation.toString());
                    System.out.println("Annotation is..." + annotation.toString());
                }
                if (annotation.toString().contains("PostMapping")) {
                    str.add(annotation.toString());
                    System.out.println("Annotation is..." + annotation.toString());
                }
            }
        }

        return str;

    }
Run Code Online (Sandbox Code Playgroud)

自定义过滤器

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomFilter implements Filter {
    @Autowired
    Config con;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO
        try {
            //filter match the pattern to get the desired result
            System.out.println(con.getPatterns());
        } catch (ClassNotFoundException e) {
            
        }
        chain.doFilter(request, response);
    }
Run Code Online (Sandbox Code Playgroud)