2017-09-14 30 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

它的工作原理。然而,這是不準確的! –

相關問題