HandlerInterceptor拦截器 postHandle执行addHeader无效,postHandle执行setStatus无效的解决方案

47 篇文章 0 订阅
26 篇文章 0 订阅

问题描述

想在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***");
    }

    //页面渲染之后调用,一般用于资源清理操作
    @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;
    }
}

验证好用

在这里插入图片描述
补充:执行顺序为 ResponseBodyAdviceHandlerInterceptor

***ResponsePostInterceptor.preHandle***
***Controller.getAllStudents4***
***ResponsePostAdvice.supports***
***ResponsePostAdvice.beforeBodyWrite***
***ResponsePostInterceptor.postHandle***
***ResponsePostInterceptor.afterCompletion***
  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值