【ZUUL】拦截器修改请求体后引出的 Content-Length 问题

背景

项目需求,对业务请求要进行拦截,并在请求体添加指定参数以进行请求合法性校验



实现方式和步骤

需求就是这么个需求了,然后就在 zuul 网关引入了一个 Filter,

在filter的 run 方法上,通过

RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();

拿到了 request

然后再通过

    public static JSONObject getPostData(HttpServletRequest request) {
        StringBuilder data = new StringBuilder();
        String line;
        BufferedReader reader;
        try {
            reader = request.getReader();
            while (null != (line = reader.readLine())) {
                data.append(line);
            }
        } catch (IOException e) {
            return null;
        }
        if (data.length() > 0) {
            JSONObject requestBody = JSONObject.parseObject(data.toString());
            return requestBody;
        }
        return new JSONObject();
    }
}

把 request 参数转成一个 json,
再然后往 json 里面加参数,

json 改变后,需要把修改后的请求体再回写到 request, 但因为 request 是不能改写的,所以又引入了: RequestWrapper 来进行对 request 进行重写
大概就是这样子:

request = new BodyRequestWrapper(request, requestBody.toString());
context.setRequest(request);

BodyRequestWrapper

public class BodyRequestWrapper extends HttpServletRequestWrapper {

    private String body;

    public BodyRequestWrapper(HttpServletRequest request, String context) {
        super(request);
        body = context;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes("UTF-8"));
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

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

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

            @Override
            public void setReadListener(ReadListener listener) {

            }
        };
        return servletInputStream;
    }

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

至此第一阶段,就完工了,在本地测试了一下,嗯,还挺好,效果达到了。

问题

问题来了~
在本地测试都挺好的一个代码,一放到测试环境,诶,就不行了,
报错信息,大概就是请求参数解析错误,一个好好的请求参数,硬生生地只截取了一部分,造成 json 解析错误了。

继续定位

嗯。。。大概知道是 Content-Length 的问题了,但是这个 Content-Length 为什么会出问题呢,
一样的请求,之前也没毛病啊?

经过一翻思索,突然想起,不是修改了请求体参数吗!它的长度自然也跟着变化了,
嗯,然后再一查。。。找到了方案,

就是在 BodyRequestWrapper 这个类上,重写 getContentLength 和 getContentLengthLong 这两个方法,把请求体重新计算后再返回就好了。emmm, nice !

    @Override
    public int getContentLength() {
        return body.length();
    }

    @Override
    public long getContentLengthLong() {
        return body.length();
    }

还是报错

修改一个版本后,满怀希望部署上去,一测,。。。。还是有问题! 还是一样的问题,这个长度还是不对!
怎么回事!
不是重写了这个长度的计算了吗!!!

emmm, 当时也是傻了,这里需要返回的根本不能按字符串长度去计算,应该按字节数组长度去返回才行。
于是,改成这样:

    @Override
    public int getContentLength() {
        return body.getBytes().length;
    }

    @Override
    public long getContentLengthLong() {
        return body.getBytes().length;
    }

终于!!!好了啊!!泪目了。

小记一下,千万记得,程序里面计算长度,网络传输说的长度,都是字节流的长度啊。。。

posted @ 2023-04-04 16:47  aaacarrot  阅读(515)  评论(0编辑  收藏  举报