简体   繁体   中英

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.

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.

I have been taking a look on Spring4 HandlerInterceptorAdapter. preHandle and postHandle methods seem to fit perfectly for this case (despite the time overhead of executing every interceptor in the chain). But, unfortunatelly, changing response header in postHandle method has no effect because the response is already build.

Spring documentation states:

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.

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.

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

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:

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM