公眾號及小程式的微信介面是通過 xml 格式進行數據交換的。
比如接收普通消息的介面:
當普通微信用戶向公眾帳號發消息時,微信伺服器將 POST 消息的 XML 數據包到開發者填寫的 URL 上。
— 微信官方文檔 – 接收普通消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>
不僅微信端推送給我們的數據是 XML 類型的,我們調用微信介面,也需要傳遞 XML 類型的數據。
比如被動回復用戶消息:
當用戶發送消息給公眾號時(或某些特定的用戶操作引發的事件推送時),會產生一個 POST 請求,開發者可以在響應包(Get)中返回特定 XML 結構,來對該消息進行響應(現支援回復文本、圖片、圖文、語音、影片、音樂)。嚴格來說,發送被動響應消息其實並不是一種介面,而是對微信伺服器發過來消息的一次回復。
— 微信官方文檔 – 被動回復用戶消息
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
因此,不同於常見的 JSON,要求我們在介面中需要處理 XML 格式的數據。
創建示例項目
使用 Nest 的命令行工具創建一個示例項目用於後面的演示。
XML 數據的接收
接收和處理 XML 類型的數據,可使用 body-parser-xml。
安裝依賴
$ yarn add body-parser body-parser-xml
啟用 body-parser 中間件
使用上面的中間件對請求傳遞的 XML 數據進行解析。
src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
const bodyParser = require('body-parser');
require('body-parser-xml')(bodyParser);
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(
bodyParser.xml({
xmlParseOptions: {
explicitArray: false, // 始終返回數組。默認情況下只有數組元素數量大於 1 是才返回數組。
},
}),
);
await app.listen(3000);
}
bootstrap();
接收並處理 XML 數據
假設配置的是微信在收到用戶消息時,調用我們的 wxhandler 介面。
src/app.controller.ts
import { Body, Controller, Logger, Post } from '@nestjs/common';
import { inspect } from 'util';
/**
* 微信回調給開發者的消息
*/
interface IWxMessageXmlData {
/** 開發者微訊號 e.g. `gh_019087f88815`*/
ToUserName: string;
/** 發送方帳號(一個OpenID)e.g.: `o5w5awUl***5pIJKY`*/
FromUserName: string;
/** 消息創建時間 (整型)e.g.`1595855711` */
CreateTime: string;
/** 消息類型,此處為 `event` */
MsgType: string;
/** 事件類型,subscribe(訂閱)、unsubscribe(取消訂閱) */
Event: 'subscribe' | 'unsubscribe';
/** 事件KEY值,目前無用 */
EventKey: string;
}
@Controller()
export class AppController {
@Post('wxhandler')
async wxhandler(@Body('xml') xmlData: IWxMessageXmlData) {
Logger.log(`xml data got: ${inspect(xmlData)}`);
return '';
}
}
測試:
$ curl --location --request POST 'localhost:3000/wxhandler' \
--header 'Content-Type: application/xml' \
--data-raw '<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>'
從列印的日誌中查看解析後的 XML 數據:
[Nest] 89424 - 10/19/2020, 7:58:00 PM xml data got:
{ ToUserName: 'toUser',
FromUserName: 'fromUser',
CreateTime: '1348831860',
MsgType: 'text',
Content: 'this is a test',
MsgId: '1234567890123456' }
+3850ms
XML 數據的返回
讓介面響應 XML 數據,需要我們在程式碼中先創建該類型的數據,可通過 xmlbuilder2 來進行。
安裝依賴
使用 xmlbuilder2 創建 XML 數據
通過上述工具進行 XML 的創建然後作為請求的響應。
src/app.controller.ts
import { Body, Controller, Logger, Post } from '@nestjs/common';
import { inspect } from 'util';
+ import { create } from 'xmlbuilder2';
@Controller()
export class AppController {
@Post('wxhandler')
async wxhandler(@Body('xml') xmlData: IWxMessageXmlData) {
Logger.log(`xml data got:\n ${inspect(xmlData)}\n`);
+ const res = create({
+ xml: {
+ ToUserName: 'openid_xxx', // 接收方帳號(收到的OpenID)
+ FromUserName: 'openod_yyy', // 開發者微訊號
+ CreateTime: new Date().getTime(), // 消息創建時間 (整型)
+ MsgType: 'text',
+ Content: 'some text',
+ },
+ }).end({ prettyPrint: true });
return res;
}
}
測試並查看返回:
$ curl --location --request POST 'localhost:3000/wxhandler' \
--header 'Content-Type: application/xml' \
--data-raw '<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
</xml>'
<?xml version="1.0"?>
<xml>
<ToUserName>openid_xxx</ToUserName>
<FromUserName>openod_yyy</FromUserName>
<CreateTime>1603109187287</CreateTime>
<MsgType>text</MsgType>
<Content>some text</Content>
</xml>⏎
示例程式碼
以上程式碼可在這個示例倉庫 wayou/xml-handling 中找到。
相關資源
|