1. 简介
在本教程中,我们将研究如何使用Spring MVC记录传入的请求。
2. 示例应用程序
让我们首先看一下我们的示例应用程序。
@RestController
public class ContactController {
@PostMapping("/contact/{name}")
public String contact(@PathVariable("name") String name, @RequestBody String details) {
return "Contact details received for: " + name;
}
}
我们有一个简单的 REST 控制器,其中包含单个端点 -/contact/{name}。在接下来的示例中,我们将针对此终结点发出请求。
3. 自定义实现
现在,我们将探索一些自定义实现来记录传入的 HTTP 请求。
3.1. 使用处理程序拦截器
HandlerInterceptor类为我们提供了在请求处理之前和之后运行的钩子。因此,我们将使用这些方法来记录请求数据:
@Slf4j
@Component
public class CustomRequestLoggingInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
StringBuffer requestURL = request.getRequestURL();
log.info("preHandle => Request URL: {}", requestURL);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
int status = response.getStatus();
log.info("afterCompletion => Response Status: {}", status);
}
}
我们的CustomRequestLoggingInterceptor类扩展了 HandlerInterceptorAdapter,它是一个实现HandlerInterceptor的抽象类。请注意,preHandle将在请求处理之前运行,afterCompletion将在请求处理完成后运行。
现在我们有了HandlerInterceptor,我们必须使用WebMvcConfigurerbean 注册它:
@Component
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomRequestLoggingInterceptor());
}
}
当应用程序收到请求时,我们的CustomRequestLoggingInterceptor类也会被调用:
INFO 27115 --- [nio-8081-exec-1] .j.s.m.r.CustomRequestLoggingInterceptor : preHandle => Request URL: http://localhost:8081/contact/John
INFO 27115 --- [nio-8081-exec-1] .j.s.m.r.CustomRequestLoggingInterceptor : afterCompletion => Response Status: 200
3.2. 使用过滤器
过滤器在 servlet 之前和之后运行,因此它适用于记录请求。在我们的例子中,servlet 是 Spring MVC 的DispatcherServlet,它处理所有传入的请求。
@Slf4j
@Component
public class CustomRequestLoggingFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest currentRequest = (HttpServletRequest) servletRequest;
final HttpServletResponse currentResponse = (HttpServletResponse) servletResponse;
StringBuffer requestURL = currentRequest.getRequestURL();
log.info("Request URL: {}", requestURL);
try {
chain.doFilter(currentRequest, servletResponse);
} finally {
int status = currentResponse.getStatus();
log.info("Response status: {}", status);
}
}
}
在这里,实际的请求处理发生在chain.doFilter调用中。换句话说,DispatcherServlet获取请求并选择适当的控制器方法。因此,我们在过滤器中编写此调用之前和之后的日志语句。
此外,我们可以使用FilterRegistrationBean 配置过滤器的注册属性,尽管我们在这里选择了默认值。
当应用程序为请求提供服务时,将写入以下日志:
INFO 5835 --- [nio-8081-exec-1] c.j.s.m.r.CustomRequestLoggingFilter : Request URL: http://localhost:8081/contact/John
INFO 5835 --- [nio-8081-exec-1] c.j.s.m.r.CustomRequestLoggingFilter : Response status: 200
4. 弹簧 MVC 支持
Spring MVC内置了对日志记录请求的支持,我们接下来将对此进行研究。
4.1. 使用共享资源请求日志记录过滤器
Spring MVC提供了CommonsRequestLoggingFilter,它可以记录请求URL,正文和其他相关信息。
要在应用程序中使用它,我们必须首先为它定义一个 bean:
@Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
CommonsRequestLoggingFilter requestLoggingFilter = new CommonsRequestLoggingFilter();
requestLoggingFilter.setIncludeClientInfo(true);
requestLoggingFilter.setIncludeHeaders(true);
requestLoggingFilter.setIncludeQueryString(true);
requestLoggingFilter.setIncludePayload(true);
return requestLoggingFilter;
}
在这里,我们正在创建一个CommonsRequestLoggingFilter的实例,并为丰富的日志语句启用所有包含选项。
然后,我们将筛选器的日志级别更改为DEBUG。我们可以使用application.properties 来做到这一点:
logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
当新请求到达时,筛选器会提供日志:
DEBUG 27115 --- [nio-8081-exec-1] o.s.w.f.CommonsRequestLoggingFilter : Before request [uri=/contact/John;client=127.0.0.1;headers=[accept:"text/plain, application/json, application/*+json, */*", content-type:"text/plain;charset=UTF-8", user-agent:"Java/1.8.0_191", host:"localhost:8081", connection:"keep-alive", content-length:"15"]]
DEBUG 27115 --- [nio-8081-exec-1] o.s.w.f.CommonsRequestLoggingFilter : After request [uri=/contact/John;client=127.0.0.1;headers=[accept:"text/plain, application/json, application/*+json, */*", content-type:"text/plain;charset=UTF-8", user-agent:"Java/1.8.0_191", host:"localhost:8081", connection:"keep-alive", content-length:"15"];payload=London, England]
4.2. 扩展抽象请求日志记录过滤器
除了使用CommonsRequestLoggingFilter,我们还可以创建自己的过滤器来扩展AbstractRequestLoggingFilter类。事实上,CommonsRequestLoggingFilter也是AbstractRequestLoggingFilter 的实现。
@Component
public class AnotherCustomLoggingFilter extends AbstractRequestLoggingFilter {
@Value("${request.logging.shouldLog}")
private boolean shouldLog;
public AnotherCustomLoggingFilter(){
setIncludeClientInfo(true);
setIncludeHeaders(true);
setIncludePayload(true);
setIncludeQueryString(true);
setBeforeMessagePrefix("Request started => ");
setAfterMessagePrefix("Request ended => ");
}
@Override
protected boolean shouldLog(HttpServletRequest request) {
return shouldLog;
}
@Override
protected void beforeRequest(HttpServletRequest request, String message) {
logger.info(message);
}
@Override
protected void afterRequest(HttpServletRequest request, String message) {
logger.info(message);
}
}
在这里,我们有OtherCustomLoggingFilter它实现了 3 个方法:shouldLog、beforeRequest和afterRequest。
我们使用应用程序属性 -request.logging.shouldLog 启用日志记录行为。在beforeRequest和afterRequest方法中,我们只是记录消息,尽管我们也可以执行更复杂的操作。最后,我们在构造函数中启用包含选项。
日志输出与CommonsRequestLoggingFilter 非常相似:
INFO 5835 --- [nio-8081-exec-1] c.j.s.m.r.AnotherCustomLoggingFilter : Request started => uri=/contact/John;client=127.0.0.1;headers=[accept:"text/plain, application/json, application/*+json, */*", content-type:"text/plain;charset=UTF-8", user-agent:"Java/1.8.0_191", host:"localhost:8081", connection:"keep-alive", content-length:"15"]
INFO 5835 --- [nio-8081-exec-1] c.j.s.m.r.AnotherCustomLoggingFilter : Request ended => uri=/contact/John;client=127.0.0.1;headers=[accept:"text/plain, application/json, application/*+json, */*", content-type:"text/plain;charset=UTF-8", user-agent:"Java/1.8.0_191", host:"localhost:8081", connection:"keep-alive", content-length:"15"];payload=London, England
5. 总结
在本教程中,我们研究了使用 Spring MVC 记录传入请求的不同方法。
在Github 上查看本教程中所有示例的源代码。