公众号及小程序的微信接口是通过 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 的命令行工具创建一个示例项目用于后面的演示。

$ nest n xml-handling

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 来进行。

安装依赖

$ yarn add 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 中找到。

相关资源

The text was updated successfully, but these errors were encountered:

Nest 中处理 XML 类型的请求与响应的更多相关文章

  1. Spring Boot 2.x基础教程:如何扩展XML格式的请求和响应

    在之前的所有Spring Boot教程中,我们都只提到和用到了针对HTML和JSON格式的请求与响应处理.那么对于XML格式的请求要如何快速的在Controller中包装成对象,以及如何以XML的格式 ...

  2. 知方可补不足~sqlserver中对xml类型字段的操作

    回到目录 在sqlserver中有很多种数据类型,而XML数据类型是比较新奇怪的一种格式,我们平常接触的可能比较少,用的也少,而在某些场合,使用XML类型可能会使我们的开发变简单,下面就是一种情况: ...

  3. T-SQL——关于XML类型

    目录 0. 将结果集转化为XML格式 1. 列值拼接为字符串 2. 字符串转换为列值 3. 一些说明 参考 志铭-2021年10月23日 10:43:21 0. 将结果集转化为XML格式 测试数据 I ...

  4. HTTP协议简介详解 HTTP协议发展 原理 请求方法 响应状态码 请求头 请求首部 java模拟浏览器客户端服务端

    协议简介 协议,自然语言里面就是契约,也是双方或者多方经过协商达成的一致意见; 契约也即类似于合同,自然有甲方123...,乙方123...,哪些能做,哪些不能做; 通信协议,也即是双方通过网络通信必 ...

  5. Java Servlet (1) —— Filter过滤请求与响应

    Java Servlet (1) -- Filter过滤请求与响应 版本: Java EE 6 参考来源: Oracle:The Java EE 6 Tutorial: Filtering Reque ...

  6. Fiddler之模拟响应、修改请求或响应数据(断点)

    在测试过程中,有时候需要修改请求或响应数据,或者直接模拟服务器响应,此时可以使用fiddler进行此类操作.可以使用断点功能完成. 一.修改请求数据 在发起请求后,需要修改请求的数据时,可以设置请求前 ...

  7. Spring Boot中如何扩展XML请求和响应的支持

    在之前的所有Spring Boot教程中,我们都只提到和用到了针对HTML和JSON格式的请求与响应处理.那么对于XML格式的请求要如何快速的在Controller中包装成对象,以及如何以XML的格式 ...

  8. Spring Boot中扩展XML请求和响应的支持

    在Spring Boot中,我们大多时候都只提到和用到了针对HTML和JSON格式的请求与响应处理.那么对于XML格式的请求要如何快速的在Controller中包装成对象,以及如何以XML的格式返回一 ...

  9. struts.xml中的结果类型与视图

    实际上在Struts2框架中,一个完整的结果视图配置文件应该是: ? 1 2 3 4 5 <action name="Action名称" class="Action ...

随机推荐

  1. linux查看目录文件以及子目录文件大小的命令

    可以使用以下命令,不过如果文件比较多,因为是递归统计大小的的,所以结果出来的会比较慢,需要等待. du -h --max-depth=1 * 以下是命令的说明 du [-abcDhHklmsSx] [ ...

  2. Spring @Transactional注解和ReentrantLock同步锁同时使用不能同步的问题

    结论:如果在service层的方法上同时使用事务和同步锁无法保证数据同步. 1 @Service 2 public class ServiceImpl{ 3 4 private static Lock ...

  3. 控制流程-if/while/for

    目录 一.控制流程之if判断 1.单分支结构 2.双分支结构 3.多分支结构 二.控制流程之while循环 1.基本使用 2.break 3.continue 三.流程控制之for循环 1.break ...

  4. API版本管理中的沟通问题

    转: API版本管理中的沟通问题 产品升级会涉及API的更改,当API改动较大时,最大的问题是如何通知API的使用者(内部人员与使用OPENAPI 的用户),我们不能强迫所有用户立即对API的更改做出 ...

  5. 【Arduino学习笔记04】消抖动的按键切换

    "开关抖动": 由于按键是基于弹簧-阻尼系统的机械部件,所以当按下一个按键时,读到的信号并不是从低到高,而是在高低电平之间跳动几毫秒之后才最终稳定. 代码解读: 1 const i ...

  6. C# 应用 - 封装类访问 Postgresql 数据库

    引入库类 连接数据库 访问数据库 1)增删改数据库 2)查数据库 数据转换 事务 1. 引入库类 引入 Npgsql.dll using Npgsql; using NpgsqlTypes; 2. 连 ...

  7. Distributed Cache(分布式缓存)-SqlServer

    分布式缓存是由多个应用服务器共享的缓存,通常作为外部服务存储在单个应用服务器上,常用的有SqlServer,Redis,NCache. 分布式缓存可以提高ASP.NET Core应用程序的性能和可伸缩 ...

  8. P1618 三连击(升级版)(JAVA语言)

    题目描述 将1,2,-,9共9个数分成三组,分别组成三个三位数,且使这三个三位数的比例是A:B:C,试求出所有满足条件的三个三位数,若无解,输出"No!!!". //感谢黄小U饮品 ...

  9. P1028_数的计算(JAVA语言)

    题目描述 我们要求找出具有下列性质数的个数(包含输入的自然数n): 先输入一个自然数n(0n≤1000),然后对此自然数按照如下方法进行处理: 不作任何处理; 在它的左边加上一个自然数,但该自然数不能 ...

  10. Java-TreeMap和Guava-HashMultiset

    一.Java-TreeMap 1.数据结构 底层数据结构是裸的红黑树,保证元素有序,没有比较器Comparator的情况按照key的自然排序,可自定义比较器.线程不安全. 可以存null,但是key不 ...