85-Nodejs课程2023:响应:异步身体解析器
#typescript #node #mongodb #fastify

在上一章中,我们看到了如何使用我们的资源并添加了夫妇功能bootextend,这使我们使我们的toJSON方法成为async,这就是我们遇到的严重问题,因为JSON.stringify不支持async函数,因此我们需要找到一种使它起作用的方法。

异步JSON PARSER

还记得我们的response课程吗?是的,我们还没有使用它,现在该使用它了,我们将使用它在将数据发送到响应之前使用它来解析数据,但是在更新它之前,我们需要更新我们的request类以传递处理程序对响应类的输出。

// src/core/http/request.ts

// ...

  /**
   * Execute the request
   */
  public async execute() {
    // check for middleware first
    const middlewareOutput = await this.executeMiddleware();

    if (middlewareOutput !== undefined) {
      // 👇🏻 send the response
      return this.response.send(middlewareOutput);
    }

    const handler = this.route.handler;

    // 👇🏻 check for validation using validateAll helper function
    const validationOutput = await validateAll(
      handler.validation,
      this,
      this.response,
    );

    if (validationOutput !== undefined) {
      // 👇🏻 send the response
      return this.response.send(validationOutput);
    }

    // call executingAction event
    this.trigger("executingAction", this.route);
    const output = await handler(this, this.response);

    // call executedAction event
    this.trigger("executedAction", this.route);

    // 👇🏻 send the response
    await this.response.send(output);
  }

因此,我们将把输出传递给响应的send方法,而不是从中间件,验证或处理程序返回输出,以便我们可以自由处理该输出。

现在让我们更新我们的response类以处理输出。

// src/core/http/response.ts
// ...

  /**
   * Send the response
   */
  public async send(data?: any, statusCode?: number) {
    if (data) {
      this.currentBody = data;
    }

    if (statusCode) {
      this.currentStatusCode = statusCode;
    }

    if (!this.currentStatusCode) {
      this.currentStatusCode = 200;
    }

    // trigger the sending event
    this.trigger("sending", this.currentStatusCode, data);

    this.baseResponse.status(this.currentStatusCode).send(data);

    // trigger the sent event
    this.trigger("sent", this.currentStatusCode, data);

    // trigger the success event if the status code is 2xx
    if (this.currentStatusCode >= 200 && this.currentStatusCode < 300) {
      this.trigger("success", data, this.currentStatusCode, this.route);
    }

    // trigger the successCreate event if the status code is 201
    if (this.currentStatusCode === 201) {
      this.trigger("successCreate", data, this.currentStatusCode, this.route);
    }

    // trigger the badRequest event if the status code is 400
    if (this.currentStatusCode === 400) {
      this.trigger("badRequest", data, this.currentStatusCode, this.route);
    }

    // trigger the unauthorized event if the status code is 401
    if (this.currentStatusCode === 401) {
      this.trigger("unauthorized", data, this.currentStatusCode, this.route);
    }

    // trigger the forbidden event if the status code is 403
    if (this.currentStatusCode === 403) {
      this.trigger("forbidden", data, this.currentStatusCode, this.route);
    }

    // trigger the notFound event if the status code is 404
    if (this.currentStatusCode === 404) {
      this.trigger("notFound", data, this.currentStatusCode, this.route);
    }

    // trigger the throttled event if the status code is 429
    if (this.currentStatusCode === 429) {
      this.trigger("throttled", data, this.currentStatusCode, this.route);
    }

    // trigger the serverError event if the status code is 500
    if (this.currentStatusCode === 500) {
      this.trigger("serverError", data, this.currentStatusCode, this.route);
    }

    // trigger the error event if the status code is 4xx or 5xx
    if (this.currentStatusCode >= 400) {
      this.trigger("error", data, this.currentStatusCode, this.route);
    }

    return this;
  }

这是我们以前的发送方法,我们将在第一次检查之后添加新行。

// src/core/http/response.ts

// ...

  /**
   * Send the response
   */
  public async send(data?: any, statusCode?: number) {
    if (data) {
      this.currentBody = data;
    }

    // parse the body and make sure it is transformed to sync data instead of async data
    data = await this.parseBody();
    // ...
  }

我们在此处添加了一个新行,它将解析我们的currentBody数据,并确保将其转换为同步数据而不是异步数据,因此我们可以将其发送到响应。

身体解析器

现在让我们创建我们的parseBody方法,但是在我们转到实施之前,让我告诉您此方法的工作流程。

基本上,此方法将调用另一种称为parse的方法,然后将currentBody传递到该方法。

我们为什么要这样做?因为我们在这里进行一些递归,因此parse方法将接收到任何类型的数据并相应地处理,无论其主体或嵌套对象。

现在返回解析方法工作流程

  1. 如果给定值是falsy值,我们将以原样返回。
  2. 检查传递的数据是否具有toJSON方法,如果是的话,则await并返回结果。
  3. 检查传递的数据是否为array,如果是的话,请循环循环,并在每个项目上调用parse方法并返回结果。
  4. 如果它不是普通的对象,也不是一个数组,并且没有toJSON,那么我们将只返回它。
  5. 如果它是一个普通的对象,那么我们将在其键上循环,并在每个值上调用parse方法并返回结果。

实际上我们将更改array的点,我们将检查值是否为iterable,而不是检查它是否是数组,因为我们要支持任何类型的峰值数据,而不仅仅是数组。

现在让我们去实施。

// src/core/http/response.ts
// we'll need it here to make couple checks later
import Is from "@mongez/supportive-is";

// ...
export class Response {
    // ...
    /**
     * Parse body
     */
    protected async parseBody() {
        return await this.parse(this.currentBody);
    }
    // ...
}

现在让我们创建parse方法。

// src/core/http/response.ts
// ...


export class Response {
    // ...

  /**
   * Parse the given value
   */
  protected async parse(value: any): Promise<any> {
    // if it is a falsy value, return it
    if (!value) return value;

    // if it has a `toJSON` method, call it and await the result then return it
    if (value.toJSON) {
      return await value.toJSON();
    }

    // if it is iterable, an array or array-like object then parse each item
    if (Is.iterable(value)) {
      return await Promise.all(
        Array.from(value).map((item: any) => {
          return this.parse(item);
        }),
      );
    }

    // if not plain object, then return it
    if (!Is.plainObject(value)) {
      return value;
    }

    // loop over the object and check if the value and call `parse` on it
    for (const key in value) {
      const subValue = value[key];

      value[key] = await this.parse(subValue);
    }

    return value;
  }
}

代码几乎与我上面写的流步骤一样,我们检查了它是否是falsy值,然后我们将其返回,如果它具有toJSON方法,则等待它的调用并返回。

如果是iterable这就是我们导入的支持是方法),那么我们将循环浏览它,并在每个项目上调用parse方法并返回结果。

如果不是plainObject,那么我们将返回它。

如果它是plainObject,那么我们将循环循环其键,并在每个值上调用parse方法并返回结果。

关于iterable数据,您可能会看到代码有些怪异,我们使用Promise.all等待所有承诺要解决,Promise.all方法会收到一个数组,这就是为什么我们使用Array.from转换iToble的数据( arrays( arrays)也被认为是)到一个数组,然后映射每个值以使用parse方法调用。

现在,如果我们再次尝试返回资源,它将正常工作。

- 结论

在本节中,我们自定义了响应的最终输出,我们使自己的数据解析器将每个async数据发送为sync数据。

â•给我买一杯咖啡。

如果您喜欢我的文章并看到对您有用,则可以buy me a coffee,它将帮助我继续前进并继续创建更多内容。

ð项目存储库

您可以在Github

上找到此项目的最新更新

ð加入我们的社区

加入我们的社区Discord获得帮助和支持(节点JS 2023频道)。

ð视频课程(阿拉伯语)

如果您想以视频格式学习此课程,则可以在Youtube上找到该课程,该课程是阿拉伯语。

ð奖励内容ð

您可能会看这些文章,这肯定会提高您的知识和生产力。

一般主题

软件包和库

React JS软件包

课程(文章)