[英]Spring Controllers: Adding a response header parameter called Elapsed-Time
I would like to add an Elapsed-Time response header parameter on every API REST request, even those that finish with error. 我想在每个API REST请求上添加一个经过时间响应标头参数,即使那些以错误结束的参数也是如此。
For instance: 例如:
===>
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 }
Being Elapsed-Time parameter calculated as the difference in milliseconds between the instant that the @RequestMapping method finishes, and the instant that the @RequestMapping method starts. 作为经过时间参数,计算为@RequestMapping方法完成的瞬间与@RequestMapping方法启动的瞬间之间的毫秒差。
I have been taking a look on Spring4 HandlerInterceptorAdapter. 我一直在研究Spring4 HandlerInterceptorAdapter。 preHandle and postHandle methods seem to fit perfectly for this case (despite the time overhead of executing every interceptor in the chain).
preHandle和postHandle方法似乎非常适合这种情况(尽管执行链中每个拦截器的时间开销)。 But, unfortunatelly, changing response header in postHandle method has no effect because the response is already build.
但是,不幸的是,在postHandle方法中更改响应头没有任何作用,因为响应已经生成。
Spring documentation states: Spring文档指出:
Note that the postHandle method of HandlerInterceptor is not always ideally suited for use with @ResponseBody and ResponseEntity methods.
请注意,HandlerInterceptor的postHandle方法并不总是非常适合与@ResponseBody和ResponseEntity方法一起使用。 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.
在这种情况下,HttpMessageConverter会在调用postHandle之前写入并提交响应,这使得无法更改响应,例如添加标头。 Instead an application can implement ResponseBodyAdvice and either declare it as an @ControllerAdvice bean or configure it directly on RequestMappingHandlerAdapter.
相反,应用程序可以实现ResponseBodyAdvice并将其声明为@ControllerAdvice bean或直接在RequestMappingHandlerAdapter上对其进行配置。
Do you know of any working elegant solution to deal with this case? 您是否知道有任何可行的解决方案来处理这种情况?
I don't think think this case is duplicating Spring - Modifying headers for every request after processing (in postHandle) because I need to capture a variable whose value is the start time (when petition gets to the application and before the @RequestMapping method starts), and then use this variable once the @RequestMapping method finishes, to compute the elapsed time. 我不认为这种情况是在复制Spring-处理后(在postHandle中)为每个请求修改标头,因为我需要捕获一个值为开始时间的变量(当请求到达应用程序时,并且@RequestMapping方法开始之前) ),然后在@RequestMapping方法完成后使用此变量来计算经过的时间。
You need to stay with Handle Interceptor, but do not implement the postHandle method, only preHandle in order to save the startTime as a parameter in the request 您需要使用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;
}
}
When the controller finishes and returns a response, a Controller Advice (class that implements ResponseBodyAdvice), will get the http servlet request part of Server Request, recover the startTime and obtain the time elapsed as follows: 当控制器完成并返回响应时,控制器建议(实现ResponseBodyAdvice的类)将获取服务器请求的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;
}
}
Finally you add the interceptor to the main application (/** path as you wanted for every Resource) 最后,将拦截器添加到主应用程序(每个资源所需的/ **路径)
@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("/**");
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.