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

Java 微信開發:XML 與 JSON 數據處理實戰指南

     編輯:Java 相關知識

前言

微信公眾號/小程序開發中,消息接收採用 XML 格式,API 調用多返回 JSON。本文通過實戰代碼詳細講解 Java 中處理微信 XML/JSON 數據的核心技術、常用庫選型及最佳實踐。

一、微信 XML 消息處理

1. 消息類型與結構

微信服務器推送的消息均為 XML 格式,主要類型:

  • 文本消息、圖片消息、語音消息、視頻消息
  • 事件推送:關注/取消關注、掃碼、地理位置、菜單點擊

2. 使用 dom4j 解析 XML


// Maven 依賴
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.4</version>
</dependency>

3. 通用消息模型類


public class WeChatXmlMessage {
    // 基礎字段
    private String toUserName;
    private String fromUserName;
    private String createTime;
    private String msgType;
    private String msgId;
    
    // 文本消息
    private String content;
    
    // 事件消息
    private String event;
    private String eventKey;
    
    // 圖片/語音/視頻
    private String picUrl;
    private String mediaId;
    private String format;
    private String recognition;
    
    // 構造方法:自動解析 XML
    public WeChatXmlMessage(String xml) throws DocumentException {
        Document doc = DocumentHelper.parseText(xml);
        Element root = doc.getRootElement();
        
        // 通用字段
        this.toUserName = root.elementText("ToUserName");
        this.fromUserName = root.elementText("FromUserName");
        this.createTime = root.elementText("CreateTime");
        this.msgType = root.elementText("MsgType");
        this.msgId = root.elementText("MsgId");
        
        // 類型特有字段
        this.content = root.elementText("Content");
        this.event = root.elementText("Event");
        this.eventKey = root.elementText("EventKey");
        this.picUrl = root.elementText("PicUrl");
        this.mediaId = root.elementText("MediaId");
        this.format = root.elementText("Format");
        this.recognition = root.elementText("Recognition");
    }
    
    // Getter/Setter 省略
    
    // 判斷輔助方法
    public boolean isTextMessage() { return "text".equals(msgType); }
    public boolean isEventMessage() { return "event".equals(msgType); }
    public boolean isSubscribeEvent() { return "subscribe".equals(event); }
    public boolean isClickEvent() { return "CLICK".equals(event); }
}

4. Controller 接收示例


@RestController
@RequestMapping("/wechat")
public class WeChatController {
    
    @PostMapping("/callback")
    public String handleCallback(@RequestBody String xmlBody,
                                 @RequestParam("signature") String signature,
                                 @RequestParam("timestamp") String timestamp,
                                 @RequestParam("nonce") String nonce,
                                 @RequestParam("encrypt_type") String encryptType,
                                 @RequestParam("msg_signature") String msgSignature) {
        
        // 1. 驗證簽名(略)
        if (!checkSignature(signature, timestamp, nonce)) {
            return "signature invalid";
        }
        
        // 2. 解密(安全模式下需要,略)
        String plainXml = xmlBody; // 或解密後的明文
        
        // 3. 解析處理
        try {
            WeChatXmlMessage msg = new WeChatXmlMessage(plainXml);
            return processMessage(msg);
        } catch (Exception e) {
            log.error("XML 解析失敗", e);
            return "error";
        }
    }
    
    private String processMessage(WeChatXmlMessage msg) {
        if (msg.isTextMessage()) {
            return buildTextResponse(msg, "收到文本:" + msg.getContent());
        } else if (msg.isEventMessage()) {
            return handleEvent(msg);
        }
        return "success";
    }
    
    private String buildTextResponse(WeChatXmlMessage msg, String content) {
        return String.format(
            "<xml><ToUserName><![CDATA[%s]></ToUserName>" +
            "<FromUserName><![CDATA[%s]></FromUserName>" +
            "<CreateTime>%d</CreateTime>" +
            "<MsgType><![CDATA[text]></MsgType>" +
            "<Content><![CDATA[%s]></Content></xml>",
            msg.getFromUserName(), msg.getToUserName(), 
            System.currentTimeMillis()/1000, content);
    }
}

二、微信 JSON API 調用處理

1. 依賴選型:Gson vs Jackson

微信開發推薦 Gson(輕量、Android 友好)或 Jackson(Spring Boot 默認、功能強)。

2. Access Token 獲取與緩存


@Component
public class WeChatAccessTokenService {
    
    private static final String TOKEN_URL = 
        "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    
    @Value("${wechat.appid}")
    private String appId;
    
    @Value("${wechat.secret}")
    private String secret;
    
    // 使用 Redis 緩存,key: wechat:access_token
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    public String getAccessToken() {
        // 1. 嘗試從緩存獲取
        String cached = redisTemplate.opsForValue().get("wechat:access_token");
        if (StringUtils.hasText(cached)) {
            return cached;
        }
        
        // 2. 遠程獲取
        String url = String.format(TOKEN_URL, appId, secret);
        String response = httpClient.get(url); // 使用 HttpClient/RestTemplate/WebClient
        
        // 3. 解析 JSON
        JsonObject json = JsonParser.parseString(response).getAsJsonObject();
        
        if (json.has("errcode")) {
            throw new WeChatException("獲取 token 失敗: " + json.get("errmsg").getAsString());
        }
        
        String token = json.get("access_token").getAsString();
        int expiresIn = json.get("expires_in").getAsInt(); // 7200 秒
        
        // 4. 緩存(提前 5 分鐘過期防止邊界問題)
        redisTemplate.opsForValue().set("wechat:access_token", token, 
            expiresIn - 300, TimeUnit.SECONDS);
        
        return token;
    }
}

3. 統一 HTTP 調用封裝


public class WeChatApiClient {
    
    private final HttpClient httpClient;
    private final Gson gson = new GsonBuilder().create();
    private final WeChatAccessTokenService tokenService;
    
    public <T> T post(String apiPath, Object request, Class<T> responseType) {
        String url = "https://api.weixin.qq.com" + apiPath + "?access_token=" + tokenService.getAccessToken();
        String jsonRequest = gson.toJson(request);
        
        HttpPost post = new HttpPost(url);
        post.setEntity(new StringEntity(jsonRequest, ContentType.APPLICATION_JSON));
        
        try (CloseableHttpResponse response = httpClient.execute(post)) {
            String jsonResponse = EntityUtils.toString(response.getEntity());
            return gson.fromJson(jsonResponse, responseType);
        } catch (IOException e) {
            throw new WeChatException("API 調用失敗", e);
        }
    }
    
    // 素材上傳等 multipart 請求單獨處理
    public MediaUploadResult uploadMedia(File file, String type) { ... }
}

4. 典型 JSON 響應處理


// 統一響應模型
public class WeChatResponse {
    private Integer errcode;
    private String errmsg;
    
    public boolean isSuccess() { return errcode == null || errcode == 0; }
    // getter/setter
}

// 具體業務響應
public class UserInfoResponse extends WeChatResponse {
    private String openid;
    private String nickname;
    private String headimgurl;
    private Integer subscribe;
    // ...
}

// 使用示例
UserInfoResponse userInfo = apiClient.post("/cgi-bin/user/info", 
    Map.of("openid", openid, "lang", "zh_CN"), UserInfoResponse.class);

if (!userInfo.isSuccess()) {
    log.error("獲取用戶信息失敗: {}", userInfo.getErrmsg());
}

三、安全模式:消息加解密

微信安全模式下,消息體經過 AES 加密,需使用官方提供的 WXBizMsgCrypt 工具類:


// 配置
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);

// 解密
String decryptMsg = pc.decryptMsg(msgSignature, timestamp, nonce, encryptXml);

// 加密回復
String encryptResp = pc.encryptMsg(replyXml, timestamp, nonce);

四、最佳實踐總結

場景推薦方案關鍵點
XML 解析dom4j / JDOM2CDATA 處理、命名空間、異常容錯
JSON 處理Gson / Jackson統一錯誤碼處理、泛型解析、日期格式
Access TokenRedis 緩存 + 定時刷新分布式鎖防止並發重複請求、提前過期
HTTP 客戶端HttpClient 5 / WebClient連接池、超時配置、重試機制
異步處理線程池 / CompletableFuture耗時操作(客服消息、模板消息)異步發送

五、常見坑點避雷

  1. XML 實體轉義:回復內容必須用 CDATA 包裹,防止特殊字符破壞 XML 結構
  2. 空指針異常:微信某些字段可能缺失,解析時需做 null 判斷
  3. 編碼問題:請求/響應統一 UTF-8,避免中文亂碼
  4. Token 並發刷新:高併發下使用分布式鎖(Redis SETNX)保證單次刷新
  5. IP 白名單:微信 API 需配置服務器 IP 白名單,否則調用失敗

總結

微信開發核心在於 XML 消息解析 與 JSON API 調用 兩大數據處理場景。選擇成熟庫、封裝統一客戶端、妥善管理 Access Token、處理加密模式,是構建穩健微信應用的關鍵。建議基於 Spring Boot 搭建微信開發脚手架,將通用能力組件化復用。

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