在自定义HandlerInterceptor中 方法执行之后获取响应体参数

需求描述

通过自定义的拦截器对部分接口的返回添加缓存,请求接口和参数作为redisKey
假设自定义 MyHandlerInterceptor 拦截器对部分接口进行拦截, 我们需要实现HandlerInterceptor 接口并重写 其中的部分方法如下:

@Component
public class MyHandlerInterceptor implements HandlerInterceptor {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
	@Override
	 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		String eid = request.getParameter("eid");
		// 获取对应参数 的缓存
		 String redisKey = getRedisKey(request);
        String s = stringRedisTemplate.opsForValue().get(redisKey);
        if(StringUtils.isNotBlank(s)){
             ResponseData responseData = JSONObject.parseObject(s, ResponseData.class);
             // 直接返回前端
             result(response,responseData);
             return false;
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
		}
		//在afterCompletion 方法中 获取到 方法执行后的响应体,判断如果执行成功则 生成对应key  和 对应结果,存入redis 中
	
	@Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        String eid = request.getParameter("eid");
        String responseBody = ContentCachingWrapperFilter.getResponseBody(response);
        ResponseData responseData = JSONObject.parseObject(responseBody, ResponseData.class);
        if(responseData.getCode() == 1){
            String redisKey = getRedisKey(request);
            stringRedisTemplate.opsForValue().set(redisKey,JSONObject.toJSONString(responseData), 30, TimeUnit.MINUTES);
        }
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
        public String getRedisKey(HttpServletRequest request){
        Map<String, String[]> parameterMap = request.getParameterMap();
        List<String> resultList = new ArrayList<>();
        parameterMap.forEach((s, strings) -> {
            for (String string : strings) {
                if(StringUtils.isNotBlank(string)){
                    resultList.add(string);
                }
            }
        });
        // 参数排序
        List<String> collect = resultList.stream().sorted().collect(Collectors.toList());
        String join = StringUtils.join(collect, "_");
       return request.getRequestURI()+"_"+ join;
    }
 // 直接返回响应体给前端
    public static void result(HttpServletResponse response, ResponseData result) {
        try {
            response.setContentType("text/json");
            response.setCharacterEncoding("UTF-8");
            PrintWriter write = response.getWriter();
            String s = JSONUtil.toJsonStr(result);
            write.write(s);
            write.flush();
            write.close();
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }
}

以上代码关键在于如何在 afterCompletion 方法中获取到相应参数

String responseBody = ContentCachingWrapperFilter.getResponseBody(response);

借助以下工具类

package com.boshiyun.application.intercept;


import org.apache.commons.io.IOUtils;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 缓存请求体和响应体过滤器
 *
 * <p>
 * 由于 requestBody 和 responseBody 分别对应的是 InputStream 和 OutputStream,由于流的特性,读取完之后就无法再被使用了。
 * 所以,需要额外缓存一次流信息。
 * </p>
 *
 * @author Charles7c
 * @since 2022/9/22 16:33
 */
@Component
public class ContentCachingWrapperFilter extends OncePerRequestFilter implements Ordered {

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 10;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        // 包装流,可重复读取
        if (!(request instanceof ContentCachingRequestWrapper)) {
            request = new ContentCachingRequestWrapper(request);
        }
        if (!(response instanceof ContentCachingResponseWrapper)) {
            response = new ContentCachingResponseWrapper(response);
        }

        filterChain.doFilter(request, response);
        updateResponse(response);
    }

    /**
     * 更新响应(不操作这一步,会导致接口响应空白)
     *
     * @param response 响应对象
     * @throws IOException /
     */
    private void updateResponse(HttpServletResponse response) throws IOException {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        Objects.requireNonNull(responseWrapper).copyBodyToResponse();
    }

    /**
     * 获取请求体
     *
     * @param request 请求对象
     * @return 请求体
     */
    public static String getRequestBody(HttpServletRequest request) throws IOException {
        String requestBody = "";
        ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
        if (wrapper != null) {
            requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8.toString());
        }
        return requestBody;
    }

    /**
     * 获取响应体
     *
     * @param response 响应对象
     * @return 响应体
     */
    public static String getResponseBody(HttpServletResponse response) throws IOException {
        String responseBody = "";
        ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8.toString());
        }
        return responseBody;
    }
}


requestBody 和 responseBody 分别对应的是 InputStream 和 OutputStream,由于流的特性,读取完之后就无法再被使用了。以上的逻辑就是起到 流的重复使用

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值