java - Spring Controller : Adding a response header parameter called Elapsed-Time

标签 java spring rest spring-mvc

我想在每个 API REST 请求上添加一个 Elapsed-Time 响应 header 参数,即使是那些以错误结束的请求。

例如:

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

作为 Elapsed-Time 参数计算为 @RequestMapping 方法完成的瞬间与 @RequestMapping 方法开始的瞬间之间的毫秒差。

我一直在看 Spring4 HandlerInterceptorAdapter。 preHandle 和 postHandle 方法似乎非常适合这种情况(尽管执行链中每个拦截器的时间开销)。但是,不幸的是,在 postHandle 方法中更改响应 header 没有任何效果,因为响应已经构建。

Spring documentation状态:

Note that the postHandle method of HandlerInterceptor is not always ideally suited for use with @ResponseBody and ResponseEntity methods. In such cases an HttpMessageConverter writes to and commits the response before postHandle is called which makes it impossible to change the response, for example to add a header. Instead an application can implement ResponseBodyAdvice and either declare it as an @ControllerAdvice bean or configure it directly on RequestMappingHandlerAdapter.

您知道处理这种情况的任何可行的优雅解决方案吗?

我不认为这个案例是重复的Spring - Modifying headers for every request after processing (in 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;
  }
}

当 Controller 完成并返回响应时,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;
  }
}

最后,您将拦截器添加到主应用程序(/** 每个资源所需的路径)

@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("/**");
  }

}

关于java - Spring Controller : Adding a response header parameter called Elapsed-Time,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46227751/

相关文章:

spring - 如何测试使用 Hibernate Search 的服务?

java - 如何保持多线程所需的公共(public)数据

spring - 如何在 `noRollbackFor` 上设置 `TransactionTemplate` ?

java - 使用java计算文本文件中重复单词的数量

java - 如何使用 HSSFFont 以十进制数设置字体

java - Autowiring 子类但使用父类作为引用

java - 未找到 Restful Webservice 公共(public)构造函数

Java 游戏 - 按下按键

java Rest API post方法给出了不允许的HTTP 405方法

javascript - 在 Ember-data 中以自定义方式保存属性