controller 控制
用于处理路由请求
路由的三种参数 Param Query Body
Param: restful API 参数
Query:url 参数
Body:post 参数
Param 就是获取 /xxx/:id
ts
@Get('/data/:id')
getData(@Param() param): string {
console.log('🚀 ~ AppController ~ getData ~ param:', param)
return 'data' + param.id
}
@Controller('user')
export class UserController {
@Get('/:id')
// param 传递 id 使用 nest 自带的管道进行数据转换
getUser(@Param('id', ParseIntPipe) id: number) {
console.log('🚀 ~ UserController ~ id:', typeof id)
return 'get user' + id
}
}
Body
ts
@Post('/data')
postData(@Body() body): string {
console.log('🚀 ~ AppController ~ postData ~ body:', body)
return 'post data'
}
Query /xxx/xxx?id=xxx¶m=222
ts
@Get('/data/:id')
getData(@Param() param, @Query() query): string {
console.log('🚀 ~ AppController ~ getData ~ param:', param)
console.log('🚀 ~ AppController ~ getData ~ query:', query)
return 'data' + param.id
}
providers 提供函数方法
就是提供方法 哪里需要使用这个方法就在哪里进行注入
其实拿 koa2 来进行举例就是
controller 是进行路由对接的 然后执行具体的函数方法 是在 service 里面执行的 数据库的修改就是进一层的里
ts
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
@Module({
imports: [],
controllers: [AppController],
providers: [AppService], // 将service方法进行提供注入到这个module这样这个module就能全局使用
})
export class AppModule {}
ts
@Get('/data/:id')
getData(@Param() param): string {
return this.appService.getData(param)
}
ts
import { Injectable } from '@nestjs/common'
@Injectable()
export class AppService {
getData(param: any) {
console.log('🚀 ~ AppService ~ getData ~ param:', param)
return 'data' + param.id
}
}
异常处理器
ts
// \src\common\exception\http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common'
import { Request, Response } from 'express'
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp()
const response = ctx.getResponse<Response>()
const request = ctx.getRequest<Request>()
const status = exception.getStatus()
response.status(status).json({
statusCode: status,
message: exception.message,
type: 'error',
timestamp: new Date().toISOString(),
path: request.url,
})
}
}
main.ts
ts
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { HttpExceptionFilter } from './common/exception/http-exception.filter'
import { VersioningType } from '@nestjs/common'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
app.setGlobalPrefix('api') // 定义全局抬头
app.enableVersioning({ type: VersioningType.URI, defaultVersion: '1' }) // 定义版本 /api/v1/xxx
app.useGlobalFilters(new HttpExceptionFilter()) // 设置全局错误拦截器
await app.listen(3000)
}
bootstrap().catch((err) => console.error(err))
config 配置文件
安装配置依赖
sql
pnpm add @nestjs/config
根目录下创建配置文件 .env (该文件不进行提交) 我们创建.env.example 进行提交到代码仓库
.env
bash
APP_NAME=nest-admin
# 数据库配置
DB_TYPE=mysql
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=
DB_PASSWORD=
DB_DATABASE=
DB_SYNCHRONIZE=false
创建config模块文件
./common/config
ts
// app.config.ts
import { registerAs } from '@nestjs/config'
export default registerAs('app', () => {
return {
name: process.env.APP_NAME,
// port: 3000,
}
})
ts
// db.config.ts
import { registerAs } from '@nestjs/config'
export default registerAs('database', () => {
return {
type: process.env.DB_TYPE || 'mysql',
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT, 10) || 3306,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
synchronize: process.env.DB_SYNCHRONIZE === 'true',
}
})
ts
// index.ts 导出
import appConfig from './app.config'
import dbConfig from './db.config'
export default [appConfig, dbConfig]
再app.module.ts 进行导入使用
ts
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { UserModule } from './modules/user/user.module'
import { AuthController } from './modules/auth/auth.controller'
import { AuthModule } from './modules/auth/auth.module'
import { ConfigModule } from '@nestjs/config'
import config from './common/config' // 导出的文件引入
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [...config], // 加载到全局
}),
UserModule,
AuthModule,
],
controllers: [AppController, AuthController],
providers: [AppService],
})
export class AppModule {}
再具体的模块之中进行使用
ts
import { Controller, Get, Inject } from '@nestjs/common'
import { ConfigType } from '@nestjs/config'
import appConfig from './common/config/app.config'
@Controller()
export class AppController {
constructor(
private readonly appService: AppService,
// 使用接收全局的注入 然后使用类型工具进行包裹 这样就会有类型提示
@Inject(appConfig.KEY) private readonly app: ConfigType<typeof appConfig>,
) {}
@Get('/test')
getTest(): string {
console.log('🚀 ~ AppController ~ getTest ~ this.app.name:', this.app)
return this.app.name
}
}
连接数据库与操作流程
mysql数据库创建
bash
数据库名: admin-dev
字符集: utf8
排序规则: utf8 general ci
css
pnpm install --save @nestjs/typeorm typeorm mysql2
- 连接数据库
ts
// app.module.ts
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { UserModule } from './modules/user/user.module'
import { AuthController } from './modules/auth/auth.controller'
import { AuthModule } from './modules/auth/auth.module'
import { ConfigModule, ConfigService } from '@nestjs/config'
import config from './common/config'
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [...config],
}),
TypeOrmModule.forRootAsync({
inject: [ConfigService],
useFactory: (configService: ConfigService) =>
({
type: configService.get('database.type'),
host: configService.get('database.host'),
port: configService.get('database.port'),
username: configService.get('database.username'),
password: configService.get('database.password'),
database: configService.get('database.database'),
synchronize: configService.get('database.synchronize'), // 是否自动同步 建议关闭 生产环境绝对不能使用
autoLoadEntities: true, // 自动加载实体
}) as TypeOrmModuleOptions,
}),
UserModule,
AuthModule,
],
controllers: [AppController, AuthController],
providers: [AppService],
})
export class AppModule {}
- 定义数据模型
ts
// user.entity.ts
import { Column, Entity, PrimaryGeneratedColumn, Unique } from 'typeorm'
@Entity('admin_user')
export class User {
@PrimaryGeneratedColumn() //主键并且自增
id: number
@Column()
@Unique(['username'])
username: string
@Column()
password: string
@Column()
role: string
@Column()
nickname: string
@Column()
active: boolean
}
- 当前哪一个module需要使用就引入 实体
ts
// user.module.ts
import { Module } from '@nestjs/common'
import { UserController } from './user.controller'
import { TypeOrmModule } from '@nestjs/typeorm'
import { User } from './user.entity'
import { UserService } from './user.service'
@Module({
imports: [TypeOrmModule.forFeature([User])], // 引入需要使用的数据模型(实体)
controllers: [UserController],
providers: [UserService], // 再Service使用具体的 数据模型操作数据库数据
})
export class UserModule {}
- 再具体模块的service进行实体使用
ts
// userService
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { User } from './user.entity'
import { Repository } from 'typeorm'
@Injectable()
export class UserService {
constructor(
// 进行注解加上实体 使用实例来进行使用
@InjectRepository(User)
private readonly userRepository: Repository<User>, // 定义类型
) {}
findOne(id: number) {
// 执行具体的sql语句查询
return this.userRepository.findOne({ where: { id } })
}
}
- service 查询然后返回给controller进行使用
ts
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common'
import { UserService } from './user.service'
@Controller('user')
export class UserController {
// 将逻辑返回给 controller 进行使用
constructor(private readonly userService: UserService) {}
@Get('/:id')
// param 传递 id 使用 nest 自带的管道进行数据转换
getUser(@Param('id', ParseIntPipe) id: number) {
return this.userService.findOne(id)
}
}
守卫/全局守卫
bash
nest g gu modules/auth
ts
// auth.module.ts
import { Module } from '@nestjs/common'
import { AuthService } from './auth.service'
import { AuthController } from './auth.controller'
import { APP_GUARD } from '@nestjs/core'
import { AuthGuard } from './auth.guard'
@Module({
controllers: [AuthController],
providers: [
AuthService,
{
// 定义全局守卫
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AuthModule {}
如果只是定义单个的守卫 那么就不需要再 这个module里面写任何东西 只需要创建一个守卫 然后哪里需要哪里注入即可
这里我们注册的是全局路由守卫 就是每一个路由请求都会进入到这个守卫里面
- 定义一个聚合装饰器,将标记公共装饰器和守卫装饰器聚合
ts
// auth.decorator.ts
import { applyDecorators, SetMetadata, UseGuards } from '@nestjs/common'
import { AuthGuard } from './auth.guard'
export const IS_PUBLIC_KEY = 'isPublic'
export function Auth() {
// 这是聚合装饰器 表示可以聚合多个装饰器 这样外部只需要调用一个装饰器即可
// 第一个装饰器作用是设置元数据 表示这是一个公共路由
// 第二个装饰器作用是使用全局守卫 表示需要进行认证
return applyDecorators(SetMetadata(IS_PUBLIC_KEY, true), UseGuards(AuthGuard))
}
/**
这里第一个装饰器 设置了 当前路由为公共路由,
然后我们再第一个装饰器就去判断是否设置为了公共路由设置了就会放行没有就会进行第二步判断
*/
- 全局守卫(基础版本) 固定写法课参照文档全局守卫
ts
// auth.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { Observable } from 'rxjs'
import { IS_PUBLIC_KEY } from './auth.decorator'
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
])
// 如果是公共路由,直接返回 true 不会被全局守卫拦截
if (isPublic) {
return true
}
return false
}
}
使用
ts
// auth.service.ts
import { Controller, Post } from '@nestjs/common'
import { Auth } from './auth.decorator'
@Controller('auth')
export class AuthController {
@Auth() // 使用装饰器
@Post('login')
login() {
return 'login'
}
}