我想在每个API REST请求中添加一个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 }
Elapsed-Time参数计算为@RequestMapping方法完成的瞬间与@RequestMapping方法启动的瞬间之间的毫秒差异。
我一直在看Spring4 HandlerInterceptorAdapter。 preHandle和postHandle方法似乎完全适合这种情况(尽管执行链中每个拦截器的时间开销)。但是,不幸的是,在postHandle方法中更改响应头没有效果,因为响应已经构建。
请注意,HandlerInterceptor的postHandle方法并不总是非常适合与@ResponseBody和ResponseEntity方法一起使用。在这种情况下,HttpMessageConverter在调用postHandle之前写入并提交响应,这使得无法更改响应,例如添加标头。相反,应用程序可以实现ResponseBodyAdvice并将其声明为@ControllerAdvice bean或直接在RequestMappingHandlerAdapter上配置它。
你知道处理这种情况的任何优雅的解决方案吗?
我不认为这种情况会重复Spring - Modifying headers for every request after processing (in postHandle),因为我需要捕获一个值为开始时间的变量(当请求到达应用程序时和@RequestMapping方法开始之前),然后使用@RequestMapping方法完成后,此变量用于计算已用时间。
答案 0 :(得分:3)
你需要继续使用Handle Interceptor,但是不要实现postHandle方法,只有preHandle才能将startTime保存为请求中的参数
let qs = {
requests: 5,
timeout: 1000
};
let prices = [];
let highest = 0;
function xhr(qs){
return $.ajax({
url: url,
dataType: 'json',
timeout: qs.timeout
}).then(data => {
let json = JSON.stringify(data['price']);
prices.push(json);
getHighestPrice(prices);
console.log(highest);
});
}
function makeRequest(qs) {
let p = $.when([]);
for(let i = 0; i < qs.requests; i++) {
p = p.then(() => xhr(qs));
// or p.then(xhr); if you don't need to pass qs on to xhr function (remove qs argument in xhr as well)
}
p.then(() => {
// this is run once all have completed
}).fail(reason => {
// this is run if there's a failure anywhere
});
}
当控制器完成并返回响应时,Controller Advice(实现ResponseBodyAdvice的类)将获取Server Request的http servlet请求部分,恢复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;
}
}
最后,您将拦截器添加到主应用程序(每个资源都需要/ **路径)
@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;
}
}