Jor*_*ñol 2 java rest spring spring-mvc
我想在每个 API REST 请求上添加一个 Elapsed-Time 响应标头参数,即使是那些以错误结束的请求。
例如:
===>
GET /api/customer/123 HTTP/1.1
Accept: application/vnd.company.app.customer-v1+json
<===
HTTP/1.1 200 OK
Content-Type: application/vnd.company.app.customer-v1+json
Elapsed-Time: 12
{ id: 123, name: Jordi, age: 28 }
Run Code Online (Sandbox Code Playgroud)
作为 Elapsed-Time 参数计算为@RequestMapping 方法完成的瞬间与@RequestMapping 方法开始的瞬间之间的毫秒差。
我一直在看 Spring4 HandlerInterceptorAdapter。preHandle 和 postHandle 方法似乎非常适合这种情况(尽管执行链中的每个拦截器都有时间开销)。但是,不幸的是,更改 postHandle 方法中的响应标头不起作用,因为响应已经构建。
Spring 文档指出:
请注意,HandlerInterceptor 的 postHandle 方法并不总是非常适合与 @ResponseBody 和 ResponseEntity 方法一起使用。在这种情况下,HttpMessageConverter 在调用 postHandle 之前写入并提交响应,这使得无法更改响应,例如添加标头。相反,应用程序可以实现 ResponseBodyAdvice 并将其声明为 @ControllerAdvice bean 或直接在 RequestMappingHandlerAdapter 上配置它。
您知道处理这种情况的任何有效的优雅解决方案吗?
我不认为这种情况是在复制Spring - 在处理后修改每个请求的标头(在 postHandle 中),因为我需要捕获一个变量,其值为开始时间(当请愿到达应用程序时并且在 @RequestMapping 方法开始之前) ),然后在@RequestMapping 方法完成后使用此变量来计算经过的时间。
需要保留 Handle Interceptor,但不实现 postHandle 方法,只实现 preHandle 以便将 startTime 作为参数保存在请求中
public class ExecuterTimeInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
当控制器完成并返回响应时,Controller Advice(实现 ResponseBodyAdvice 的类)将获取 Server Request 的 http servlet 请求部分,恢复 startTime 并获取经过的时间,如下所示:
@ControllerAdvice
public class GeneralControllerAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
ServletServerHttpRequest servletServerRequest = (ServletServerHttpRequest) request;
long startTime = (long) servletServerRequest.getServletRequest().getAttribute("startTime");
long timeElapsed = System.currentTimeMillis() - startTime;
response.getHeaders().add("Elapsed-Time", String.valueOf(timeElapsed));
return body;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,您将拦截器添加到主应用程序中(每个资源都需要 /** 路径)
@SpringBootApplication
@ComponentScan(basePackages = "com.your.package")
@Configuration
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new ExecuterTimeInterceptor()).addPathPatterns("/**");
}
}
Run Code Online (Sandbox Code Playgroud)