Spring 过滤器:获取 servlet url-pattern

问题描述 投票:0回答:2

假设我已经定义了这些 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(...)
        [...]
    }
}

我定义了两个过滤器(日志记录和授权),在过滤器内我想获取与当前请求匹配的 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();
        }
    }
    [...]
}

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

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

欢迎提供链接或建议。

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

java spring rest servlets mapping
2个回答
5
投票

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

自定义过滤器

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

}

O/P:

 Request template is, /user/variables/{name}

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


2.使用拦截器的解决方案

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

使用InterceptorRegistry进行注册

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

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      registry.addInterceptor(loggerInterceptor);
   }
}

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;

    }

自定义过滤器

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

2
投票

根据提出的问题更新答案以使用

GenericFilterBean.doFilter
。 您可以通过添加finally 子句来做到这一点。这是必需的,因为您需要在调用
chain.doFilter(request, response)
之前先执行
request.getAttribute
,因为该属性直到请求生命周期的后期才会设置。因此,如果您尝试基于传入的 uri 模板执行一些操作,子类化 GenericFilterBean 可能不是正确的选择,因为您需要首先调用 chain.doFilter 。

 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            //Do something
            chain.doFilter(request, response);
        } finally {
         String pathTemplate = (String) 
request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
         System.out.println("Incoming Request path Template from Generic Filter : " + pathTemplate);
}


}

替代选项:

假设你这里使用的是spring web。 您可以定义一个

HandlerInterceptor
并将其注册到
InterceptorRegistry

@Component
public class LogPathTemplateInterceptor
        implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
        String pathTemplate = (String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        System.out.println("Incoming Request path Template : " + pathTemplate);
        return true;
    }
}

定义完成后,将其注册到InterceptorRegistry。看看路径模式是如何添加到拦截器中的。

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private LogPathTemplateInterceptor logInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(logInterceptor)
                .addPathPatterns("/variables/**");
    }
}

这应该记录请求模板路径而不是带有变量的路径。

© www.soinside.com 2019 - 2024. All rights reserved.