What is `HttpServletRequestWrapper` does?

HttpServletRequestWrapperJava Servlet API 中的一个类,作为 HttpServletRequest 接口的包装器(Decorator)实现。

该类设计为装饰者模式(Decorator Pattern)的一部分,允许开发人员通过包装现有的 HttpServletRequest对象来定制或修改请求行为。比如:

过滤或修改请求参数

转换请求体数据

添加或删除请求头信息

实现请求级的安全控制,如防止 XSS(跨站脚本攻击)或 SQL 注入等安全风险

修改请求URI或其他请求属性

样例

java 复制代码
import com.zhangziwa.practisesvr.utils.stream.StreamIUtils;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.text.StringEscapeUtils;

import java.io.*;

public class FilterHttpServletRequest extends HttpServletRequestWrapper {
    private final byte[] body;
    private ByteArrayInputStream byteArrayInputStream;
    private ServletInputStream servletInputStream;
    private BufferedReader bufferedReader;

    public FilterHttpServletRequest(HttpServletRequest request) throws Exception {
        super(request);
        body = StreamIUtils.readStream2Bytes(request.getInputStream());
        byteArrayInputStream = new ByteArrayInputStream(body);
    }

    public String getBody() throws UnsupportedEncodingException {
        String characterEncoding = this.getCharacterEncoding();
        return new String(body, characterEncoding);
    }

    /**
     * 覆盖父类方法,实现获取参数时自动对参数值进行XSS攻击过滤。
     *
     * @param name 参数名
     * @return 如果参数存在,则返回经过HTML转义的、已过滤XSS攻击的参数值;若参数不存在,则返回null
     */
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        if (value != null) {
            // 对请求参数值进行XSS过滤
            return StringEscapeUtils.escapeHtml4(value);
        }
        return null;
    }

    /**
     * 重写父类的getCharacterEncoding方法,获取请求的字符编码。
     * 若当前请求的字符编码未设置或为空,则默认返回"utf-8"作为字符编码。
     *
     * @return 请求的字符编码,若原编码为空则返回"utf-8"
     */
    @Override
    public String getCharacterEncoding() {
        // 调用父类的getCharacterEncoding方法获取字符编码
        String encoding = super.getCharacterEncoding();
        // 如果字符编码为空,则默认为utf-8
        return encoding == null ? "utf-8" : encoding;
    }

    /**
     * 重写父类或接口中的getReader方法,提供一个BufferedReader对象。
     *
     * @return 返回一个根据请求体内容和字符编码方式创建的BufferedReader对象
     */
    @Override
    public BufferedReader getReader() {
        // 如果输入流尚未初始化,则使用请求体内容创建一个新的ByteArrayInputStream
        if (byteArrayInputStream == null) {
            byteArrayInputStream = new ByteArrayInputStream(body);
        }

        // 如果BufferedReader还未创建,则进行以下逻辑:
        if (bufferedReader == null) {
            // 获取当前请求的字符编码方式
            String characterEncoding = getCharacterEncoding();

            try {
                // 使用获取到的字符编码方式以及已有的ByteArrayInputStream创建一个InputStreamReader对象
                // 并在此基础上封装一个BufferedReader对象以提高读取效率
                bufferedReader = new BufferedReader(new InputStreamReader(byteArrayInputStream, characterEncoding));
            } catch (UnsupportedEncodingException e) {
                // 若遇到不支持的字符编码异常,捕获并抛出一个包含详细错误信息的RuntimeException
                throw new RuntimeException("Unsupported encoding: " + characterEncoding, e);
            }
        }

        // 返回已经创建好的BufferedReader对象
        return bufferedReader;
    }

    /**
     * 重写父类的 getInputStream 方法,提供一个自定义的 ServletInputStream 实例,
     * 该实例从内部的 byteArrayInputStream 中读取数据,并支持监听器模式。
     *
     * @return 自定义的 ServletInputStream 实例,用于读取请求体数据
     */
    @Override
    public ServletInputStream getInputStream() {
        // 确保 servletInputStream 的初始化在多线程环境下是安全的
        if (servletInputStream == null) {
            synchronized (this) {
                // 创建并初始化一个 ServletInputStream 子类实例
                servletInputStream = new ServletInputStream() {
                    /**
                     * 从内部的 byteArrayInputStream 中读取下一个字节数据
                     *
                     * @return 下一个可读字节,如果已到达流末尾则返回 -1
                     * @throws IOException 如果发生输入/输出错误
                     */
                    @Override
                    public int read() throws IOException {
                        return byteArrayInputStream.read();
                    }

                    /**
                     * 判断是否已经读取完所有数据,即 byteArrayInputStream 是否还有可用数据
                     *
                     * @return 如果没有更多数据可供读取,则返回 true;否则返回 false
                     */
                    public boolean isFinished() {
                        return byteArrayInputStream.available() == 0;
                    }

                    /**
                     * 指示此输入流是否准备好进行读取操作
                     *
                     * @return 始终返回 true,表示此输入流始终处于就绪状态
                     */
                    public boolean isReady() {
                        return true;
                    }

                    /**
                     * 设置 ReadListener 监听器,用于异步读取数据。此处未实现具体逻辑。
                     *
                     * @param readListener 用于处理数据读取事件的 ReadListener 实例
                     */
                    @Override
                    public void setReadListener(ReadListener readListener) {
                        // 留给子类实现
                    }
                };
            }
        }
        // 返回已初始化的 servletInputStream
        return this.servletInputStream;
    }
}
相关推荐
亲爱的非洲野猪21 分钟前
Kafka消息积压的多维度解决方案:超越简单扩容的完整策略
java·分布式·中间件·kafka
wfsm23 分钟前
spring事件使用
java·后端·spring
微风粼粼41 分钟前
程序员在线接单
java·jvm·后端·python·eclipse·tomcat·dubbo
缘来是庄1 小时前
设计模式之中介者模式
java·设计模式·中介者模式
rebel1 小时前
若依框架整合 CXF 实现 WebService 改造流程(后端)
java·后端
代码的余温2 小时前
5种高效解决Maven依赖冲突的方法
java·maven
慕y2742 小时前
Java学习第十六部分——JUnit框架
java·开发语言·学习
paishishaba3 小时前
Maven
java·maven
张人玉3 小时前
C# 常量与变量
java·算法·c#
Java技术小馆3 小时前
GitDiagram如何让你的GitHub项目可视化
java·后端·面试