【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;
}
终于!!!好了啊!!泪目了。
小记一下,千万记得,程序里面计算长度,网络传输说的长度,都是字节流的长度啊。。。