目录

概述

设置Nest.js演示

清单1.recipe.controller.ts

使用控制器进行路由

清单2.将新控制器添加到模块

清单3./src/recipes.service.ts

将服务添加到模块

清单4.将服务添加到模块

使用服务

清单 5.在RecipesController中使用服务

创建POST终结点

清单6.一个简单的AuthGuard

清单7.使用app.module.ts注册防护

清单8.保护POST端点

清单9./src/recipes.service.ts addRecipe方法

清单10.测试新的终结点和authguard

将Nest与TypeScript配合使用

清单11. Recipe模型

清单12.强类型POST方法

清单13.强类型POST方法

结论


不要与Next.js混淆,Nest.js是一种更新且独特的JavaScript服务器技术方法。它需要一个熟悉的服务器,如ExpressFastify,并分层一些有用的抽象,这些抽象旨在授权和简化更高级别的应用程序设计。由于其独特的编程范式、一阶TypeScript支持和依赖注入等内置功能的融合,Nest.js在过去几年中稳步增长。

Nest.js是对JavaScript生态系统的一个有趣的贡献,非常值得您关注。在使用服务器端JavaScriptTypeScript时,这是一个很好的工具。

概述

在本文中,我们将对Nest.js进行旋风式浏览,示例包括路由、控制器、生产者(依赖项注入)和防护身份验证。您还将了解Nest.js模块系统。

我们的示例是用于管理意大利面食谱列表的应用程序。我们将包括一个管理实际数据集的依赖注入服务,以及一个RESTful API,我们可以使用它来列出所有配方或按ID恢复单个配方。我们还将设置一个简单的经过身份验证的PUT端点,用于添加新配方。

让我们从搭建一个新项目开始。一旦有了这些,我们就可以深入研究这些示例。

设置Nest.js演示

我们可以使用Nest.js命令行界面来设置快速的应用程序布局,首先使用以下命令全局安装Nest$ npm install -g @nestjs/cli。除了该create命令之外,nestjs还包括有用的功能,例如generate共享可重用的设计。全局安装使我们能够访问它以及更多。

现在,我们可以使用$ nest new iw-nest创建一个新的应用程序。您可以选择所需的任何包管理器(npmyarnpnpm)。对于此演示,我将使用pnpm。无论如何,过程都是一样的。

切换到新目录/iw-nest,然后使用以下命令启动开发服务器:$ pnpm run start。您可以通过访问localhost:3000来验证应用程序是否正在运行,您应该会在其中看到“HelloWorld消息。此消息来自iw-nest/src/app.controller.ts。如果你查看该文件,你可以看到它正在使用注入的服务。让我们创建一个新的控制器(src/recipes.controller.ts),它返回一个配方列表,如清单1所示。

清单1.recipe.controller.ts

import { Controller, Get, Inject } from '@nestjs/common';

@Controller('recipes')
export class RecipesController {
  @Get()
  getRecipes() {
    return '[{"name":"Ravioli"}]';
  }
}

使用控制器进行路由

清单1让我们了解了Nest.js中路由的基础知识。您可以看到,我们使用@Controller(‘recipes’)注解将类定义为具有/recipes路由的控制器。对getRecipes()方法进行批注以使用@Get()处理GET方法。

目前,此控制器只是将/recipes GET映射到硬编码的响应字符串。在Nest.js提供服务之前,我们需要向模块注册新的控制器。模块是Nest中的另一个重要概念,用于帮助组织应用程序代码。在我们的例子中,我们需要打开/src/app.module.ts并添加控制器,如清单2、所示。

清单2.将新控制器添加到模块

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
// Our new controller:
import { RecipesController } from './recipes.controller'; 

@Module({
  imports: [],
  controllers: [AppController,RecipesController],
  providers: [AppService],
})
export class AppModule {}

Nest.js中的依赖注入框架让人想起Java生态系统中的Spring。仅内置依赖注入就值得考虑Nest.js,即使没有其他花里胡哨的东西。

我们将定义一个服务提供者并将其连接到我们的控制器。这是将应用程序组织成层的干净方法。您可以在清单3中看到我们的新服务类/src/recipes.service.ts

清单3./src/recipes.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class RecipesService {
  private readonly recipes = [
    {
      name: 'Ravioli',
      ingredients: ['pasta', 'cheese', 'tomato sauce'],
    },
    {
      name: 'Lasagna',
      ingredients: ['pasta', 'meat sauce', 'cheese'],
    },
    {
      name: 'Spaghetti',
      ingredients: ['pasta', 'tomato sauce'],
    },
  ];

  getRecipes() {
    return this.recipes;
  }
}

将服务添加到模块

要使用此服务提供程序,我们还需要将其添加到app.module.ts文件中,如清单4所示。

清单4.将服务添加到模块

@Module({
  imports: [],
  controllers: [AppController,RecipesController],
  providers: [AppService, RecipesService] // add the service 
})

模块是组织应用程序的好方法。它们可以充当逻辑分组机制,提供分层结构,其中最基本的模块被明确定义,其他模块依赖于它们。

使用服务

现在我们可以在RecipesController中使用服务,如清单5所示。如果你是依赖注入的新手,这似乎是很多额外的工作。但是,随着系统的增长,以标准化的方式在应用程序范围内定义和使用类的能力对应用程序体系结构来说是一个真正的福音。

清单 5.RecipesController中使用服务

import { Controller, Get, Inject } from '@nestjs/common';
import { RecipesService } from './recipes.service';

@Controller('recipes')
export class RecipesController {
  @Inject()
  private readonly recipesService: RecipesService;
  @Get()
  getRecipes() {
    return this.recipesService.getRecipes();
  }
}

从本质上讲,我们导入RecipesService类,然后为了获得对它的引用,我们在recipesService成员上使用@Inject()注释。注入系统会根据类型将其连接到RecipesService类的实例。默认情况下,注入的服务是Nest中的单例,因此所有客户端类都将获得对同一实例的引用。可以使用服务的其他“scopes”来微调它们的实例化方式。除了构造函数注入,Nest还支持基于属性的注入。

现在,如果运行应用程序并转到localhost:3000/recipes,你将看到服务中recipes数组的JSON输出。

创建POST终结点

现在,让我们添加一个新的POST端点,以允许用户添加配方。我们将使用Nest.js中所谓的防护装置,通过最简单的身份验证来保护它。防护人员坐在控制器前面,负责确定请求的路由方式。您可以在清单6中看到我们的简单防护。现在,它只是检查请求中是否有身份验证标头。

清单6.一个简单的AuthGuard

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    // Simple authentication logic
    const request = context.switchToHttp().getRequest();
    const authHeader = request.headers.authorization;
    return authHeader === 'Bearer secret-token';
  }
}

接下来,在模块中注册防护,如清单7所示。

清单7.使用app.module.ts注册防护

// ...
import { AuthGuard } from './auth.guard';

@Module({
  imports: [],
  controllers: [AppController, RecipesController],
  providers: [AppService, RecipesService, AuthGuard], 
})
export class AppModule {}

现在,我们可以使用新的guard服务来保护我们的POST端点,如清单8所示。请注意新的导入。

清单8.保护POST端点

import { Controller, Get, Inject, Post, Body, UseGuards } from '@nestjs/common';
import { RecipesService } from './recipes.service';
import { AuthGuard } from "./auth.guard";

@Controller('recipes')
export class RecipesController {
  @Inject()
  private readonly recipesService: RecipesService;
  @Get()
  getRecipes() {
    return this.recipesService.getRecipes();
  }
  @Post()
  @UseGuards(AuthGuard)
  addRecipe(@Body() recipe: any) {
    return this.recipesService.addRecipe(recipe);
  }
}

请注意,该@UserGuards注解用于将新的防护应用于addRecipe()方法,该方法也被指定为具有@Post注解的POST端点。Nest将负责为我们实例化和应用防护到端点。

我们还向服务中添加了一个非常简单的addRecipe()方法,如清单9所示。

清单9./src/recipes.service.ts addRecipe方法

addRecipe(recipe: any) {
    this.recipes.push(recipe);
    return recipe;
  }

现在,我们可以使用几个CURL请求来测试身份验证和端点,如清单10所示。

清单10.测试新的终结点和authguard

$ curl -X POST -H "Authorization: Bearer secret-token" -H "Content-Type: application/json" -d '{"name": "Carbonara", "ingredients": ["pasta", "eggs", "bacon"]}' http://localhost:3000/recipes

{"name":"Carbonara","ingredients":["pasta","eggs","bacon"]}

$ curl -X POST -H "Content-Type: application/json" -d '{"name": "Carbonara", "ingredients": ["pasta", "eggs", "bacon"]}' http://localhost:3000/recipes

{"message":"Forbidden resource","error":"Forbidden","statusCode":403}

您可以看到授权正在起作用,因为只允许持有Bearer标头的请求通过。

NestTypeScript配合使用

到目前为止,我们只使用了一个JavaScript对象。在TypeScript世界中,创建一个Recipe模型对象并将其用作值对象来浏览信息是很常见的。例如,我们可以创建Recipe类(清单11)并在addRecipe方法中使用它(清单12)。

清单11. Recipe模型

export class Recipe {
  constructor(public name: string, public ingredients: string[]) {}

  getName(): string {
    return this.name;
  }

  getIngredients(): string[] {
    return this.ingredients;
  }
}

最后,您可以将addRecipe()POST方法设置为强类型,Next将自动为我们填充模型对象:

清单12.强类型POST方法

import { Injectable } from '@nestjs/common';
import { Recipe } from './recipe.model';

@Injectable()
export class RecipesService {
  private readonly recipes: Recipe[] = [
    new Recipe('Ravioli', ['pasta', 'cheese', 'tomato sauce']),
    new Recipe('Lasagna', ['pasta', 'meat sauce', 'cheese']),
    new Recipe('Spaghetti', ['pasta', 'tomato sauce']),
  ];

  getRecipes(): Recipe[] {
    return this.recipes;
  }

  addRecipe(recipe: Recipe): Recipe {
    this.recipes.push(recipe);
    return recipe;
  }
}

然后,您可以将addRecipe()POST方法设置为强类型,Nest将自动为我们填充模型对象,如清单13所示。

清单13.强类型POST方法

// ...
import { Recipe } from './recipe.model';

@Controller('recipes')
export class RecipesController {
  // ...
  @Post()
  @UseGuards(AuthGuard)
  addRecipe(@Body() recipe: Recipe) {
    const newRecipe = new Recipe(recipe.name, recipe.ingredients);
    return this.recipesService.addRecipe(newRecipe);
  }
}

结论

JavaScriptduck类型和TypeScript的强类型之间的选择实际上取决于您、团队或组织决定使用什么。JavaScript为您提供了开发速度和灵活性,而TypeScript为您提供了结构和更多工具支持。

还值得注意的是,Nest.js包含响应式编程,您可以从方法和端点返回promise。更重要的是,您可以返回RxJS Observable。这为您提供了将应用程序与异步数据流连接在一起的强大选项。

虽然我们只是触及了它能做什么的表面,但很明显,Nest.js是一个经过深思熟虑且功能强大的平台,用于构建Node服务器。它兑现了在Express之上建立更高层次层的承诺,以改进架构和设计支持。如果你想构建服务器端的JavaScript,尤其是TypeScript应用程序,Nest是一个不错的选择。

https://www.infoworld.com/article/3703212/intro-to-nestjs-the-higher-order-javascript-and-typescript-server.html

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐