You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中调用不可修改的JSON读取方法两次报错的解决求助

解决HttpServletRequest输入流仅能读取一次的问题

问题背景

现有两个无法修改的方法readAndParseJSON()和readJSON(),当先后传入同一个HttpServletRequest对象调用时,会抛出Exception while parsing JSON string: Unexpected token END OF FILE at position 0错误。原因是HttpServletRequest的输入流只能被读取一次,第二次调用时流已经为空。

相关调用代码

JSONObject jsonObject = (JSONObject) JSONReaderAndParser.readAndParseJSON(req);
if (jsonObject.get("abc") != null) 
  Boolean abc = Boolean.parseBoolean(jsonObject.get("abc").toString());         
fun(JSONReaderAndParser.readJSON(request),abc);

不可修改的目标方法实现

public static Object readAndParseJSON(HttpServletRequest request) throws Exception {
    Object object;

    try {
        JSONParser parser = new JSONParser();
        StringBuilder jsonString = new StringBuilder();
        String data = null;
        data = IOUtils.toString(request.getInputStream(), "UTF-8");
        jsonString.append(data);

        object = parser.parse(jsonString.toString());
    } catch (IOException e) {
        throw new Exception("Server error");
    } catch (ParseException e) {
        throw new Exception("bad request");
    }

    if (object == null) {
        throw new Exception("Invalid json");
    }

    return object;
}

public static String readJSON(HttpServletRequest request) throws Exception {
    StringBuffer jsonString = new StringBuffer();
    String data = null;
    try {
        data = IOUtils.toString(request.getInputStream(), "UTF-8");
        jsonString.append(data);
    } catch (Exception e) {
        throw new Exception();
    }
    if (data == null) {
        throw new Exception("Parsed object is null");
    }

    return jsonString.toString();
}

解决方案

核心思路是通过自定义HttpServletRequestWrapper包装原始请求,提前缓存输入流的内容,让输入流可以被重复读取。具体步骤如下:

1. 实现自定义请求包装类

这个类会把输入流内容读取到字节数组中缓存,后续每次调用getInputStream()时都返回基于缓存数组的新流:

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.InputStreamReader;

public class CachedRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] cachedBody;

    public CachedRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 读取原始输入流并缓存
        cachedBody = IOUtils.toByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        // 返回基于缓存字节数组的新流
        return new CachedServletInputStream(cachedBody);
    }

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

    // 实现自定义的ServletInputStream
    private static class CachedServletInputStream extends ServletInputStream {
        private final ByteArrayInputStream inputStream;

        public CachedServletInputStream(byte[] cachedBody) {
            this.inputStream = new ByteArrayInputStream(cachedBody);
        }

        @Override
        public int read() throws IOException {
            return inputStream.read();
        }

        @Override
        public boolean isFinished() {
            return inputStream.available() == 0;
        }

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

        @Override
        public void setReadListener(javax.servlet.ReadListener readListener) {
            // 按需实现,此处留空不影响核心功能
        }
    }
}

2. 修改调用逻辑,使用包装后的请求

在调用目标方法前,先把原始请求包装成CachedRequestWrapper,后续所有方法调用都使用这个包装对象:

// 先包装原始请求,缓存输入流内容
CachedRequestWrapper cachedReq = new CachedRequestWrapper(req);

// 用包装对象调用第一个方法
JSONObject jsonObject = (JSONObject) JSONReaderAndParser.readAndParseJSON(cachedReq);
Boolean abc = null;
if (jsonObject.get("abc") != null) {
    abc = Boolean.parseBoolean(jsonObject.get("abc").toString());
}

// 继续用同一个包装对象调用第二个方法
fun(JSONReaderAndParser.readJSON(cachedReq), abc);

这样两个方法都能读取到完整的输入流内容,避免了流为空的错误。

内容的提问来源于stack exchange,提问作者mosid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 17:02:09