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

Jsoup 發送 POST 請求拋出 InvocationTargetException 排查實錄

     編輯:Java 綜合教學

Jsoup 發送 POST 請求拋出 InvocationTargetException 排查實錄

記錄一次看似詭異的 InvocationTargetException 排查過程,最終通過 Gson 繞過問題,但根因值得深思,作為網絡請求異常處理的反面教材。

一、現場還原

業務流程:通過 icid 查詢客戶密鑰 → 攜帶密鑰請求商品列表 → 解析 JSON → 遍歷匹配商品獲取 giid。

二、關鍵代碼片段

// 1. 獲取密鑰
String key = Jsoup.connect(NMApi.url() + "/Stores/findKey")
    .timeout(60000)
    .data("icid", icid)
    .post()
    .text();

if (key == null || key.isEmpty()) {
    return "查詢客戶密鑰失敗";
}

// 2. 獲取商品列表
String result = Jsoup.connect(NMApi.url() + "/wh/goods")
    .timeout(10000)
    .data("RequestName", "wGoodsInfo")
    .data("icID", icid)
    .data("cKey", key)
    .data("cDepot", cdepot)
    .data("iPage", "1")
    .data("iPagePer", "111")
    .data("cqgCode", "")
    .data("cqcCode", "")
    .data("iqwGiid", "")
    .data("nqType", "0")
    .get()  // 注意:此處用 GET
    .text();

System.out.println("查詢商品字符串: " + result);

// 3. Gson 解析
Map<String, Object> map = new Gson().fromJson(result, new TypeToken<Map<String, Object>>(){}.getType());
ArrayList<goodsRec> reclist = (ArrayList<goodsRec>) map.get("RecList");

// 4. 遍歷匹配
String giid = "";
for (int i = 0; i < reclist.size(); i++) {
    if (reclist.get(i).getCgName().equals(cgoods) || reclist.get(i).getCgCode().equals(cgoods)) {
        giid = reclist.get(i).getIwGiid();
        break;
    }
}
if (giid.isEmpty()) return "查無此物品";

三、異常現象

  • 第 402 行(約對應 reclist.get(4) 或循環內部)拋出 InvocationTargetException
  • 堆棧信息未保留,僅知為反射調用異常包裝器
  • 改用 Gson 直接反序列化為對象列表後問題消失

四、深度排查與根因假設

疑點分析
HTTP 方法不匹配接口文檔要求 POST,代碼用 .get() 發送 GET,導致服務端返回錯誤頁面/空數據,JSON 解析產生非預期結構
泛型擦除導致類型轉換失敗map.get("RecList") 返回 List<LinkedTreeMap>(Gson 默認),強轉 ArrayList<goodsRec> 拋 ClassCastException,被反射調用包裝為 InvocationTargetException
數據結構不穩定服務端可能返回錯誤碼對象而非列表,導致 RecList 為 null 或非 List 類型
Jsoup 連接復用問題同一 Connection 對象多次調用 post()/get() 可能攜帶殘留數據

五、修正版最佳實踐代碼

// 定義標準響應結構
class ApiResponse {
    List<GoodsRec> RecList;
    String errorCode;
    String errorMsg;
}

class GoodsRec {
    String cgName;
    String cgCode;
    String iwGiid;
    // getter/setter
}

// 業務方法
public String findGiid(String icid, String cdepot, String cgoods) {
    try {
        // 1. 獲取密鑰 (POST)
        String key = Jsoup.connect(apiUrl + "/Stores/findKey")
            .timeout(60000)
            .ignoreContentType(true)  // 關鍵:防非 text/html 返回拋異常
            .data("icid", icid)
            .post()
            .text();

        if (key == null || key.isEmpty()) return "查詢客戶密鑰失敗";

        // 2. 查詢商品 (按文檔用 POST)
        String json = Jsoup.connect(apiUrl + "/wh/goods")
            .timeout(10000)
            .ignoreContentType(true)
            .data("RequestName", "wGoodsInfo")
            .data("icID", icid)
            .data("cKey", key)
            .data("cDepot", cdepot)
            .data("iPage", "1")
            .data("iPagePer", "100")
            .post()  // 修正為 POST
            .text();

        // 3. 安全解析
        ApiResponse resp = new Gson().fromJson(json, ApiResponse.class);
        if (resp.RecList == null || resp.RecList.isEmpty()) return "查無商品數據";

        // 4. 匹配
        for (GoodsRec rec : resp.RecList) {
            if (cgoods.equals(rec.cgName) || cgoods.equals(rec.cgCode)) {
                return rec.iwGiid;
            }
        }
        return "查無此物品";

    } catch (Exception e) {
        log.error("查詢異常", e);
        return "系統異常: " + e.getMessage();
    }
}

六、核心經驗總結

  1. 嚴格遵循接口文檔 HTTP 方法:GET/POST 混用是首要排查點。
  2. 加上 ignoreContentType(true):Jsoup 默認只接受 text/html,API 返回 application/json 會直接拋異常。
  3. 定義強類型響應類:避免 Map<String,Object> + 強轉,利用 Gson 直接映射到 POJO,編譯期發現類型錯誤。
  4. 防御性編程:每層解析後判 null、判 empty、catch 異常,提供清晰錯誤碼而非拋給上層。
  5. InvocationTargetException 通常是反射調用的「外殼」:看 getCause() 才是真兇(如 ClassCastException、NullPointerException)。

「繞過問題」不是解決問題。這次通過 Gson 直接映射對象掩蓋了類型轉換錯誤,真正根因極大概率是 **HTTP 方法錯誤導致返回非預期 JSON**,配合 **Jsoup 默認 content-type 校驗** 觸發異常鏈。養成「先看網絡層、再看解析層、最後看業務層」的排查順序,能避開 90% 的坑。

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