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

Chrome 預載機制導致請求重複發送與 ClientAbortException 解決方案

     編輯:Java 綜合教學

Chrome 預載機制導致請求重複發送與 ClientAbortException 解決方案

問題現象

在 Chrome 瀏覽器中點擊「列印 PDF」按鈕,後端 Servlet 執行了兩次:

  1. 第一次正常生成 PDF,Document.close() 成功完成。
  2. 緊接著 Servlet 再次被觸發,第二次嘗試輸出 PDF 時拋出異常:
org.apache.catalina.connector.ClientAbortException: java.net.SocketException: Software caused connection abort: socket write error

根因:瀏覽器在第一次請求後已關閉連接,第二次請求嘗試向已斷開的連接寫入響應導致。

僅 Chrome/Safari 等 WebKit 內核瀏覽器出現,Firefox/IE 正常。

根因分析:預提取資源機制

Chrome 為加速頁面載入,採用 預提取 (Prefetching/Prerendering) 機制:

  • 當用戶在網址列輸入 URL 或滑鼠懸停於連結時,瀏覽器預先發送請求載入資源。
  • 若預請求返回的是檔案下載響應 (Content-Disposition: attachment),瀏覽器判斷無法預載,主動斷開連接,導致伺服器端出現 ClientAbortException。
  • 用戶實際按下 Enter 或點擊時,瀏覽器發起正式請求,此為第二次請求。

參考資料:Chrome 等瀏覽器的預提取資源機制導致的一個請求發送兩次的問題

影響範圍

  • 檔案下載、PDF 列印、報表匯出等返回 application/pdf、application/octet-stream 等非文本響應的介面。
  • 統計計數介面:Chrome 下每次請求被計數兩次,導致數據偏差。

解決方案

方案一:檢測並忽略預載請求 (推薦)

Chrome 預載請求會帶有特定 Header:Purpose: prefetch 或 X-Moz: prefetch。在 Servlet/Filter 中攔截:

@WebFilter("/*")
public class PrefetchFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        String purpose = request.getHeader("Purpose");
        String xMoz = request.getHeader("X-Moz");
        
        if ("prefetch".equalsIgnoreCase(purpose) || "prefetch".equalsIgnoreCase(xMoz)) {
            // 預載請求直接返回 204 No Content,不執行業務邏輯
            ((HttpServletResponse) res).setStatus(HttpServletResponse.SC_NO_CONTENT);
            return;
        }
        chain.doFilter(req, res);
    }
}

方案二:使用 POST 而非 GET

預載機制通常僅對 GET 請求生效。將下載/列印操作改為表單 POST 提交或 AJAX POST 可避免預載。但需注意 RESTful 風格與冪等性設計。

方案三:響應頭控制 (效果有限)

在響應中添加:

response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);

此法無法完全阻止預載請求發送,僅能防止緩存,不建議作為唯一手段。

方案四:幂等性設計 + 狀態檢查

若業務允許,使下載介面具備冪等性:首次生成 PDF 存入暫存/快取,後續請求直接返回快取內容,避免重複生成與連接異常。

驗證方法

  1. Chrome 開啟開發者工具 Network 面板,勾選 "Preserve log"。
  2. 2. 觸發下載操作,觀察是否出現兩筆相同 URL 請求,第一筆狀態為 (canceled) 或有 Purpose: prefetch Header。 3. 部署 Filter 後重新測試,確認只剩一筆正式請求。

總結

Chrome 預載機制是為提升用戶體驗,但對非冪等的下載類介面造成干擾。最穩健的做法是在網關或 Filter 層識別 Purpose: prefetch 請求並直接返回 204,避免業務邏輯重複執行與連接異常。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved