springboot使用拦截器拦截接口的请求body, 修改body

新建拦截器: WebFilter

目的:拦截所有请求过滤器,并将请求类型是HttpServletRequest类型的请求替换为自定义{@link com.*.biz.service.common.interceptor.RequestWrapper

import com.biz.service.common.interceptor.RequestWrapper;
import org.springframework.core.annotation.Order;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @Description: 拦截所有请求过滤器,并将请求类型是HttpServletRequest类型的请求替换为自定义{@link com.biz.service.common.interceptor.RequestWrapper}
 */
@Order(-1)
@javax.servlet.annotation.WebFilter(filterName = "webFilter", urlPatterns = "/*")
public class WebFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {
        ServletRequest request = null;
        if (servletRequest instanceof HttpServletRequest) {
            request = new RequestWrapper((HttpServletRequest) servletRequest);
        }
        if (request == null) {
            chain.doFilter(servletRequest, servletResponse);
        } else {
            chain.doFilter(request, servletResponse);
        }
    }

    @Override
    public void destroy() {
    }
}
拦截器配置
import com.biz.service.common.filter.WebFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Collections;

@Configuration
public class FilterConfig {


    @Bean
    FilterRegistrationBean<WebFilter> myFilterFilterRegistrationBean() {
        FilterRegistrationBean<WebFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new WebFilter());
        bean.setOrder(-1);
        bean.setUrlPatterns(Collections.singletonList("/*"));
        return bean;
    }
}

新建 RequestWrapper

目的: getInputStream获取流 然后在流中获取数据 但是这个方法只能获取一次;重写httpservletrequestwrapper把request保存下来.用过滤器把保存的request填进去 就可以多次读取了

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * getInputStream获取流 然后在流中获取数据 但是这个方法只能获取一次
 * 重写httpservletrequestwrapper把request保存下来
 * 用过滤器把保存的request填进去 就可以多次读取了
 */
public class RequestWrapper extends HttpServletRequestWrapper {

    private String body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }
        body = stringBuilder.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            public boolean isFinished() {
                return false;
            }
            public boolean isReady() {
                return false;
            }
            public void setReadListener(ReadListener readListener) {
            }
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;

    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
    public String getBody() {
        return this.body;
    }
    // 赋值给body字段
    public void setBody(String body) {
        this.body = body;
    }
}

创建过滤器,获取请求的body,处理后,set回请求中;

import cn.hutool.core.util.StrUtil;
import com.biz.api.common.annotations.I18nTimeHandle;
import com.biz.service.utils.DateZoneUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

@Slf4j
public class DateHandlerInterceptor implements HandlerInterceptor {

    //在请求处理之前进行调用(Controller方法调用之前)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        I18nTimeHandle annotation = handlerMethod.getMethod().getAnnotation(I18nTimeHandle.class);
        if (annotation == null) {
            return true;
        }
        /**
         * getInputStream获取流 然后在流中获取数据 但是这个方法只能获取一次,重写HttpServletRequestWrapper把request保存下来
         * 用过滤器把保存的request填进去 就可以多次读取了
         */

        //如果方法上存在 I18nTimeHandle 注解,则进行时间时区处理
        if (request instanceof RequestWrapper) {
            RequestWrapper requestWrapper = (RequestWrapper) request;
            String body = requestWrapper.getBody();
            log.info("处理前的请求体:{}", body);
            //获取请求json中时间字符串
            List<String> jsonStrDateStr = DateZoneUtil.getJsonStrDateStr(body);
            for (String originDate : jsonStrDateStr) {
                //将前端传入的时间 转换成 东八区的时间
                String targetDate = DateZoneUtil.converDateGMT(originDate, "+8", "+6");
                body = StrUtil.replace(body, originDate, targetDate);
            }
            log.info("处理后的请求体:{}", body);
            //将处理后的body放回
            requestWrapper.setBody(body);
        }
        return true;
    }

    //请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    //在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

过滤器配置

import com.biz.service.common.interceptor.DateHandlerInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebAppConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 可添加多个,这里选择拦截所有请求地址,进入后判断是否有加注解即可
        registry.addInterceptor(new DateHandlerInterceptor()).addPathPatterns("/**");
    }
}

工具类代码


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.biz.service.common.exception.BizException;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

public class DateZoneUtil {

    private static String GMT_8 = "GMT+8";

    /**
     * 转换时间时区
     *
     * @param dateStr        需要转的时间字符串
     * @param sourceTimeZone 源时间时区 +8
     * @param targetTimeZone 目标时间时区 +6
     * @return
     * @throws ParseException
     */
    public static String converDateGMT(String dateStr, String sourceTimeZone, String targetTimeZone) {
        sourceTimeZone = StrUtil.isEmpty(sourceTimeZone) ? GMT_8 : converTimeZone(sourceTimeZone);
        targetTimeZone = StrUtil.isEmpty(targetTimeZone) ? GMT_8 : converTimeZone(targetTimeZone);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //获取传入的时间值
        Long time = 0L;
        try {
            time = new Date(sdf.parse(dateStr).getTime()).getTime();
        } catch (ParseException pe) {
            throw new BizException("时间格式化异常");
        }
        //获取源时区时间相对的GMT时间
        Long sourceRelativelyGMT = time - TimeZone.getTimeZone(sourceTimeZone).getRawOffset();
        //GMT时间+目标时间时区的偏移量获取目标时间
        Long targetTime = sourceRelativelyGMT + TimeZone.getTimeZone(targetTimeZone).getRawOffset();
        Date date = new Date(targetTime);
        return cn.hutool.core.date.DateUtil.formatDateTime(date);
    }

    /**
     * timeZone转换 +8->GMT+8
     *
     * @param timeZone
     * @return
     */
    private static String converTimeZone(String timeZone) {
        if (StrUtil.isEmpty(timeZone)) {
            return GMT_8;
        }
        return "GMT" + timeZone;
    }

    /**
     * 从jsonStr中获取时间字符串(只有符合格式的才会被截取 yyyy-MM-dd HH:mm:ss )
     *
     * @param jsonStr
     * @return
     */
    public static List<String> getJsonStrDateStr(String jsonStr) {
        //递归获取jsonObject中的时间字符串
        return getJsonObjectDateStr(JSONObject.parseObject(jsonStr));
    }

    /**
     * 递归获取jsonObject中的时间字符串
     *
     * @param jsonObject
     * @return
     */
    private static List<String> getJsonObjectDateStr(JSONObject jsonObject) {
        List<String> resultList = Lists.newArrayList();
        jsonObject.entrySet().forEach(v -> {
            if (v.getValue() != null && v.getValue().getClass().equals(String.class) && isValidDate(String.valueOf(v.getValue()))) {
                resultList.add(/*v.getKey() + "---" +*/ String.valueOf(v.getValue()));
            } else if (v.getValue() != null && v.getValue().getClass().equals(JSONObject.class)) {
                JSONObject jsonObject1 = (JSONObject) v.getValue();
                resultList.addAll(getJsonObjectDateStr(jsonObject1));
            } else if (v.getValue() != null && v.getValue().getClass().equals(JSONArray.class)) {
                JSONArray jsonArray = (JSONArray) v.getValue();
                for (Object o : jsonArray) {
                    JSONObject jsonObject2 = (JSONObject) o;
                    resultList.addAll(getJsonObjectDateStr(jsonObject2));
                }
            }
        });

        return resultList;
    }


    public static Boolean isValidDate(String s) {
        if (s.length() != 19) {
            return Boolean.FALSE;
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            dateFormat.parse(s);
            return Boolean.TRUE;
        } catch (Exception e) {
            return Boolean.FALSE;
        }
    }
}

<think> 首先,用户的问题是:“我想查找在Spring Boot使用拦截器提取请求指定内容的方法”。用户要求基于提供的引用内容回答问题。 回顾提供的引用: - 引用[1]:提到在Spring Boot中创建拦截器。 - 引用[2]:说明如何将自定义拦截器添加到拦截器链中,提到拦截静态资源请求。 - 引用[3]:比较过滤器和拦截器,解释拦截器的功能:在请求被响应前、响应后、视图渲染前和请求结束后执行。不能修改request内容,但可以抛出异常或返回false来暂停请求。 - 引用[4]:给出实现自定义拦截器的步骤:实现HandlerInterceptor接口,创建配置类重写addInterceptors方法,添加拦截器到链中。 用户的问题焦点是“提取请求指定内容的方法”。意思是,从HTTP请求中提取特定的数据,例如查询参数、头部信息、请求体等。 基于引用[3],它明确指出:“我们不能通过拦截器修改request内容,但是可以通过抛出异常(或者返回false)来暂停request的执行。” 这表明拦截器不能修改请求内容,但可以读取请求内容。 所以,在拦截器中提取请求指定内容的方法是可行的,因为拦截器可以访问请求对象。 现在,我需要结构化回答。系统指令要求: - 使用中文回答。 - 结构清晰,逐步解决问题。 - 行内数学表达式用$...$(但这里可能不涉及数学)。 - 独立公式用$$...$$(同样,可能不适用)。 - LaTeX语法正确(可能需要用于代码)。 - 生成相关问题。 - 引用末尾自然地添加引用标识,如[^1]。 回答结构: 1. **解释拦截器的作用**:简要回顾拦截器的作用,基于引用。 2. **提取请求内容的方法**:具体说明如何在拦截器中提取请求内容。 3. **示例代码**:提供代码示例来演示。 4. **注意事项**:基于引用[3],强调不能修改请求内容。 5. **相关问题**:生成相关问题。 关键点: - 在HandlerInterceptor中,preHandle方法可以访问HttpServletRequest对象,从中提取内容。 - 示例:提取查询参数、头部、请求体等。 步骤: 1. 创建自定义拦截器实现HandlerInterceptor。 2. 重写preHandle方法(或postHandle、afterCompletion)来访问请求。 3. 在preHandle中,使用request.getParameter、request.getHeader等提取内容。 4. 添加到拦截器链中。 代码示例使用Java,因为这是Spring Boot。 相关问题生成: 1. 如何在拦截器修改请求内容?(但引用说不能修改,所以可能不合适) 2. 拦截器与过滤器的区别是什么? 3. 如何只拦截特定路径? 4. 提取请求内容的常见用例。 确保回答真实可靠:基于提供的引用和一般知识。 最终回答大纲: - 引言:解释拦截器提取请求内容的方法。 - 实现步骤: - 创建自定义拦截器。 - 使用HttpServletRequest提取内容。 - 代码示例。 - 注意事项。 - 相关问题。 </think> ### 在Spring Boot使用拦截器提取请求指定内容的方法 在Spring Boot中,拦截器(Interceptor)是一种强大的机制,用于在请求处理的不同阶段(如请求到达控制器前、响应发送前等)进行自定义操作。基于您提供的引用内容,我将逐步解释如何使用拦截器从HTTP请求中提取指定的内容(如查询参数、请求头、请求体等)。关键点是:**拦截器可以读取请求内容,但不能修改它**[^3]。以下是详细的方法和步骤,确保内容真实可靠,基于引用结合Spring Boot最佳实践。 #### 步骤1: 理解拦截器的作用和限制 - **作用**:拦截器通过实现`HandlerInterceptor`接口,允许您在以下时机执行操作: - `preHandle`:在请求被控制器处理前执行(适合提取内容)。 - `postHandle`:在控制器处理完请求后、视图渲染前执行。 - `afterCompletion`:在请求完全结束后执行。 - **限制**:根据引用[^3],拦截器不能修改请求内容(如改变查询参数或请求体),但可以读取和分析请求数据。这不同于过滤器(Filter),过滤器属于Servlet API,能修改请求,但拦截器提供更精细的控制。 - **适用场景**:提取请求指定内容常用于日志记录、权限验证、数据校验等。例如,从请求头提取Token,或从查询参数提取用户ID。 #### 步骤2: 实现自定义拦截器提取请求内容 要提取请求指定内容,您需要创建一个自定义拦截器在`preHandle`方法中访问`HttpServletRequest`对象。以下是完整实现步骤,基于引用[^4]的框架。 1. **创建自定义拦截器类**: - 实现`HandlerInterceptor`接口,重写`preHandle`方法。 - 在`preHandle`中,使用`HttpServletRequest`的方法提取内容: - `request.getParameter("key")`:提取查询参数(如URL中的`?name=value`)。 - `request.getHeader("Header-Name")`:提取请求头内容。 - `request.getAttribute("attribute")`:提取请求属性(需先设置)。 - 注意:提取请求体Body)如JSON数据时,需要使用`InputStream`或第三方库(如Jackson),但不能直接修改内容[^3]。 - 示例代码: ```java import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; public class ContentExtractorInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 提取查询参数:例如,从URL提取 name 参数 String name = request.getParameter("name"); System.out.println("提取查询参数 name: " + name); // 实际中可记录日志或存储 // 提取请求头:例如,提取 Authorization 头 String authHeader = request.getHeader("Authorization"); System.out.println("提取请求头 Authorization: " + authHeader); // 提取请求体Body)内容:例如,JSON数据(注意:请求体只能读取一次) // 使用 InputStream 读取,避免修改内容 ServletInputStream inputStream = request.getInputStream(); // 实际中,可使用 ObjectMapper 等解析JSON // 示例:读取为字符串(简化版) String body = new String(inputStream.readAllBytes()); System.out.println("提取请求体: " + body); // 重要:返回true继续请求,返回false或抛出异常可中断请求[^3] return true; } // 可选:重写 postHandle 或 afterCompletion 方法进行后处理 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 可在此处提取响应内容(如果需要) } } ``` - **注意事项**: - 请求体Body)需要使用`InputStream`读取,但要注意请求体只能读取一次(防止冲突)。建议在`preHandle`中使用避免修改。 - 如果提取内容用于验证(如Token),可在`preHandle`中检查返回`false`中断请求[^3]。 - 确保代码无误:这个示例基于标准Spring MVC语法,在Spring Boot中可直接使用。 2. **创建配置类注册拦截器**: - 基于引用[^4],创建一个配置类继承`WebMvcConfigurer`(或实现`WebMvcConfigurer`接口),重写`addInterceptors`方法,将自定义拦截器添加到拦截器链中。 - 您可以指定拦截路径(如只拦截`/api/**`),避免不必要的拦截(引用[^2]提到,默认可能拦截所有请求,包括静态资源)。 - 示例代码: ```java import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { // 实例化自定义拦截器添加到链中 registry.addInterceptor(new ContentExtractorInterceptor()) .addPathPatterns("/api/**") // 指定拦截路径,例如所有/api开头的请求 .excludePathPatterns("/api/public/**"); // 排除特定路径,如公开API } } ``` - **解释**:此配置确保拦截器只作用于指定路径,提高效率(引用[^2]强调拦截器链的管理)。 #### 注意事项和最佳实践 - **性能考虑**:频繁提取请求体可能影响性能(因为请求体读取是I/O操作)。建议只在必要时提取,使用缓存或异步处理优化。 - **安全性和错误处理**:提取内容时,检查null值或无效输入(如`request.getParameter`可能返回null)。如果提取失败,可通过返回`false`或抛出异常中断请求[^3]。 - **与过滤器的区别**:如果需要修改请求内容(如添加请求参数),应使用过滤器(Filter)。拦截器更适合只读操作(引用[^3]对比了过滤器和拦截器)。 - **测试建议**:使用Postman或curl发送测试请求,验证拦截器是否正确提取内容。 #### 总结 在Spring Boot中,使用拦截器提取请求指定内容的核心是通过自定义`HandlerInterceptor`实现,在`preHandle`方法中调用`HttpServletRequest`的相关方法(如`getParameter`或`getHeader`)来实现。方法简单高效,但不能修改请求内容。实际应用中,常用于提取登录信息、请求日志等场景[^1][^3]。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值