问题描述
想在postHandle
方法里执行addHeader
方法来补充一些Header
信息(如分页信息),但是最后执行却未如期显示
拦截器源码
import com.zhangziwa.practisesvr.utils.response.ResponseContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class ResponsePostInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
# 想在postHandle方法里执行addHeader方法来补充一些Header信息
HttpHeaders headers = ResponseContext.getHeaders();
if (headers != null) {
headers.forEach((key, values) -> values.forEach((value) -> {
if (!response.getHeaderNames().contains(key)) {
response.addHeader(key, value);
}
}));
}
if (ResponseContext.getResponseCode() != null) {
response.setStatus(ResponseContext.getResponseCode().value());
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ResponseContext.clear();
}
}
Controller 构造初始的HttpHeaders
@RequestMapping(value = "/getAllStudents4", method = RequestMethod.GET)
public ResponseEntity<List<Student>> getAllStudents4() {
System.err.println("***Controller.getAllStudents4***");
List<Student> students = userService.listStudents3(1, 5);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("test", "test");
return ResponseEntity.ok().headers(httpHeaders).contentType(MediaType.APPLICATION_JSON).body(students);
}
postHandle 补充HttpHeaders的代码正常执行
执行结果却没有补充的HttpHeaders
原因分析
[Ref] springboot设置返回的response中的header解决办法
解决方案
方案1:postHandle
追加HttpHeaders
的逻辑前移到preHandle
方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.err.println("***ResponsePostInterceptor.preHandle***");
response.addHeader("test1", "test1");
return true;
}
验证可行
弊端
preHandle
执行时机是在进入Controller层
之前,如果要追加的HttpHeaders
是在Controller层
里计算得到的,则此方案完全用不上
方案2:使用ResponseBodyAdvice
+ @ControllerAdvice
Further Reading : @ControllerAdvice
源码:HandlerInterceptor
去除补充HttpHeaders
逻辑
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class ResponsePostInterceptor implements HandlerInterceptor {
//在Controller执行之前调用,如果返回false,controller不执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.err.println("***ResponsePostInterceptor.preHandle***");
return true;
}
//controller执行之后,且页面渲染之前调用
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.err.println("***ResponsePostInterceptor.postHandle***");
if (response.isCommitted()) {
System.out.println("response is committed");
return;
}
}
//页面渲染之后调用,一般用于资源清理操作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.err.println("***ResponsePostInterceptor.afterCompletion***");
ResponseContext.clear();
}
}
源码:ResponseBodyAdvice
补充HttpHeaders
逻辑
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import static java.util.Objects.nonNull;
@ControllerAdvice
public class ResponsePostAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
System.err.println("***ResponsePostAdvice.supports***");
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class clazz, ServerHttpRequest request, ServerHttpResponse response) {
System.err.println("***ResponsePostAdvice.beforeBodyWrite***");
HttpHeaders headers = response.getHeaders();
// 分页信息添加到ServerHttpResponse
HttpHeaders headersContext = ResponseContext.getHeaders();
// 实现方式1: 一次性梭哈 重复项新值覆盖旧值
// if (nonNull(headersContext) && !headersContext.isEmpty()) {
// headers.addAll(headersContext);
// }
// 实现方式1: 逐个补充 重复项不添加
if (nonNull(headersContext) && !headersContext.isEmpty()) {
headersContext.forEach((key, values) -> values.forEach((value) -> {
headers.addIfAbsent(key, value);
}));
}
// 状态码添加到ServerHttpResponse
if (nonNull(ResponseContext.getResponseCode())) {
response.setStatusCode(ResponseContext.getResponseCode());
}
return body;
}
}
验证好用
补充:执行顺序为 ResponseBodyAdvice
→ HandlerInterceptor
***ResponsePostInterceptor.preHandle***
***Controller.getAllStudents4***
***ResponsePostAdvice.supports***
***ResponsePostAdvice.beforeBodyWrite***
***ResponsePostInterceptor.postHandle***
***ResponsePostInterceptor.afterCompletion***