2017-09-14 31 views
0

我想在每个API REST请求上添加一个Elapsed-Time响应头参数,即使那些错误完成了。Spring控制器:添加一个名为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 } 

正在经历时间参数计算为瞬间的@RequestMapping方法完成,并且该@RequestMapping方法启动的瞬间之​​间毫秒的差异。

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

Spring documentation状态:

注意的HandlerInterceptor的的postHandle方法并不总是非常适用于和@ResponseBody方法ResponseEntity使用。在这种情况下,HttpMessageConverter会在调用postHandle之前写入并提交响应,从而无法更改响应,例如添加标题。相反,应用程序可以实现ResponseBodyAdvice,并将其声明为@ControllerAdvice bean或直接在RequestMappingHandlerAdapter上进行配置。

您是否知道处理这种情况的工作优雅解决方案?

我不认为这种情况是重复Spring - Modifying headers for every request after processing (in postHandle),因为我需要捕获一个变量,其值是开始时间(当请求到达应用程序之前和@RequestMapping方法启动之前),然后使用此变量一次@RequestMapping方法结束,以计算已用时间。

+0

的可能的复制[春天 - 修改为每个请求标题(的postHandle)处理后(https://stackoverflow.com/questions/30702970/弹簧修改头文件为每个请求后处理后处理) – jhyot

回答

2

你需要留在手柄拦截,但没有实现的postHandle方法,只有preHandle为了将开始时间保存为参数,在请求

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

当控制器完成并返回的响应,一个控制器咨询(类实现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; 
    } 
} 

最后你拦截添加到主APPLICAT离子(/ **路径,你想为每一个资源)

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

} 
+1

谢谢,它的工作原理!我想指出,也许pathPattern应该是“/ **”,以便在每个资源上添加拦截器。 –

+0

你是对的Jordi。我着手编辑答案。对于将来的读者,这个链接可以帮助理解URL模式中的/ *和/ **之间的区别:https://stackoverflow.com/questions/12569308/spring-difference-of-and-with-regards-to-paths – avilbol

+0

它的工作原理。然而,这是不准确的! –

相关问题