Nest.js 处理 XML 请求及响应

翟修永
2023-12-01

0x01 背景

笔者最近在使用 Nest.js 开发服务端程序,遇到了一些需要处理 XML 的场景,搜遍了网络上发现没有比较优雅的方式,于是摸索后将过程整理出来。

0x02 研究

Google 搜索 nest handle xml 的第一个结果是一篇中文文章:

Nest 中处理 XML 类型的请求与响应

但是照猫画虎了一番,发现 TS 总是报错,可能是 body-parser-xml 实现得不算很优雅,而且对 TS 支持得不够好,就放弃了。

Nest.js 底层框架默认是 Express,搜索得知默认会使用 body-parser 来处理请求,但是不支持,所以第一步首先要修改支持 application/xml 的 **Content-Type,**配合 Nest 的 middleware,我们可以封装一层,只针对单个 module 来使用。

同时对比了一下 XML 的处理模块,发现 fast-xml-parser ⭐️⭐️最多,所以决定使用它来进行 XML 和 JSON 间的相互转换。

0x03 编码

好,上代码~,首先我们创建一个 middleware,使用如下命令:

nest g middleware xml

Nest CLI 会生成 xml.middleware.ts,编写如下代码:

import { Injectable, NestMiddleware } from '@nestjs/common';
import * as bodyParser from 'body-parser';

const bodyParserMiddleware = bodyParser.text({
  limit: '1024kb',
  type: 'application/xml',
});

@Injectable()
export class XMLMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    bodyParserMiddleware(req, res, next);
  }
}

以上代码只是封装了一层 body-parser,使之可以处理 XML 请求。

然后在 app.module.ts 中添加这个 middleware:

import { XMLMiddleware } from './common/xml.middleware';

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(XMLMiddleware).forRoutes({
      path: 'wework/*',
      method: RequestMethod.POST,
    });
  }
}

由于笔者是开发企业微信的回调服务,所以这里只针对 wework 开头的接口应用。

随后就可以在 controller 中使用 @Body 接收到 XML 字符串了。如:

import { Body, Controller, Post, Logger } from '@nestjs/common';
import * as xmlParser from 'fast-xml-parser';

@Controller('wework')
export class UsersController {
  private logger = new Logger(UsersController.name);

  @Post('callback')
  respond(@Body() body): string {
    if (xmlParser.validate(body) !== true) {
      return 'xml invalid';
    }
    this.logger.log('from controller', body);
    const parsed = xmlParser.parse(body);
    return this.weworkService.respond(parsed.xml);
  }
}

然后使用 fast-xml-parser 即可将 XML 字符串转换成 JSON 串了~

这样虽然也能使用了,但是解析 XML 的操作还是要写在业务代码中,就如上述代码中的

const parsed = xmlParser.parse(body);

那么有没有更优雅的方式呢?

答案是有的,这就要用到 Nest.js 中的 Custom Decorator 了

0x04 优化

首先新建一个 decorator,使用如下指令:

nest g decorator xml

nest 会生成一个 xml.decorator.ts 文件,编写如下代码:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import * as xmlParser from 'fast-xml-parser';

export const XML = createParamDecorator((data: string, ctx: ExecutionContext) => {
  const { body } = ctx.switchToHttp().getRequest();
  if (xmlParser.validate(body) !== true) {
    return 'xml invalid';
  }
  const { xml } = xmlParser.parse(body);
  return xml;
});

这里的逻辑比较简单,就是先校验 XML,然后解析并返回。

需要注意的是 fast-xml-parser 解析后的返回值是包在 xml 属性里的,所以需要解构一下。

到了使用的地方,我们只要像使用其他 Decorator 一样,在 Controller 的参数那里调用即可:

@Post('callback/:uuid')
@HttpCode(200)
async respond(@Param('uuid') uuid, @XML() parsed: IWeworkEncrypt) {
  return this.weworkService.respond(uuid, parsed);
}

0x05 总结

到这里本篇文章主要内容就完了,整体思路比较简单,个人感觉这个框架用起来还是有一定门槛的,不过相关的生态都很完善,大部分问题都能找到解决方案~

由于是 Nest.js 新手,对里面的概念理解的不是很透彻,不过有问题可以多多交流哈~

 类似资料: