首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 讀取 HTTP 請求 Body 完整代碼示例

     編輯:Java 相關知識

HTTP 請求由 Header 和 Body 組成。Servlet 中通過 HttpServletRequest.getReader() (字符流) 或 getInputStream() (字節流) 讀取 Body。本文提供完整工具方法,含編碼處理、大數據流式讀取、Spring MVC 替代方案與常見坑點。

原生 Servlet 讀取 Body

方法 1:BufferedReader 逐行讀取 (適用文本、JSON、表單)


public static String readBodyAsString(HttpServletRequest request) throws IOException {
    StringBuilder sb = new StringBuilder();
    try (BufferedReader reader = request.getReader()) { // 自動按 request 編碼解碼
        char[] buffer = new char[1024];
        int len;
        while ((len = reader.read(buffer)) != -1) {
            sb.append(buffer, 0, len);
        }
    }
    return sb.toString();
}

注意:

  • getReader() 和 getInputStream() **互斥**,只能調用其中之一,否則拋 IllegalStateException。
  • 默認編碼 ISO-8859-1,需確保客戶端發送 Content-Type: application/json; charset=UTF-8 或在 Filter 中設置 request.setCharacterEncoding("UTF-8")。

方法 2:InputStream 讀取字節 (適用二進制、文件、自定義編碼)


public static byte[] readBodyAsBytes(HttpServletRequest request) throws IOException {
    try (InputStream is = request.getInputStream();
         ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        byte[] buffer = new byte[4096];
        int len;
        while ((len = is.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        return baos.toByteArray();
    }
}

// 轉字符串 (指定編碼)
String body = new String(readBodyAsBytes(request), StandardCharsets.UTF_8);

方法 3:Servlet 3.1+ 非阻塞讀取 (高併發、長連接)


@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncBodyServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        req.startAsync();
        ReadListener listener = new ReadListener() {
            private final StringBuilder sb = new StringBuilder();
            @Override
            public void onDataAvailable() throws IOException {
                try (BufferedReader reader = req.getReader()) {
                    char[] buf = new char[1024];
                    int len;
                    while (req.isReady() && (len = reader.read(buf)) != -1) {
                        sb.append(buf, 0, len);
                    }
                }
            }
            @Override
            public void onAllDataRead() {
                String body = sb.toString();
                // 處理 body
                req.getAsyncContext().complete();
            }
            @Override
            public void onError(Throwable t) {
                log.error("讀取失敗", t);
                req.getAsyncContext().complete();
            }
        };
        req.getInputStream().setReadListener(listener);
    }
}

Spring MVC 簡化讀取

場景註解/參數說明
JSON 直接綁定對象@RequestBody UserDTO dto自動反序列化 (Jackson),最常用
原始 JSON 字符串@RequestBody String json獲取原始報文,用於日誌/轉發
表單數據@RequestParam Map formapplication/x-www-form-urlencoded
文件上傳@RequestPart MultipartFile filemultipart/form-data
原始 InputStreamHttpServletRequest request 參數手動讀取,罕用

常見坑點與解決方案

問題原因解決
getReader() 返回空字串流已被 Filter/Interceptor 讀取使用 ContentCachingRequestWrapper 包裝請求,允許重複讀取
中文亂碼編碼不一致Filter 中 request.setCharacterEncoding("UTF-8");客戶端發送 charset
大文件 OOM一次性讀入內存流式處理、臨時文件、分塊上傳
非阻塞讀取數據不全未正確處理 isReady()/onAllDataRead()嚴格遵循 ReadListener 契約
雙次讀取異常調用了 getReader() 再調 getInputStream()全鏈路統一用其中一種,或用 RequestWrapper

可重複讀取請求包裝器 (生產必備)


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

    public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 讀取並緩存
        try (InputStream is = request.getInputStream();
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            byte[] buf = new byte[1024];
            int len;
            while ((len = is.read(buf)) != -1) baos.write(buf, 0, len);
            cachedBody = baos.toByteArray();
        }
    }

    @Override
    public ServletInputStream getInputStream() {
        return new ServletInputStream() {
            private final ByteArrayInputStream bais = new ByteArrayInputStream(cachedBody);
            @Override public int read() { return bais.read(); }
            @Override public boolean isFinished() { return bais.available() == 0; }
            @Override public boolean isReady() { return true; }
            @Override public void setReadListener(ReadListener l) { throw new UnsupportedOperationException(); }
        };
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(getInputStream(), 
            StandardCharsets.UTF_8));
    }
}

// Filter 中包裝
@WebFilter("/*")
public class RequestCachingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) 
            throws IOException, ServletException {
        if (req instanceof HttpServletRequest) {
            HttpServletRequest httpReq = (HttpServletRequest) req;
            // 僅對有 Body 的請求包裝 (POST/PUT/PATCH)
            if ("POST".equalsIgnoreCase(httpReq.getMethod()) 
                || "PUT".equalsIgnoreCase(httpReq.getMethod())
                || "PATCH".equalsIgnoreCase(httpReq.getMethod())) {
                chain.doFilter(new ContentCachingRequestWrapper(httpReq), resp);
                return;
            }
        }
        chain.doFilter(req, resp);
    }
}

總結

  1. 原生 Servlet 用 request.getReader() 讀文本,getInputStream() 讀二進制,**二選一**。
  2. Spring MVC 優先用 @RequestBody 自動綁定,原始報文用 @RequestBody String。
  3. 攔截器/過濾器需多次讀取 Body 時,**必須**用 ContentCachingRequestWrapper 包裝。
  4. 編碼統一 UTF-8:Filter 設置、客戶端 Header、資料庫連接、JVM 文件編碼。
  5. 大數據場景採用流式處理或非阻塞 IO,避免全量載入內存。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved