NestJS实战:构建高性能类型安全的Node.js短链接服务
引言
URL是Uniform Resource Locator的缩写,它是分配给网络上独特资源的地址。由于URL的唯一性,没有两个资源可以具有相同的URL。
URL的长度和复杂程度各不相同。URL可能像example.com
这样简短,也可能像http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk
这样冗长。复杂的URL可能不美观,会导致搜索引擎优化(SEO)问题,并对营销计划产生负面影响。URL缩短器可以将长URL映射为较短的URL,并在使用短URL时将用户重定向到原始URL。
在本教程中,您将使用NestJS创建一个URL缩短器。首先,您将在服务中实现URL缩短和重定向逻辑。然后,您将创建路由处理程序来方便缩短和重定向请求。
先决条件
要按照本教程进行操作,您需要准备以下材料:
- Node.js 16或更高版本的本地开发环境。请根据您的系统,参考《如何安装Node.js并创建本地开发环境》教程进行设置。
- 系统上已安装NestJS CLI(您将在第一步中进行设置),并熟悉NestJS。请查阅《NestJS入门指南》。
- 熟悉TypeScript。您可以查阅《TypeScript编程系列》以了解更多信息。
第一步 – 准备开发环境
在这个步骤中,您将设置所有开始实施URL缩短逻辑所需的内容。您将全局安装NestJS CLI,生成一个新的NestJS应用样板,安装依赖项,并创建项目的模块、服务和控制器。
首先,如果您以前没有安装过Nest CLI,您需要全局安装它。您将使用这个CLI来生成项目目录和所需的文件。运行以下命令以安装Nest CLI:
- npm install -g @nestjs/cli
-g
选项将全局在您的系统上安装Nest CLI。
您将会看到以下输出结果:
- 输出...
- added 249 packages, and audited 250 packages in 3m
- 39 packages are looking for funding
- run npm fund for details
- found 0 vulnerabilities
然后,您将使用新安装的命令来创建项目并生成必要的样板引导文件:
- nest new URL-shortener
您将会看到以下输出结果:
- 输出
这是文章《如何使用NestJS在NodeJS中构建一个类型安全的URL缩短器》的第2部分(共8部分)。
- ⚡ 正在为您搭建应用程序...
- 创建 url-shortener/.eslintrc.js (631 字节)
- 创建 url-shortener/.prettierrc (51 字节)
- 创建 url-shortener/nest-cli.json (118 字节)
- 创建 url-shortener/package.json (2002 字节)
- 创建 url-shortener/README.md (3339 字节)
- 创建 url-shortener/tsconfig.build.json (97 字节)
- 创建 url-shortener/tsconfig.json (546 字节)
- 创建 url-shortener/src/app.controller.spec.ts (617 字节)
- 创建 url-shortener/src/app.controller.ts (274 字节)
- 创建 url-shortener/src/app.module.ts (249 字节)
- 创建 url-shortener/src/app.service.ts (142 字节)
- 创建 url-shortener/src/main.ts (208 字节)
- 创建 url-shortener/test/app.e2e-spec.ts (630 字节)
- 创建 url-shortener/test/jest-e2e.json (183 字节)
- ? 您希望使用哪个包管理器? (使用方向键选择)
- > npm
- yarn
- pnpm
选择 npm。
您将看到以下输出结果:
- 输出√ 安装进行中... ☕
- ? 成功创建项目 url-shortener
- ? 通过以下命令开始:
- $ cd url-shortener
- $ npm run start
- 感谢您安装 Nest ?
- 请考虑向我们的开放集体捐款
- 以帮助我们维护此软件包。
- ? 捐款:https://opencollective.com/nest
移动到您创建的项目目录中。
- cd url-shortener
您将在这个目录下运行所有后续命令。
注意:
当您生成新项目时,NestJS CLI 会创建 app.controller.ts
、app.controller.spec.ts
和 app.service.ts
文件。因为您在本教程中不需要它们,您可以选择删除或忽略它们。
接下来,您将安装所需的依赖项。
本教程需要一些依赖项,您将使用 Node.js 的默认包管理器 npm 进行安装。所需的依赖项包括 TypeORM、SQLite、Class-validator、Class-transformer 和 Nano-ID。
TypeORM是一个对象关系映射(ORM)工具,它极大地简化了TypeScript应用与关系型数据库之间的交互。此ORM与NestJS实现了无缝集成,这得益于NestJS专用的@nestjs/typeorm
软件包。您将使用此依赖项以及NestJS原生的typeorm
软件包来与SQLite数据库进行交互。
运行以下命令来安装TypeORM及其针对NestJS的专用包:
- npm install @nestjs/typeorm typeorm
SQLite是一个轻量级、快速且独立的SQL数据库引擎库。您将使用此依赖项作为数据库,用于存储和检索缩短后的URL。
运行以下命令来安装SQLite:
- npm install sqlite3
class-validator
软件包包含了在NestJS中用于数据验证的装饰器。您将结合数据传输对象(DTO)使用此依赖项,以验证发送到应用程序的数据。
运行以下命令来安装class-validator
:
- npm install class-validator
class-transformer
包允许您将普通对象转换为类的实例,反之亦然。您将与class-validator
一起使用此依赖项,因为class-validator
无法单独运行。
执行以下命令以安装class-transformer
:
- npm install class-transformer
Nano-ID是一个安全、对URL友好的唯一字符串ID生成器。您将使用此依赖项为每个URL资源生成一个唯一的ID。
运行以下命令来安装Nano-ID:
- npm install nanoid@^3.0.0
注意:Nano-ID高于3.0.0版本已停用对CommonJS模块的支持。这个问题可能会导致您的应用程序出错,因为TypeScript编译器生成的JavaScript代码仍使用CommonJS模块系统。
在安装必要的依赖项之后,您将使用Nest CLI生成项目的模块、服务和控制器。模块将组织您的项目结构,服务将处理URL缩短器的所有业务逻辑,而控制器将负责处理路由。
运行以下命令生成您的模块:
- nest generate module url
您将看到以下输出结果:
输出CREATE src/url/url.module.ts (80 bytes)
UPDATE src/app.module.ts (304 bytes)
然后运行以下命令来生成您的服务:
- nest generate service url --no-spec
使用--no-spec
标志可以告诉Nest CLI在生成文件时不包含测试文件。在本教程中,您不需要测试文件。
您将看到下面的输出结果:
输出CREATE src/url/url.service.ts (87 bytes)
UPDATE src/url/url.module.ts (151 bytes)
然后运行以下命令来生成您的控制器:
- nest generate controller url --no-spec
您将看到如下输出结果:
输出CREATE src/url/url.controller.ts (95 bytes)
UPDATE src/url/url.module.ts (233 bytes)
在这一步中,您已经为应用程序生成了大部分开发所需的文件。接下来,您将把应用程序连接到一个数据库。
第二步 — 将您的应用程序连接到数据库
在这一步中,您将创建一个实体来模拟数据库中的URL资源。实体是包含存储数据所需属性的文件。您还将创建一个存储库作为应用程序和数据库之间的访问层。
请使用Nano或您偏好的文本编辑器,在src/url
文件夹中创建并打开名为url.entity.ts
的文件。
- nano src/url/url.entity.ts
这个文件将包含模型数据的实体定义。
接下来,在您的src/url/url.entity.ts
文件中,添加以下的TypeScript代码:
src/url/url.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
装饰器。
代码创建并导出一个带有@Entity()
装饰器注释的Url
类,该注释将此类标记为TypeORM实体。
每个属性都使用相应的装饰器进行指定和注释:使用@PrimaryGeneratedColumn()
装饰器来标识id
属性,使用@Column()
装饰器标识其他属性。@PrimaryGeneratedColumn()
是一个会自动为被注释属性生成值的装饰器。TypeORM将使用它为每个资源生成一个ID。@Column()
是一个将被注释属性作为数据库列添加的装饰器。
应该存储在数据库中的属性包括以下内容:
id
是数据库表的主键。urlCode
是由Nano-ID包生成的唯一ID,将用于标识每个URL。longUrl
是发送到您的应用程序进行缩短的原始URL。shortUrl
是缩短后的URL。
保存并关闭文件。
接下来,您将在应用程序和数据库之间建立连接。
首先,用Nano或者您喜欢的文本编辑器打开src/app.module.ts
文件。
- 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
相关的代码行,但它们不会影响本教程的其余部分。
在imports
数组中,调用TypeOrmModule
的forRoot
方法,以便在应用程序的所有模块中共享数据库连接。forRoot
方法接受一个配置对象作为参数。
该配置对象包含用于创建数据库连接的属性,具体如下:
type
属性:指定您使用TypeORM交互的数据库类型。在此示例中,设置为'sqlite'
。database
属性:指定您数据库的首选名称。在此示例中,设置为'URL.sqlite'
。entities
属性:一个包含项目中所有实体的数组。在此示例中,数组中只指定了一个Url
实体。synchronize
属性:该选项会在每次运行代码时自动将数据库表与您的实体同步并更新表结构。在此示例中,设置为true
。
注意:
synchronize
设置为true
是理想的选择。但在生产环境中,它应始终设置为false
,以避免潜在的数据丢失。保存并关闭文件。
接下来,您将创建一个仓库(Repository),作为应用程序与数据库之间的访问层。您需要将实体连接到其父模块,这种连接使得Nest和TypeORM能够自动创建一个仓库。
打开 src/url/url.module.ts
。
- 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
实体。
保存并关闭文件。
Nest和TypeORM将在幕后为您创建一个仓库,作为您的服务与数据库之间的访问层。
在这一步骤中,您已将应用程序连接到数据库。现在,您已准备好实现URL缩短的逻辑。
第三步 — 实现服务逻辑
在这一步中,您将使用两种方法来实现您的服务逻辑。第一种方法是shortenUrl
,它将包含所有URL缩短逻辑。第二种方法是redirect
,它将包含将用户重定向到原始URL的所有逻辑。您还将创建一个数据传输对象(DTO)来验证进入应用程序的数据。
为服务提供对仓库的访问
在实现这些方法之前,您将授权您的服务访问您的仓库,以便您的应用程序能够读取和写入数据库中的数据。
首先,打开src/url/url.service.ts
。
- 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。为了验证传入方法的数据是否有效,您将使用数据传输对象(DTO)以及class-validator
和class-transformer
包来验证数据。
创建一个数据传输对象(DTO)
在创建异步方法之前,您将创建shortenUrl
异步方法所需的数据传输对象(DTO)。数据传输对象是定义数据如何在应用程序之间传输的对象。
首先,在您的URL文件夹中创建一个dtos
(数据传输对象)文件夹。
- mkdir src/url/dtos
然后,在该文件夹中创建一个名为url.dto.ts
的文件。
- nano src/url/dtos/url.dto.ts
将以下代码添加到新文件中:
src/url/dtos/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
文件。
- nano src/main.ts
将下面的代码段添加到现有文件中:
src/main.ts
的内容如下:
这是文章《如何使用NestJS在NodeJS中构建一个类型安全的URL缩短器》的第5部分(共8部分)。
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
文件。
- nano 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
中导入 NotFoundException
、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;
// 检查长链接是否为有效URL
if (!isURL(longUrl)) {
throw new BadRequestException('字符串必须是有效的URL');
}
const urlCode = nanoid(10);
const baseURL = 'http://localhost:3000';
try {
// 检查URL是否已被缩短
let url = await this.repo.findOneBy({ longUrl });
// 如果存在,则直接返回其短链接
if (url) return url.shortUrl;
// 如果不存在,则进行缩短
const shortUrl = `${baseURL}/${urlCode}`;
// 将新记录添加到数据库
url = this.repo.create({
urlCode,
longUrl,
shortUrl,
});
this.repo.save(url);
return url.shortUrl;
} catch (error) {
console.log(error);
throw new UnprocessableEntityException('服务器错误');
}
在上述代码块中,长链接(longUrl
)从url
对象中解构出来。随后,使用isURL
方法对longUrl
进行有效性检查和验证。
接着,使用nanoid
生成一个urlCode
。默认情况下,nanoid
会生成一个由21个字符组成的唯一字符串。若要更改此默认行为,可将所需长度作为参数传入。在此示例中,我们传入了10
,以使URL尽可能短。
然后,定义了基础URL(baseURL
)。基础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;
// 检查长链接是否为有效URL
if (!isURL(longUrl)) {
throw new BadRequestException('字符串必须是有效的URL');
}
const urlCode = nanoid(10);
const baseURL = 'http://localhost:3000';
try {
// 检查URL是否已被缩短
let url = await this.repo.findOneBy({ longUrl });
// 如果存在,则直接返回其短链接
if (url) return url.shortUrl;
// 如果不存在,则进行缩短
const shortUrl = `${baseURL}/${urlCode}`;
// 将新记录添加到数据库
url = this.repo.create({
urlCode,
longUrl,
shortUrl,
});
this.repo.save(url);
return url.shortUrl;
} catch (error) {
console.log(error);
throw new UnprocessableEntityException('服务器错误');
}
}
}
保存文件。
至此,您已设置了URL缩短逻辑的第一部分。接下来,您将在服务中实现重定向方法。
创建重定向方法
重定向方法将包含将用户重定向到长URL的逻辑。
在src/url/url.service.ts
文件中,继续在UrlService
类的底部添加以下代码,以实现重定向方法:
src/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('资源未找到');
}
}
重定向方法接受urlCode
作为参数,并尝试在数据库中查找具有匹配urlCode
的资源。如果资源存在,则返回该资源;否则,将抛出一个NotFoundException
错误。
您最终完成的url.service.ts
文件现在看起来是这样的:
src/url/url.service.ts
这个文件是URL服务的实现。这是文章《如何使用NestJS在NodeJS中构建一个类型安全的URL缩短器》的第7部分(共8部分)。
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;
// 检查 longUrl 是否为有效 URL
if (!isURL(longUrl)) {
throw new BadRequestException('字符串必须是有效的URL');
}
const urlCode = nanoid(10);
const baseURL = 'http://localhost:3000';
try {
// 检查 URL 是否已被缩短
let url = await this.repo.findOneBy({ longUrl });
// 如果存在,则直接返回
if (url) return url.shortUrl;
// 如果不存在,则进行缩短
const shortUrl = `${baseURL}/${urlCode}`;
// 将新记录添加到数据库
url = this.repo.create({
urlCode,
longUrl,
shortUrl,
});
this.repo.save(url);
return url.shortUrl;
} catch (error) {
console.log(error);
throw new UnprocessableEntityException('服务器错误');
}
}
async redirect(urlCode: string) {
try {
const url = await this.repo.findOneBy({ urlCode });
if (url) return url;
} catch (error) {
console.log(error);
throw new NotFoundException('资源未找到');
}
}
}
保存并关闭文件。
至此,您的URL缩短逻辑已完成,它包含两个核心方法:一个用于URL缩短,另一个用于将缩短的URL重定向到原始URL。
接下来,您将在控制器类中实现这两种方法的路由处理程序。
第四步 – 实施控制器逻辑
在本步骤中,您将创建两个路由处理程序:一个POST路由处理程序用于处理缩短请求,一个GET路由处理程序用于处理重定向请求。
在控制器类中实现路由之前,您必须将服务注入到控制器中。
首先,打开您的 src/url/url.controller.ts
文件。
- nano src/url/url.controller.ts
将以下高亮行添加到文件中:
src/url/url.controller.ts
文件内容:
import { Controller } from '@nestjs/common';
import { UrlService } from './url.service';
@Controller('url')
export class UrlController {
constructor(private service: UrlService) {}
}
首先,您从 ./url.service
导入 UrlService
。然后,在您的控制器类中,您声明一个构造函数并初始化一个私有变量 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
文件内容:
@Controller()
export class UrlController {
constructor(private service: UrlService) {}
}
仍在 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
进行注释,可以从请求对象中提取请求体并将其值填充到 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);
}
您创建了一个带有两个参数的 redirect
方法:一个被 @Res
装饰器注释的 res
,一个被 @Param
装饰器注释的 code
。@Res
装饰器将其注释的类转换为Express响应对象,允许您使用特定库的命令,例如Express的重定向方法。@Param
装饰器从请求对象中提取参数属性,并将其值填充到被装饰的参数中。
您使用 @Get
装饰器对 redirect
方法进行注释,并传递一个通配符参数 ':code'
。然后您将 'code'
作为参数传递给 @Param
装饰器。
接着,您在服务上调用 redirect
方法,等待结果,并将其存储在变量 url
中。
最后,您返回 res.redirect()
并传递 url.longUrl
作为参数。这个方法将处理发送到 localhost:<端口>/code
的GET请求,也就是您的缩短网址。
您的 src/url/url.controller.ts
文件现在将看起来像这样:
这是文章《如何使用NestJS在NodeJS中构建一个类型安全的URL缩短器》的第8部分(共8部分)。
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缩短工具。
首先,通过运行程序来启动您的应用。
- 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请求。
- 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 的官方文档。