栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【JsonCachingRequestWrapper】改造ContentCachingRequestWrapper支持JSON

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【JsonCachingRequestWrapper】改造ContentCachingRequestWrapper支持JSON

JsonCachingRequestWrapper
  • 背景
  • 实现 JsonCachingRequestWrapper
    • 实现思路
    • 如何使用
  • 写在最后

背景

因为Java的inputstream只能读一次,所以ServletRequest中的inputstream一旦中途被读过一次,controller层就会报错(因为再读inputstream时发现为空)

而spring提供了一个ContentCachingRequestWrapper,但是只支持application/x-www-form-urlencoded类型的请求,对于JSON是不支持的。并且它不支持多次读inputstream,只是把读完之后的内容cache了起来,然后我们可以通过getContentAsByteArray()方法获取cache住的内容。

实现 JsonCachingRequestWrapper 实现思路

实现思路很简单,将inputstream读入一个byte[],后续每次需要使用inputstream时,通过byte[]实例化一个新的inputstream。当然这里还有一个知识点,servlet提供了一个HttpServletRequestWrapper基础类,让我们可以通过继承这个类来改造request的行为。下面是完整的代码:

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;


public class JsonCachingRequestWrapper extends HttpServletRequestWrapper {
    private static final String CONTENT_TYPE_JSON = "application/json";

    private byte[] cache;

    public JsonCachingRequestWrapper(HttpServletRequest request) {
        super(request);
        int contentLength = request.getContentLength();
        writeRequestParametersToCachedContent(contentLength);
    }

    public static boolean isSupport(ServletRequest request) {
        String contentType = request.getContentType();
        return CONTENT_TYPE_JSON.equals(contentType);
    }


    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new BodyInputStream(this.cache);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }


    private void writeRequestParametersToCachedContent(int contentLength) {
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(contentLength);
            byte[] buffer = new byte[256];
            ServletInputStream inputStream = super.getInputStream();
            while (!inputStream.isFinished()) {
                int nums = inputStream.read(buffer);
                outputStream.write(buffer, 0, nums);
            }
            this.cache = outputStream.toByteArray();
        } catch (IOException ex) {
            throw new IllegalStateException("Failed to write request parameters to cached content", ex);
        }
    }

    public byte[] getContentAsByteArray() {
        return this.cache;
    }


    private static class BodyInputStream extends ServletInputStream {

        private final InputStream delegate;
        private boolean isFinished = false;

        public BodyInputStream(byte[] body) {
            this.delegate = new ByteArrayInputStream(body);
        }

        @Override
        public boolean isFinished() {
            return isFinished;
        }

        @Override
        public boolean isReady() {
            return true;
        }

        @Override
        public void setReadListener(ReadListener readListener) {
            throw new UnsupportedOperationException();
        }

        @Override
        public int read() throws IOException {
            int read = this.delegate.read();
            if (read == -1) {
                isFinished = true;
            }
            return read;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            int read = this.delegate.read(b, off, len);
            if (read == -1) {
                isFinished = true;
            }
            return read;
        }

        @Override
        public int read(byte[] b) throws IOException {
            int read = this.delegate.read(b);
            if (read == -1) {
                isFinished = true;
            }
            return read;
        }

        @Override
        public long skip(long n) throws IOException {
            return this.delegate.skip(n);
        }

        @Override
        public int available() throws IOException {
            return this.delegate.available();
        }

        @Override
        public void close() throws IOException {
            this.delegate.close();
        }

        @Override
        public synchronized void mark(int readlimit) {
            this.delegate.mark(readlimit);
        }

        @Override
        public synchronized void reset() throws IOException {
            this.delegate.reset();
        }

        @Override
        public boolean markSupported() {
            return this.delegate.markSupported();
        }
    }
}
如何使用

细心的朋友会发现上面有一个方法JsonCachingRequestWrapper.isSupport(ServletRequest request).这个方法可以用来判断是否是JSON类型的请求,如果是表单类型或者是上传文件,显然用JsonCachingRequestWrapper是不合适的。

  • 初始化,通常我们可以这么初始化(一般放在filter中):
if (JsonCachingRequestWrapper.isSupport(servletRequest)) {
    servletRequest = new JsonCachingRequestWrapper((HttpServletRequest) servletRequest);
}
  • 使用(这里以打印日志为例)
if (JsonCachingRequestWrapper.isSupport(request)) {
    JsonCachingRequestWrapper requestWrapper = (JsonCachingRequestWrapper) request;
    String reqParams = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
    log.info("Req-log: {} {}", requestWrapper.getRequestURI(), reqParams);
}
写在最后

JavaIO的知识点还是挺多的,一开始学也比较抽象,要学的好还得懂一点操作系统,值得大家找资料仔细学习下。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1007284.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号