NestJS
NestJS 是一种基于 TypeScript 的后端框架,它结合了 Express 和 Angular 的优点,提供了一种现代化、模块化和可扩展的开发方式。NestJS 的主要目标是提供一个高效、可维护和可测试的服务端应用程序框架,同时提供了许多有用的功能和工具,如依赖注入、模块化体系结构、中间件、管道、拦截器、异常过滤器等。
NestJS 的主要特点包括:
基于 TypeScript:NestJS 是一种基于 TypeScript 的框架,支持静态类型检查和强类型编程,提高了代码的可维护性和可读性。
模块化体系结构:NestJS 使用模块化体系结构,将应用程序拆分为多个模块,每个模块可以独立开发、测试和部署,提高了代码的可扩展性和可维护性。
依赖注入:NestJS 支持依赖注入,通过注入依赖项来实现松耦合的架构设计,提高了代码的可测试性和可维护性。
中间件和管道:NestJS 提供了中间件和管道机制,可以在请求和响应之间添加额外的逻辑,如身份验证、日志记录、异常处理等,提高了应用程序的可靠性和安全性。
异常过滤器:NestJS 提供了异常过滤器,可以捕获应用程序中的异常并进行处理,提高了应用程序的稳定性和可靠性。
NestJS 可以用于构建各种类型的后端服务,如 RESTful API、WebSocket 服务、微服务等。NestJS 社区提供了许多有用的扩展和插件,如 Swagger UI、TypeORM、GraphQL 等,可以帮助开发人员更加高效地构建和管理后端服务。
如果您想成为一名后端开发人员,NestJS 是一个非常有用的框架,需要掌握 TypeScript 的基础知识和 NestJS 的开发方式,了解常用的模块和工具,如路由、控制器、服务、中间件、管道、拦截器等。掌握 NestJS 可以帮助您更加高效和灵活地构建和管理后端服务,为自己的职业发展和个人成长打下坚实的基础。
![NestJS](https://cdn.portal.levenx.com/levenx-world/lRmVPCNHBhbAuKlM.jpg)
查看更多相关内容
解释Nest.js ExecutionContext的作用。
Nest.js 中的 `ExecutionContext` 是一个非常重要的类,它提供了当前请求的详细上下文信息。这对于执行通用的任务和操作非常有用,特别是在守卫(Guards)、拦截器(Interceptors)、过滤器(Filters)等功能中。
### ExecutionContext 的核心作用
1. **提供当前请求的详细信息:** 它封装了请求对象,例如 HTTP 请求的 headers、body、params、query 等,使得开发者可以在任何需要的地方访问这些信息。
2. **跨平台的特性:** Nest.js 支持多种网络框架如 Express 和 Fastify。ExecutionContext 能够抽象这些细节,让开发者编写的代码更具通用性,而不需要担心底层网络框架的具体实现。
### 实例应用
假设我们正在开发一个 API,需要在每个请求中验证用户的权限。我们可以使用守卫(Guard)来检查用户的权限。在这个守卫中,我们会利用 `ExecutionContext` 来访问当前请求的用户信息,并根据这些信息做出相应的权限验证。
```typescript
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
if (!requiredRoles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
```
在上面的代码中,`ExecutionContext` 被用来切换到 HTTP 请求,并从请求中获取用户信息。这样我们就可以根据用户的角色和所需的角色进行比较,判断是否允许访问。
### 总结
通过 `ExecutionContext`,Nest.js 提供了一种非常灵活和强大的方式来处理请求的上下文,使得开发者可以轻松地实现各种中间件功能,如安全验证、日志记录、异常处理等,而不必深入到底层的网络框架细节。这样不仅提高了代码的可维护性,也增强了其通用性和可扩展性。
阅读 9 · 7月15日 10:19
NestJs 中如何捕获 Typeorm 的事务错误?
在NestJS中结合Typeorm使用事务时,我们可以捕获事务错误并进行相应的处理。一般来说,有几种方法可以捕获并处理这些错误:
### 使用`try/catch`块
在Typeorm中,你可能会使用`queryRunner`来创建和管理事务。在这种情况下,可以使用`try/catch`块来捕获事务中发生的任何错误。
例如:
```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Connection } from 'typeorm';
import { YourEntity } from './entities/your.entity';
@Injectable()
export class YourService {
constructor(
@InjectRepository(YourEntity)
private yourEntityRepository: Repository<YourEntity>,
private connection: Connection
) {}
async someTransactionalMethod(): Promise<void> {
const queryRunner = this.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
// 这里是你的事务逻辑
// ...
await queryRunner.commitTransaction();
} catch (err) {
// 如果事务中发生错误,这里将会捕获到
await queryRunner.rollbackTransaction();
// 你可以在这里处理错误
throw new Error(`Transaction failed: ${err.message}`);
} finally {
// 你需要释放queryRunner
await queryRunner.release();
}
}
}
```
### 使用事务装饰器
NestJS与Typeorm集成时,提供了`@Transaction()`和`@TransactionManager()`装饰器,可以在方法上使用这些装饰器来自动处理事务。如果在这些事务中发生错误,Typeorm会自动回滚事务,并且可以通过常规的错误处理方式(如全局异常过滤器或者方法内的`try/catch`)来捕获错误。
例如:
```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, EntityManager, Transaction, TransactionManager } from 'typeorm';
import { YourEntity } from './entities/your.entity';
@Injectable()
export class YourService {
constructor(
@InjectRepository(YourEntity)
private yourEntityRepository: Repository<YourEntity>
) {}
@Transaction()
async someTransactionalMethod(
@TransactionManager() manager: EntityManager
): Promise<void> {
try {
// 在这里使用manager进行操作,例如:
await manager.save(YourEntity, /* ... */);
// ...
} catch (err) {
// 如果有错误发生,Typeorm会自动回滚事务
// 此处可以处理错误
throw new Error(`Transaction failed: ${err.message}`);
}
}
}
```
在上述两种方法中,如果事务出现错误,可以通过抛出自定义错误或者使用NestJS内置的异常过滤器(`HttpException`或者是更特定的异常类)来处理错误,并且可以在异常过滤器中进一步自定义错误处理逻辑,例如记录日志、发送警报等。
记得,错误处理是一个重要的部分,应该根据具体的应用场景来设计适当的错误处理策略。
阅读 48 · 6月27日 12:16
如何在 Nestjs 中实现多种 passport - jwt 身份验证策略
在NestJS中实现多种`passport-jwt`身份验证策略通常意味着您需要定义多个策略,每个策略都有不同的验证规则或使用不同的JWT密钥。以下是一系列步骤来实现这一功能,以及一个例子:
### 步骤 1: 安装必要的包
首先,您需要安装Passport、passport-jwt和@nestjs/passport。
```bash
npm install @nestjs/passport passport passport-jwt
```
### 步骤 2: 创建 JWT 策略
在`src/auth/strategies`文件夹中创建两个文件,分别对应不同的JWT策略。
#### 例如:
- `jwt.strategy.ts`(默认策略)
- `jwt-admin.strategy.ts`(专用于管理员的策略)
每个文件将扩展`PassportStrategy`类,并在构造函数中定义不同的秘密或验证选项。
### 步骤 3: 定义策略
在各自的策略文件中,您应该定义继承自`PassportStrategy`的类,并且为每一个策略提供一个唯一的名称。
#### 例如:
`jwt.strategy.ts`:
```typescript
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, ExtractJwt } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
return { userId: payload.sub, username: payload.username };
}
}
```
`jwt-admin.strategy.ts`:
```typescript
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, ExtractJwt } from 'passport-jwt';
@Injectable()
export class JwtAdminStrategy extends PassportStrategy(Strategy, 'jwt-admin') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_ADMIN_SECRET,
passReqToCallback: true,
});
}
async validate(req: Request, payload: any) {
if (req.url.startsWith('/admin')) {
return { adminId: payload.sub, adminName: payload.username };
}
return null;
}
}
```
注意,在`JwtAdminStrategy`中,我们使用了`passReqToCallback: true`,它允许我们在`validate`方法中访问`req`对象。
### 步骤 4: 注册策略
在`AuthModule`中,使用`@Module()`装饰器注册您的策略。确保引入策略并将其添加到`providers`数组中。
```typescript
import { Module } from '@nestjs/common';
import { JwtStrategy } from './strategies/jwt.strategy';
import { JwtAdminStrategy } from './strategies/jwt-admin.strategy';
@Module({
// ...
providers: [JwtStrategy, JwtAdminStrategy],
// ...
})
export class AuthModule {}
```
### 步骤 5: 在控制器中使用策略
在您的控制器中,使用`@UseGuards()`装饰器激活特定策略。
```typescript
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('api')
export class ApiController {
@Get('user')
@UseGuards(AuthGuard('jwt'))
getUserProfile() {
// 用户端点逻辑
}
@Get('admin')
@UseGuards(AuthGuard('jwt-admin'))
getAdminProfile() {
// 管理员端点逻辑
}
}
```
在上面的例子中,当访问`/api/user`时,将使用默认的JWT策略进行身份验证,而访问`/api/admin`时,将使用管理员的JWT策略进行身份验证。
### 注意事项
- 确保环境变量`JWT_SECRET`和`JWT_ADMIN_SECRET`分别为用户JWT和管理员JWT设置了不同的密钥。
- 在`validate`方法中,您应该返回一个有效载荷对象,该对象将被附加到请求对象的`user`属性。
- 如果您需要处理特定的验证逻辑,例如验证用户是否具有管理员权限,应该在`validate`方法中进行这些检查。
总之,NestJS和Passport提供了灵活的方式来定义和使用多种身份验证策略,从而使得您能够根据不同的业务场景来保护您的API。
阅读 33 · 6月27日 12:16
NestJS 如何使用 TypeORM 内存数据库?
NestJS 中使用 TypeORM 内存数据库,主要是为了在开发过程中进行快速原型开发或者用于测试时不希望持久化数据到真实数据库。以下是使用 TypeORM 内存数据库的步骤:
1. **安装依赖**:首先,确保你已经安装了 NestJS 相关的 TypeORM 包,以及数据库驱动。对于内存数据库,我们通常使用 `sqlite3`,因为它可以很容易地在内存中运行。
```bash
npm install @nestjs/typeorm typeorm sqlite3
```
2. **配置 TypeORM**:在你的 `app.module.ts` 或者相应的模块配置中,你需要设置 TypeORM 以使用 SQLite 的内存数据库。这里是一个配置的例子:
```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:', // 指定数据库为内存数据库
entities: [/* 你的实体类位置 */],
synchronize: true, // 注意:仅在开发环境中使用
}),
// ... 其他模块
],
})
export class AppModule {}
```
在这个配置中:
- `type` 设置为 `sqlite`,因为我们使用 SQLite 作为我们的内存数据库。
- `database` 设置为 `:memory:`,这告诉 SQLite 创建一个内存数据库。
- `entities` 数组应该包含你的应用程序中所有的实体类。
- `synchronize` 设置为 `true`,这将使得 TypeORM 在每次应用程序启动时自动创建数据库表。这是非常方便的,但应该只在开发环境中使用,因为它可能导致生产数据丢失。
3. **定义实体**:在你的应用中创建实体,这些实体将映射到数据库中的表。例如:
```typescript
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
}
```
4. **使用 Repository 进行操作**:在你的服务中,你可以注入这些实体的仓库(Repository),并使用这些仓库进行数据的增删改查操作。例如:
```typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {}
findAll(): Promise<User[]> {
return this.userRepository.find();
}
// ... 其他增删改查方法
}
```
使用内存数据库进行测试时,你的数据在每次应用程序重新启动时都会丢失,这对于某些类型的测试是非常有用的,因为它保证了测试的隔离性。
以上是在 NestJS 中使用 TypeORM 配置内存数据库的一般步骤。这让你能够快速开始开发和测试,而无需担心影响持久化的数据存储。
阅读 43 · 6月27日 12:16
Nestjs 如何通过 registerSync 导入动态模块中?
在 NestJS 中,动态模块允许我们根据不同的条件动态注册模块、提供者或控制器。这种灵活性对于构建根据配置或环境变量差异化行为的服务特别有用。
要在 NestJS 中通过 `registerSync` 导入动态模块,我们通常会使用模块的静态方法,比如 `forRoot` 或 `forRootSync`,来同步地注册模块及其相关依赖。这些方法通常返回一个动态模块对象,其中包含了 `module` 属性指向当前模块,以及 `providers` 和 `exports` 属性列出了要注册和导出的提供者。
以下是一个典型的例子,假设我们有一个配置模块 `ConfigModule`,它可以同步接收一些配置项,并且我们想要将这个模块动态导入到我们的应用中。
```typescript
// config.module.ts
import { Module, DynamicModule, Global } from '@nestjs/common';
import { ConfigService } from './config.service';
@Global()
@Module({})
export class ConfigModule {
static registerSync(options: ConfigOptions): DynamicModule {
const providers = [
{
provide: ConfigService,
useValue: new ConfigService(options),
},
];
return {
module: ConfigModule,
providers: providers,
exports: providers, // 将 ConfigService 加入导出列表,使得其他模块可以注入使用
};
}
}
```
在上面的代码中,`ConfigModule` 可以同步地接收一些配置,并通过 `registerSync` 方法导入这些配置。`ConfigService` 是一个示例服务,它将使用这些配置。
然后在应用的根模块中,我们可以通过 `ConfigModule.registerSync` 方法将 `ConfigModule` 导入应用,并传递所需的配置选项。这里是如何进行导入的:
```typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from './config/config.module';
@Module({
imports: [
ConfigModule.registerSync({
// 配置项
}),
],
// ...其他的模块和提供者
})
export class AppModule {}
```
在这个例子中,我们同步地向 `ConfigModule` 传递了配置项,并且将其作为依赖导入了应用模块 `AppModule` 中。通过这种方式,我们可以在整个应用中使用 `ConfigService` 服务并访问配置项。
阅读 48 · 6月27日 12:16
NestJS 如何在拦截器中的获取请求内容?
在 NestJS 中,拦截器(Interceptors)提供了一种强大的方式来拦截和处理进出的请求和响应。要在拦截器中获取请求内容,您需要访问当前执行的上下文,这可以通过实现 `Interceptor` 接口并使用 `ExecutionContext` 类来完成。
下面是如何在拦截器中获取请求内容的步骤:
1. **创建拦截器**: 首先,您需要创建一个新的拦截器。可以通过使用 `@Injectable` 装饰器来创建,然后实现 `NestInterceptor` 接口。
2. **实现 intercept 方法**: `NestInterceptor` 接口要求实现一个 `intercept` 方法,该方法接收 `ExecutionContext` 和 `CallHandler` 两个参数。
3. **从 ExecutionContext 获取请求对象**: `ExecutionContext` 对象提供了访问请求和响应对象的能力。通过 `switchToHttp` 方法,您可以获取当前的 HTTP 请求对象。
4. **读取请求内容**: 一旦拥有了请求对象,就可以通过它的属性如 `body`、`query`、`params` 等来访问请求的内容。
下面是一个简单的例子,说明如何在 NestJS 拦截器中获取请求内容:
```typescript
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const body = request.body; // 获取请求体
const query = request.query; // 获取查询参数
const params = request.params; // 获取路由参数
console.log(`Incoming Request: ${request.method} ${request.url}`);
console.log('Body:', body);
console.log('Query:', query);
console.log('Params:', params);
// 继续执行请求的下一步操作
return next
.handle()
.pipe(
tap(() => console.log(`Outgoing Response: The response has been sent.`)),
);
}
}
```
在上面的例子中,`LoggingInterceptor` 拦截器通过 `ExecutionContext` 获取了当前的 HTTP 请求,并打印了请求方法、URL、请求体、查询参数和路由参数。然后,它通过 `next.handle()` 允许请求继续其处理流程,并且在响应发出后打印了一条日志消息。
要将这个拦截器应用到您的 NestJS 应用程序中,您需要在模块的 `providers` 数组中注册它,并且可以在控制器或特定路由上使用 `@UseInterceptors` 装饰器来激活它。
阅读 24 · 6月27日 12:16
如何在 Nestjs 中使用 express 的路由特定中间件?
在NestJS中,您可以在模块、控制器或单个路由级别上使用Express中间件。NestJS本质上建立在Express之上(默认情况下,但也可以选择使用Fastify),因此可以直接使用Express中间件。以下是一些使用Express路由特定中间件的方法:
### 全局中间件
如果您想要在整个应用程序中使用中间件,可以在`main.ts`文件中全局注册它:
```typescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as helmet from 'helmet'; // 一个用于安全的中间件
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(helmet()); // 全局使用helmet中间件
await app.listen(3000);
}
bootstrap();
```
### 模块级中间件
在`Module`层面,您可以通过实现`NestModule`接口并在`configure`方法中配置中间件:
```typescript
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { loggerMiddleware } from './logger.middleware';
@Module({
// ...其他配置项如controllers和providers
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(loggerMiddleware)
.forRoutes('cats'); // 只对'cats'路由应用中间件
}
}
```
### 控制器级中间件
如果您只想在特定控制器的路由中使用中间件,可以在控制器内的构造函数中配置:
```typescript
import { Controller, Get, UseMiddleware } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
@UseMiddleware(loggerMiddleware) // 使用装饰器应用中间件
findAll() {
// ...路由的处理逻辑
}
}
```
请注意,`@UseMiddleware` 装饰器不是NestJS官方提供的API,这里仅为说明目的。在NestJS中,你可以使用`@UseGuards`, `@UseInterceptors`, `@UsePipes`, 和 `@UseFilters`。对于中间件,通常在`Module`级别使用`configure`方法注册,但如果需要直接在路由处理程序中使用Express中间件,你可以使用以下方式:
```typescript
import { Controller, Get, Req, Res, Next } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findCat(@Req() req: Request, @Res() res: Response, @Next() next: NextFunction) {
loggerMiddleware(req, res, next);
// ...之后会调用next(),然后继续路由的处理逻辑
}
}
```
通过上面的方法,您可以根据需要在NestJS应用程序中的适当层次结构级别上使用Express路由特定中间件。这样做时,一定要考虑中间件的作用范围和执行顺序,确保您的应用程序的安全和效率。
阅读 36 · 6月27日 12:16
NestJS 如何插入具有 OneToMany 关系的实体?
当使用 NestJS 与一个类似于TypeORM这样的ORM(对象关系映射)库来处理数据库操作时,可以通过定义恰当的实体关系模型来插入具有OneToMany关系的实体。
以下是如何定义和插入具有OneToMany关系的实体的步骤:
1. **定义实体模型**
假设我们有两个实体:`User` 和 `Photo`。每个用户可以有多张照片,所以我们在 `User` 实体内部定义一个OneToMany关系。
```typescript
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { Photo } from './photo.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
// OneToMany关系
@OneToMany(() => Photo, (photo) => photo.user)
photos: Photo[];
}
```
对应的 `Photo` 实体会有一个ManyToOne关系回指到 `User` 实体。
```typescript
// photo.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.entity';
@Entity()
export class Photo {
@PrimaryGeneratedColumn()
id: number;
@Column()
url: string;
// ManyToOne关系
@ManyToOne(() => User, (user) => user.photos)
user: User;
}
```
2. **插入实体**
使用TypeORM的Repository API,你可以先创建一个User实例,然后创建多个Photo实例并将它们与User实例相关联。
```typescript
// some.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { Photo } from './entities/photo.entity';
@Injectable()
export class SomeService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
@InjectRepository(Photo)
private photosRepository: Repository<Photo>
) {}
async createUserAndPhotos(name: string, photoUrls: string[]) {
// 创建User实例
const user = this.usersRepository.create({ name });
// 持久化User实体
await this.usersRepository.save(user);
// 创建Photo实例并关联到User
for (const photoUrl of photoUrls) {
const photo = this.photosRepository.create({
url: photoUrl,
user: user // 关联User
});
// 持久化Photo实体
await this.photosRepository.save(photo);
}
// 返回创建好的User,其中包含关联的Photos
return this.usersRepository.findOne({ where: { id: user.id }, relations: ['photos'] });
}
}
```
在这个例子中,我们首先创建了一个新的 `User` 实例,然后保存它。接着,我们遍历一组照片URLs来创建 `Photo` 实例,并将每个 `Photo` 实例的 `user` 属性设置为我们刚刚创建的 `User` 实例。每个 `Photo` 实例也被保存。最后,我们可能想要检索新创建的 `User` 实例及其相关的 `Photo` 实例,我们可以使用 `findOne` 方法并指定 `relations` 选项来包含关联的 `Photo` 实例。
请注意,这里的代码片断需要在 NestJS 服务中运行,这意味着你首先需要设置好你的 NestJS 项目,包括安装TypeORM和数据库驱动,配置模块以注入仓库等。这个过程中,你也应该确保正确处理任何可能发生的异常,例如使用try/catch块或在服务方法中使用合适的错误处理逻辑。
阅读 29 · 6月27日 12:16
如何组织在 NestJs 服务中抛出业务逻辑异常?
在NestJS中组织和抛出业务逻辑异常,通常会遵循以下步骤:
### 1. 定义自定义异常类
利用NestJS提供的`HttpException`类,你可以创建自定义异常类以表示特定的业务逻辑问题。例如,如果你在用户服务中需要抛出一个当用户未找到时的异常,你可以定义一个`UserNotFoundException`:
```typescript
import { HttpException, HttpStatus } from '@nestjs/common';
export class UserNotFoundException extends HttpException {
constructor() {
super('User not found', HttpStatus.NOT_FOUND);
}
}
```
### 2. 在服务中抛出自定义异常
在你的服务(service)中,当检测到某个特定的业务逻辑错误时,你可以抛出自定义的异常。例如,在一个用户的服务中,当你尝试获取一个不存在的用户时,可以抛出刚刚定义的`UserNotFoundException`:
```typescript
@Injectable()
export class UsersService {
constructor(@InjectRepository(User) private readonly userRepository: Repository<User>) {}
async findOne(id: string): Promise<User> {
const user = await this.userRepository.findOne(id);
if (!user) {
throw new UserNotFoundException();
}
return user;
}
}
```
### 3. 异常过滤器
NestJS支持异常过滤器(exception filters),它们可以捕获整个应用程序的异常。你可以定义一个全局异常过滤器或特定于控制器或路由的异常过滤器。
```typescript
import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus } from '@nestjs/common';
import { HttpException } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus();
const exceptionResponse = exception.getResponse();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exceptionResponse['message'] || exceptionResponse,
});
}
}
```
然后,你可以在你的模块中注册异常过滤器:
```typescript
@Module({
// ...其他配置
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
```
### 4. 响应给客户端
一旦抛出异常并由异常过滤器处理,客户端将接收到一个结构化的响应,其中包含了异常的相关信息,如HTTP状态码、错误消息等。
### 示例:
假设客户端尝试获取一个不存在的用户。
请求:
```http
GET /users/123
```
响应:
```json
{
"statusCode": 404,
"timestamp": "2023-03-10T10:00:00.000Z",
"path": "/users/123",
"message": "User not found"
}
```
以上步骤确保了NestJS中业务逻辑异常的清晰组织和一致的处理方式,同时也为客户端提供了有用的错误处理信息。
阅读 15 · 6月27日 12:16
NestJS 如何使用拦截器修改来自 PUT 的请求内容和响应内容?
在 NestJS 中,拦截器(Interceptors)是一个非常强大的功能,可以对请求和响应进行额外的处理、转换或扩展。拦截器可以在请求处理流程的不同阶段被调用,允许你在方法执行之前或之后执行一些逻辑。
要使用拦截器修改来自 PUT 请求的内容和响应内容,你首先需要创建一个拦截器类。这个类需要实现 `NestInterceptor` 接口,并且定义一个 `intercept` 方法。在这个方法中,你可以访问请求对象(`ExecutionContext`)并修改它,也可以操作调用处理方法后得到的响应结果。
以下是一个例子,展示了如何创建一个简单的拦截器来修改 PUT 请求的请求体和响应体:
```typescript
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class PutRequestInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const ctx = context.switchToHttp();
const request = ctx.getRequest();
// 修改 PUT 请求的请求体
if (request.method === 'PUT') {
const body = request.body;
// 假设我们要给请求体添加一个 timestamp 字段
request.body = { ...body, timestamp: new Date().toISOString() };
}
// 使用 RxJS 的 map 操作符修改响应体
return next.handle().pipe(
map(data => {
// 假设我们要给响应体添加一个 success 字段
return { ...data, success: true };
}),
);
}
}
```
接下来,你需要将这个拦截器应用到对应的 PUT 路由处理器上。这可以通过在控制器的方法上使用 `@UseInterceptors` 装饰器来完成:
```typescript
import { Controller, Put, UseInterceptors } from '@nestjs/common';
@Controller('items')
export class ItemsController {
@Put()
@UseInterceptors(PutRequestInterceptor)
updateItem() {
// 处理 PUT 请求,并返回响应
}
}
```
在这个例子中,我们首先检查了请求方法,如果是 PUT,我们就修改了请求体,通过添加一个 `timestamp` 字段。随后,我们利用了 RxJS 的 `map` 操作符来修改处理方法的响应结果,通过添加一个 `success` 字段。
需要注意的是,拦截器可以用于很多用途,比如日志记录、异常映射、请求响应转换等。通过组合多个拦截器,可以构建出非常强大且灵活的中间件链。在实际开发中,你的拦截器可以根据需求进行复杂的数据处理和业务逻辑实现。
阅读 24 · 6月27日 12:16