微信公眾號/小程序開發中,消息接收採用 XML 格式,API 調用多返回 JSON。本文通過實戰代碼詳細講解 Java 中處理微信 XML/JSON 數據的核心技術、常用庫選型及最佳實踐。
微信服務器推送的消息均為 XML 格式,主要類型:
// Maven 依賴
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.4</version>
</dependency>
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); }
}
@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);
}
}
微信開發推薦 Gson(輕量、Android 友好)或 Jackson(Spring Boot 默認、功能強)。
@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;
}
}
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) { ... }
}
// 統一響應模型
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 / JDOM2 | CDATA 處理、命名空間、異常容錯 |
| JSON 處理 | Gson / Jackson | 統一錯誤碼處理、泛型解析、日期格式 |
| Access Token | Redis 緩存 + 定時刷新 | 分布式鎖防止並發重複請求、提前過期 |
| HTTP 客戶端 | HttpClient 5 / WebClient | 連接池、超時配置、重試機制 |
| 異步處理 | 線程池 / CompletableFuture | 耗時操作(客服消息、模板消息)異步發送 |
微信開發核心在於 XML 消息解析 與 JSON API 調用 兩大數據處理場景。選擇成熟庫、封裝統一客戶端、妥善管理 Access Token、處理加密模式,是構建穩健微信應用的關鍵。建議基於 Spring Boot 搭建微信開發脚手架,將通用能力組件化復用。