假设我已经定义了这些 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。使用上面的例子:
基本上我需要的是获取过滤器的 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
过滤器在请求到达 DispatcherServlet 之前拦截请求,这使得它们非常适合粗粒度任务,例如:
注意* 在尝试调用 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
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);
}
}
单例范围并设置为预实例化(默认)的 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);
}
根据提出的问题更新答案以使用
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/**");
}
}
这应该记录请求模板路径而不是带有变量的路径。