如何使用NestJS在NodeJS中构建一个类型安全的URL缩短器

引言

URL是Uniform Resource Locator的缩写,它是分配给网络上独特资源的地址。由于URL的唯一性,没有两个资源可以具有相同的URL。

URL的长度和复杂程度各不相同。URL可能像example.com这样简短,也可能像http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk这样冗长。复杂的URL可能不美观,会导致搜索引擎优化(SEO)问题,并对营销计划产生负面影响。URL缩短器可以将长URL映射为较短的URL,并在使用短URL时将用户重定向到原始URL。

在本教程中,您将使用NestJS创建一个URL缩短器。首先,您将在服务中实现URL缩短和重定向逻辑。然后,您将创建路由处理程序来方便缩短和重定向请求。

先决条件

要按照这个教程进行操作,你需要准备以下材料:

  • A local development environment for Node.js version 16 or higher. Follow the How To Install Node.js and Create a Local Development Environment tutorial that suits your system.
  • The NestJS CLI installed on your system, which you will set up in Step 1, and familiarity with NestJS. Review Getting Started with NestJS.
  • Familiarity with TypeScript. You can review the How To Code in TypeScript series to learn more.

第一步 – 准备开发环境

在这个步骤中,您将设置所有开始实施URL缩短逻辑所需的内容。您将全局安装NestJS,生成一个新的NestJS应用样板,安装依赖项,并创建项目的模块、服务和控制器。

首先,如果您以前没有安装过Nest CLI,您需要全局安装它。您将使用这个CLI来生成项目目录和所需的文件。运行以下命令以安装Nest CLI:

  1. npm install -g @nestjs/cli

-g选项将全局在您的系统上安装Nest CLI。

你将会看到以下的输出结果。

  1. Output
    ...
  2. added 249 packages, and audited 250 packages in 3m
  3. 39 packages are looking for funding
  4. run npm fund for details
  5. found 0 vulnerabilities

然后,您将使用新的命令来创建项目并生成必要的样板引导文件。

  1. nest new URL-shortener

您将会看到以下输出结果。

  1. Output
    ...
  2. ⚡ We will scaffold your app in a few seconds..
  3. CREATE url-shortener/.eslintrc.js (631 bytes)
  4. CREATE url-shortener/.prettierrc (51 bytes)
  5. CREATE url-shortener/nest-cli.json (118 bytes)
  6. CREATE url-shortener/package.json (2002 bytes)
  7. CREATE url-shortener/README.md (3339 bytes)
  8. CREATE url-shortener/tsconfig.build.json (97 bytes)
  9. CREATE url-shortener/tsconfig.json (546 bytes)
  10. CREATE url-shortener/src/app.controller.spec.ts (617 bytes)
  11. CREATE url-shortener/src/app.controller.ts (274 bytes)
  12. CREATE url-shortener/src/app.module.ts (249 bytes)
  13. CREATE url-shortener/src/app.service.ts (142 bytes)
  14. CREATE url-shortener/src/main.ts (208 bytes)
  15. CREATE url-shortener/test/app.e2e-spec.ts (630 bytes)
  16. CREATE url-shortener/test/jest-e2e.json (183 bytes)
  17. ? Which package manager would you ❤️ to use? (Use arrow keys)
  18. > npm
  19. yarn
  20. pnpm

选择 npm。

你将会看到以下的输出结果:

  1. Output
    √ Installation in progress... ☕
  2. ? Successfully created project url-shortener
  3. ? Get started with the following commands:
  4. $ cd url-shortener
  5. $ npm run start
  6. Thanks for installing Nest ?
  7. Please consider donating to our open collective
  8. to help us maintain this package.
  9. ? Donate: https://opencollective.com/nest

移动到您创建的项目目录中。

  1. cd url-shortener

你将在这个目录下运行所有后续命令。

Note

注意:当您生成新项目时,NestJS CLI 会创建 app.controller.ts、app.controller.spec.ts 和 app.service.ts 文件。因为您在本教程中不需要它们,您可以选择删除或忽略它们。

接下来,您将安装所需的依赖项。

这个教程需要一些依赖项,你将使用NodeJS的默认包管理器npm进行安装。所需的依赖项包括TypeORM,SQLite,Class-validator,Class-transformer和Nano-ID。

TypeORM是一个对象关系映射工具,它方便了TypeScript应用和关系数据库之间的交互。这个ORM与NestJS无缝集成,得益于NestJS专用的@nestjs/typeorm软件包。你将使用这个依赖项和NestJS的原生typeorm软件包来与一个SQLite数据库进行交互。

运行以下命令来安装TypeORM及其针对NestJS的专用包:

  1. npm install @nestjs/typeorm typeorm

SQLite是一个库,它实现了一个小型、快速、独立的SQL数据库引擎。您将使用此依赖项作为您的数据库来存储和检索缩短的URL。

运行以下命令来安装SQLite:

  1. npm install sqlite3

The class-validator软件包包含了在NestJS中用于数据验证的装饰器。您将使用此依赖项与数据传输对象一起验证发送到应用程序的数据。

运行以下命令来安装 class-validator :

  1. npm install class-validator

class-transformer包允许你将普通的对象转化为类的实例,反之亦可。你将会与class-validator一起使用这个依赖项,因为它无法单独运行。

运行以下命令来安装class-transformer:

执行以下命令以安装class-transformer:

  1. npm install class-transformer

Nano-ID是一个安全、友好URL的唯一字符串ID生成器。您将使用这个依赖项为每个URL资源生成一个唯一的ID。

运行以下命令来安装Nano-ID:

  1. npm install nanoid@^3.0.0

Note

注意:Nano-ID高于3.0.0版本已停用对CommonJS模块的支持。这个问题可能会导致您的应用程序出错,因为Typescript编译器生成的JavaScript代码仍使用CommonJS模块系统。

在安装必要的依赖之后,您将使用Nest CLI生成项目的模块、服务和控制器。模块将组织您的项目,服务将处理URL缩短器的所有逻辑,而控制器将处理路由。

运行以下命令生成你的模块:

  1. nest generate module url

你将会看到以下的输出结果。

Output
CREATE src/url/url.module.ts (80 bytes) UPDATE src/app.module.ts (304 bytes)

然后运行以下命令来生成您的服务:

  1. nest generate service url --no-spec

使用 `–no-spec` 标志告诉 Nest CLI 生成文件时不包括测试文件。在本教程中,您不需要测试文件。

你将会看到下面的输出结果:

Output
CREATE src/url/url.service.ts (87 bytes) UPDATE src/url/url.module.ts (151 bytes)

然后运行以下命令来生成您的控制器:

  1. nest generate controller url --no-spec

你将会看到如下输出结果:

Output
CREATE src/url/url.controller.ts (95 bytes) UPDATE src/url/url.module.ts (233 bytes)

在这一步中,你生成了你的应用程序和大部分开发所需的文件。接下来,你将把你的应用程序连接到一个数据库。

第二步 — 将您的应用程序连接到数据库

在这一步中,您将创建一个实体来模拟数据库中的URL资源。一个实体是包含存储数据的必要属性的文件。您还将创建一个存储库作为应用程序和数据库之间的访问层。

请使用Nano或您偏爱的文本编辑器,在src/url文件夹中创建并打开名为url.entity.ts的文件。

  1. nano src/url/url.entity.ts

这个文件将包含模型数据的实体。

接下来,在你的src/url/url.entity.ts文件中,添加以下的Typescript代码:

src/url/url.entity.ts的翻译:-网址网址.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Url {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    urlCode: string;

    @Column()
    longUrl: string;

    @Column()
    shortUrl: string;
}

首先,从’typeorm’中导入Entity、Column和PrimaryGeneratedColumn装饰器。

代码创建并导出一个带有实体装饰器注释的Url类,该注释标记该类为实体。

每个属性都使用相应的装饰器进行指定和注释:使用PrimaryGeneratedColumn装饰器来标识id属性,使用Column装饰器标识其他属性。PrimaryGeneratedColumn是一个会自动为被注释属性生成值的装饰器。TypeOrm将使用它为每个资源生成一个id。Column是一个将被注释属性作为数据库列添加的装饰器。

应该存储在数据库中的属性包括以下内容:

  • id is the primary key for the database table.
  • urlCode is the unique id generated by the nanoid package and will be used to identify each URL.
  • longUrl is the URL sent to your application to be shortened.
  • shortUrl is the shortened URL.

保存并关闭文件。

接下来,您将在应用程序和数据库之间建立连接。

首先,用Nano或者你喜欢的文本编辑器打开src/app.module.ts文件。

  1. nano src/app.module.ts

然后,将这些突出显示的行添加到文件中。

app.module.ts 源代码文件
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url/url.entity';
import { UrlModule } from './url/url.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'URL.sqlite',
      entities: [Url],
      synchronize: true,
    }),
    UrlModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

首先,从@nestjs/typeorm导入TypeOrmModule和从./url/url.entity导入Url。您可能仍然有与AppController和AppService相关的行。将它们留在文件中不会影响本教程的其余部分。

在import数组中,调用TypeOrmModule的forRoot方法来通过应用程序的所有模块共享连接。forRoot方法接受一个配置对象作为参数。

配置对象包含创建连接的属性。这些属性包括以下内容:

  • The type property denotes the kind of database you are using TypeOrm to interact with. In this case, it is set to ‘sqlite’.
  • The database property denotes the preferred name for your database. In this case, it is set to ‘URL.sqlite’.
  • The entities property is an array of all the entities in your project. In this case, you have just one entity specified as Url inside the array.
  • The synchronize option automatically syncs your database tables with your entity and updates the tables each time you run the code. In this case, it is set to true.

Note

注意:在开发环境中,将同步设置为true是理想的。在生产中,它应该始终设置为false,因为会导致数据丢失。

保存并关闭文件。

接下来,您将创建一个仓库,作为应用程序和数据库之间的访问层。您需要将实体连接到其父模块,这种连接使得Nest和TypeOrm可以自动创建一个仓库。

打开 src/url/url.module.ts。

  1. nano src/url/url.module.ts

在现有文件中,添加高亮的行。

src/url/url.module.ts的中文释义是什么?
import { Module } from '@nestjs/common';
import { UrlService } from './url.service';
import { UrlController } from './url.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Url])],
  providers: [UrlService],
  controllers: [UrlController],
})
export class UrlModule {}

你在Module装饰器内部创建一个imports数组,其中引入了@nestjs/typeorm中的TypeOrmModule和./url.entity中的Url。在imports数组内,你调用了TypeOrmModule的forFeature方法。forFeature方法接受一个实体数组作为参数,所以你传入了Url实体。

保存并关闭文件。

巢和TypeOrm将在幕后创建一个仓库,充当您的服务和数据库之间的访问层。

在这一步骤中,你将你的应用程序连接到了一个数据库。现在你已经准备好来实现URL缩短的逻辑了。

第三步 — 实施服务逻辑

在这一步中,您将使用两种方法来实现您的服务逻辑。第一种方法是shortenUrl,它将包含所有的URL缩短逻辑。第二种方法是redirect,它将包含将用户重定向到原始URL的所有逻辑。您还将创建一个数据传输对象来验证进入应用程序的数据。

提供对存储库的服务访问

在实施这些方法之前,您将授权您的服务访问您的存储库,以便您的应用程序能够读取和写入数据库中的数据。

首先,打开src/url/url.service.ts。

  1. nano src/url/url.service.ts

请将以下突出显示的行添加到现有文件中。

src/url/url.service.ts的内容可以用以下方式进行汉语原生语言的简述(仅提供一种选项):
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}
}

你从typeorm中导入Repository,从@nestjs/typeorm中导入InjectRepository,以及从./url.entity中导入Url。

在你的UrlService类中,你创建了一个构造函数。在构造函数内部,你声明了一个私有变量repo作为参数。然后,你将repo的类型设置为具有Url泛型类型的Repository。你使用InjectRepository装饰器注释了repo变量,并将Url作为参数传递进去。

保存并关闭文件。

您的服务现在可以通过repo变量访问您的存储库。所有的数据库查询和TypeOrm方法都将在其上调用。

接下来,您将创建一个异步方法,名为shortenUrl。该方法将接受一个URL作为参数并返回一个缩短的URL。为了验证传入方法的数据是否有效,您将使用数据传输对象以及class-validator和class-transformer包来验证数据。

创建一个数据传输对象

在创建异步方法之前,您将创建由 shortenUrl 异步方法所需的数据传输对象。数据传输对象是定义数据如何在应用程序之间传输的对象。

首先,在您的URL文件夹中创建一个DTO(数据传输对象)文件夹。

  1. mkdir src/url/dtos

然后,在该文件夹中创建一个名为url.dto.ts的文件。

  1. nano src/url/dtos/url.dto.ts

将以下代码添加到新文件中。

源代码/网址/数据传输对象/url.dto.ts
import { IsString, IsNotEmpty } from 'class-validator';

export class ShortenURLDto {
  @IsString()
  @IsNotEmpty()
  longUrl: string;
}

你从class-validator中导入IsString和IsNotEmpty装饰器。然后,你创建并导出了ShortenURLDto类。在ShortenURLDto类内部,你创建了一个longUrl属性,并赋予了string类型。

你也可以使用IsString和IsNotEmpty装饰器对longUrl属性进行注释。使用这些装饰器对longUrl属性进行注释将确保longUrl始终为字符串且不为空。

保存并关闭文件。

然后,打开你的src/main.ts文件。

  1. nano src/main.ts

将下面的代码段添加到现有的文件中:

主要.ts文件的位置是src/main.ts。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
  await app.listen(3000);
}
bootstrap();

您导入了ValidationPipe,该管道使用class-validator包来对输入您应用程序的所有数据进行验证。

然后,你在应用实例(app)上调用useGlobalPipes方法,并传递一个ValidationPipe实例以及一个选项对象。选项对象中的whitelist属性被设置为true。useGlobalPipes方法在应用级别绑定ValidationPipe,确保所有的路由都受到保护,以防止不正确的数据。将whitelist属性设置为true会剥离已验证(返回的)对象中未在DTO中指定的属性。

保存并关闭文件。

接下来,您将把数据传输对象导入到url.service.ts文件中,并应用到shortenUrl方法中。

创建短链接方法

缩短网址方法将处理大部分的网址缩短逻辑。该方法将接受一个类型为ShortenURLDto的参数url。

首先,打开你的src/url/url.service.ts文件。

  1. nano src/url/url.service.ts

将下面的行添加到文件中的突出显示的部分:

src/url/url.service.ts 的内容可以用以下方式进行翻译:
src/url/url.service.ts 的服务。
import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';
...

首先,你从 @nestjs/common 中导入 NotFoundExeception、BadRequestException 和 UnprocessableEntityException,因为你将在错误处理中使用它们。接下来,你从 nanoid 中导入 {nanoid} 和 class-validator 中的 isURL。isURL 将用于确认提供的 longUrl 是否为有效的 URL。最后,你从 ‘./dtos/url.dto’ 导入 ShortenURLDto 用于数据验证。

然后,在下面的UrlService类构造函数中添加以下内容:

src/url/url.service.ts的中文翻译如下:
...
async shortenUrl(url: ShortenURLDto) {}

然后,将下面的代码添加到你的 shortenUrl 方法中。

src/url/url.service.ts
...
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }

在上面的代码块中,长链接(longUrl)被从URL对象中解构出来。然后,使用isURL方法,会检查longUrl是否是一个有效的URL,并进行验证。

使用nanoid生成一个urlCode。默认情况下,nanoid生成一个独一无二的由二十一个字符组成的字符串。如果要改变默认行为,可以将所需长度作为参数传递进去。在这种情况下,你传入10作为值,因为你希望URL尽可能短。

然后,定义基本URL。 基本URL是您网站地址的一致根目录。 在开发中,它是你的本地主机服务器; 在生产中,它是你的域名。 对于本教程,示例代码使用localhost。

一个try-catch块将承载与数据库进行错误处理的所有代码。

将URL缩短两次可能会导致重复的数据,因此在数据库上运行一个查找查询来判断URL是否存在。如果存在,将返回其短网址;否则,代码会继续将其缩短。如果在数据库中未找到URL记录,就会通过拼接基本URL和URL代码来创建一个短网址。

然后,使用urlCode、longUrl和shortUrl创建一个url实体实例。调用repo的save方法并将实例作为参数传递,将url实例保存到数据库中。然后,返回shortUrl。

最终,如果发生错误,错误将在catch块中记录到控制台,并抛出一个UnprocessableEntityException消息。

这就是你的 url.service.ts 文件现在的样子。

src/url/url.service.ts 的中文原生释义。
import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }
}

保存文件。

在这里,你设置了URL缩短逻辑的第一部分。接下来,你将在你的服务中实现重定向方法。

创建重定向方法

重定向方法将包含将用户重定向到长URL的逻辑。

在src/url/url/service.ts文件中,仍然在UrlService类的底部添加以下代码,以实现重定向方法。

src/url/url.service.ts

源代码/url/url.service.ts

...
  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }

重定向方法接受urlCode作为参数,并尝试在数据库中查找具有匹配urlCode的资源。如果资源存在,则返回该资源;否则,抛出一个NotFoundException错误。

你的完成的 url.service.ts 文件现在看起来是这样的。

src/url/url.service.ts这个文件是URL服务的实现。
import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }

  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }
}

保存并关闭文件。

你的URL缩短逻辑已经完成,包含两个方法:一个用于 URL 缩短,另一个用于将缩短的 URL 重定向到原始 URL。

在接下来的步骤中,您将在您的控制器类中实现这两种方法的路由处理程序。

第四步 – 实施控制器逻辑

在这个步骤中,您将创建两个路由处理程序:一个POST路由处理程序来处理缩短请求,一个GET路由处理程序来处理重定向请求。

在实现控制器类中的路由之前,你必须将服务提供给控制器。

首先,打开你的 src/url/url.controller.ts 文件。

  1. nano src/url/url.controller.ts

将突出显示的行添加到文件中。

src/url/url.controller.ts文件的原始地址/网址控制器.ts。
import { Controller } from '@nestjs/common';
import { UrlService } from './url.service';

@Controller('url')
export class UrlController {
  constructor(private service: UrlService) {}
}

首先,您从./url.service中导入UrlService。然后,在您的控制器类中,您声明一个构造函数并初始化一个私有变量service作为参数。您将service赋予UrlService类型。

Controller 装饰器当前有一个名为’url’的字符串作为参数,这意味着该控制器只能处理发送到localhost/3000/url/路由的请求。由于这样的行为,URL 缩短逻辑中出现了一个错误,缩短的URL包含了一个基本URL(localhost:3000)和一个URL代码(wyt4_uyP-Il),它们组合起来形成了新的URL(localhost:3000/wyt4_uyP-Il)。因此,该控制器无法处理这些请求,缩短链接将返回404 Not Found错误。为了解决这个问题,从Controller装饰器中移除’url’参数,并为每个处理程序实现各自的路由。

在你移除了’url’参数之后,你的UrlController会变成这样:

src/url/url.controller.ts的中文原生释义如下:源码/url/url.controller.ts
@Controller()
export class UrlController {
  constructor(private service: UrlService) {}
}

仍然在src / url / url.controller.ts文件中,将高亮项添加到导入语句中。

src/url/url.controller.ts的内容进行改写如下,请选择其中一种版本:

src/url/url.controller.ts的文件进行重新编写如下:

import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

你从@nestjs/common中导入了Body、Get、Param、Post和Res,以及从./dtos/url.dto中导入了ShortenURLDto。当你在这个文件中添加时,装饰器将进一步定义。

然后在构造函数下面将以下代码添加到UrlController中以定义POST路由处理程序:

src/url/url.controller.ts的中文本地化版本
...
  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

你创建一个名为shortenUrl的方法,它带有一个类型为ShortenURLDto的url参数。通过使用Body装饰器对url进行注释,可以从请求对象中提取body对象并将其值填充到url变量中。

然后,你使用Post装饰器对整个方法进行注解,并将’shorten’作为参数传递。该处理程序将处理发送到localhost:<端口>/shorten的所有Post请求。然后,你调用服务上的shortenUrl方法,并将url作为参数传递。

接下来,在POST路由下方添加以下代码来定义用于重定向的GET路由处理程序:

src/url/url.controller.ts的内容进行中文翻译:
...
  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }

你创建了一个带有两个参数的重定向方法:一个被Res装饰器注释的res,一个被Param装饰器注释的code。Res装饰器将其注释的类转换为Express响应对象,允许你使用特定库的命令,如Express的重定向方法。Param装饰器从req对象中提取params属性,并将其值填充到被装饰的参数中。

你使用Get修饰符对重定向方法进行注释,并传递一个通配符参数’:code’。然后你将’code’作为参数传递给Param修饰符。

然后您在服务上调用重定向方法,等待结果,并将其存储在变量url中。

最后,你返回 res.redirect() 并传递 url.longUrl 作为参数。这个方法将处理 GET 请求到 localhost:/code,或者你的缩短的网址。

你的src/url/url.controller.ts文件现在将看起来像这样:

src/url/url.controller.ts的含义是什么?
import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

@Controller()
export class UrlController {
  constructor(private service: UrlService) {}

  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }
}

保存并关闭文件。

现在,您已经定义了您的POST和GET路由处理程序,您的URL缩短器功能已经完全可用。在下一步中,您将对其进行测试。

步骤五 — 测试URL缩短服务

在这个步骤中,您将测试在之前步骤中定义的URL缩短工具。

首先,通过运行程序来启动你的应用。

  1. npm run start

您将会看到如下输出:

Output
[Nest] 12640 - 06/08/2022, 16:20:04 LOG [NestFactory] Starting Nest application... [Nest] 12640 - 06/08/2022, 16:20:07 LOG [InstanceLoader] AppModule dependencies initialized +2942ms [Nest] 12640 - 06/08/2022, 16:20:07 LOG [InstanceLoader] TypeOrmModule dependencies initialized +1ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +257ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] TypeOrmModule dependencies initialized +2ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] UrlModule dependencies initialized +4ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RoutesResolver] UrlController {/}: +68ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RouterExplorer] Mapped {/shorten, POST} route +7ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RouterExplorer] Mapped {/:code, GET} route +2ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [NestApplication] Nest application successfully started +7ms

打开一个新的终端,使用curl或您喜欢的API测试工具,向http://localhost:3000/shorten发出POST请求,使用下面的数据或任意您选择的数据。有关使用curl的更多信息,请参阅本教程中的描述。

运行这个命令进行一个示例的POST请求。

  1. curl -d "{\"longUrl\":\"http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk\"}" -H "Content-Type: application/json" http://localhost:3000/shorten

使用-d标志注册HTTP POST请求数据,并使用-H标志设置HTTP请求的头部。运行此命令将向您的应用程序发送长网址并返回一个缩短的URL。

你将收到一个短链接作为回复,例如下面这个例子:

http://localhost:3000/MWBNHDiloW

最后,复制短网址并将链接粘贴到浏览器中,然后按下回车键。您将被重定向到原始资源。

结论

在本文中,您使用NestJS创建了一个URL缩短工具。如果您添加一个前端界面,就可以将其部署供公众使用。您可以在Github上查看完整的项目。

NestJS 提供了类型安全和架构,使您的应用程序更加安全、可维护和可扩展。想要了解更多信息,请访问 NestJS 的官方文档。

发表回复 0

Your email address will not be published. Required fields are marked *