84-Nodejs课程2023:资源:启动和扩展
#typescript #node #mongodb #fastify

到目前为止,我们在资源方面做得很好,让我们再走一步,并定义一种使用bootextend方法自定义资源输出的方法。

需求

为什么我们需要这些方法?好吧,有时我们需要自定义资源的输出,例如,我们可能需要向输出添加一个新密钥,或者我们可能需要从输出中删除键,或者我们可能需要更改一个值输出中的键。

这些更改主要是条件的,例如,如果当前资源为User Resource,并且当前用户与资源中的用户相同,那么我们可能需要在输出中添加一个新密钥,例如假设我们要在输出中添加一个名为cart的新密钥,但是我们只想在当前用户与资源中的用户相同时添加此密钥。

另一个用例,如果您正在使用课程API,并且您正在返回课程列表,或者只是一门课程,我们想在最终输出中添加一个新键而且,如果当前用户已订阅该课程,我们希望将此值设置为true,否则将将密钥设置为false或可能根本不返回,因此这是我们资源中要实现的must功能。

引导并扩展

基本上,这是两种方法,在将我们的output属性转换为最终输出之前,将调用第一个boot,第二个extend将在将我们的output属性转换为最终输出后。

因此,您可以使用boot在输出中添加新键,替换或从中删除任何键,您可以使用extend将新键添加到输出,替换或从中删除任何键,但是在输出转换后。

执行

实现非常简单,这是两种方法(non-static of course),在转换输出之前将调用第一个方法,而第二个方法将在转换输出后调用。

请注意,我们将进行这两种方法异步方法,以便您可以执行其中的任何异步操作,例如从数据库中获取数据。

// core/resources/resource.ts

// ...

export default class Resource {

  /**
   * Boot method
   * Called before transforming the resource
   */
  public async boot() {
    //
  }

  /**
   * Extend the resource output
   * Called after transforming the resource
   */
  public async extend() {
    //
  }
  // ...
}

简单的两个async空方法,现在我们将更新我们的toJSON函数为async方法,并在转换输出之前和之后调用这两种方法。

// core/resources/resource.ts

// ...

export default class Resource {

  // ...

  /**
   * Transform resource to object, that's going to be used as the final output
   */
  public async toJSON() {
    await this.boot();

    await this.transformOutput();

    await this.extend();

    return this.data;
  }
  // ...
}

您可能会想知道,我们的toJSON方法中的那个巨型代码块在哪里,我只是将其移至transformOutput方法,现在更加干净。

我们还可以制作将在转换过程中使用的任何可呼叫功能也为async

异步转换

现在,让我们更新我们的transformOutput方法也为async方法。

在其中,将会打电话给transformValue此方法必须为awaited,以便我们也可以将其制作为async

// core/resources/resource.ts

// ...

export default class Resource {

  // ...

  /**
   * Transform final output
   */
  protected async transformOutput() {
    for (const key in this.output) {
      // first check if key is disabled
      if (this.isDisabledKey(key)) continue;

      if (!this.isAllowedKey(key)) continue;

      // get value type
      const valueType = this.output[key];

      // now get the value from the given resource data
      const value = get(
        this.resource,
        key,
        get(this.defaults, key, missingKey),
      );

      if (value === missingKey) {
        continue;
      }

      if (Is.empty(value)) continue;

      if (Array.isArray(value)) {
        this.data[key] = value.map(
          async item => await this.transformValue(item, valueType),
        );
      } else {
        this.data[key] = await this.transformValue(value, valueType);
      }
    }
  }

  // ...
}

我在transformOutput方法中添加了async关键字,然后我在transformValue方法调用中添加了await关键字。

请注意,我们必须将映射函数中的回调为async,因此我们可以await transformValue方法调用。

现在,让我们更新我们的transformValue方法也为async方法。

// core/resources/resource.ts

// ...


export default class Resource {

  // ...

  /**
   * Transform final output
   */
  protected async transformOutput() {
    for (const key in this.output) {
      // first check if key is disabled
      if (this.isDisabledKey(key)) continue;

      if (!this.isAllowedKey(key)) continue;

      // get value type
      const valueType = this.output[key];

      // now get the value from the given resource data
      const value = get(
        this.resource,
        key,
        get(this.defaults, key, missingKey),
      );

      if (value === missingKey) {
        continue;
      }

      if (Is.empty(value)) continue;

      if (Array.isArray(value)) {
        this.data[key] = value.map(
          async item => await this.transformValue(item, valueType),
        );
      } else {
        this.data[key] = await this.transformValue(value, valueType);
      }
    }
  }

  /**
   * Transform value
   */
  protected async transformValue(value: any, valueType: any) {
    if (typeof valueType === "string") {
      value = this.cast(value, valueType);
    } else if (valueType.prototype instanceof Resource) {
      if (!this.isValidResourceValue(value)) return null;
      value = new valueType(value);
    } else if (typeof valueType === "function") {
      value = await valueType.call(this, value);
    }

    return value;
  }
  // ...
}

我在transformValue方法中添加了async关键字,然后我在valueType函数调用中添加了await关键字。

现在让我们在我们的UserResource类中尝试一下。

// src/app/users/resources/user-resource.ts
import Resource from "core/resources/resource";

export default class UserResource extends Resource {
  /**
   * {@inheritDoc}
   */
  public async boot(): Promise<void> {
    this.data.welcome = true;
  }

  /**
   * {@inheritDoc}
   */
  public async extend(): Promise<void> {
    this.data.goodbye = true;
  }
}

现在您应该看到这样的东西:

  {
    "welcome": true,
    "isActive": true,
    "isPhoneVerified": false,
    "name": "John Doe",
    "email": "hassanzohdy@gmail.com",
    "image": "http://127.0.0.1:3000/uploads/users/my-image.jpg",
    "goodbye": true
  },

但这确实无法正常工作,为什么?因为我们的方法toJSON基本上是sync方法,现在我们将其更改为async方法,因此这将不再起作用。

这将是我们的下一个主题。

- 结论

在本章中,我们讨论了bootingextending资源的重要性,以及为什么它应该在我们的代码库中。

在下一章中,我们将看到如何使我们的toJSON方法成为async方法。

那是我们的电力响应班来的地方。

â•给我买一杯咖啡。

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

ð项目存储库

您可以在Github

上找到此项目的最新更新

ð加入我们的社区

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

ð视频课程(阿拉伯语)

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

ð奖励内容ð

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

一般主题

软件包和库

React JS软件包

课程(文章)