nestjs基础
安装nestjs
npm i -g @nestjs/cli
nest new project-name
介绍 | 地址 |
---|---|
nestjs中文网 | nestjs中文 |
小满nest | 小满nestjs |
typescript中文网 | ts中文 |
小满ts | 小满ts |
可能的问题
1.因为在此系统上禁止运行脚本。
有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
首次在计算机上启动 Windows PowerShell 时,现用执行策略很可能是 Restricted(默认设置)。Restricted 策略不允许任何脚本运行。
win+x 打开PowerShell(管理员)
set-ExecutionPolicy RemoteSigned //设置为打开,键入Y或者A,同意
get-executionpolicy//查看是否更改成功,为RemoteSigned表示成功
2.配置eslint后运行项目有如下警告(换行格式问题):Delete `␍`eslintprettier/prettier,执行以下命令(可以自动修复这些问题):
npm run lint --fix
或者
在VScode的设置里搜eol,修改
或者
下载vscode插件:
EditorConfig for VS Code
在项目根目录新建 .editorconfig
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
原因
在window系统中,clone代码下来,会自动把换行符LF(linefeed character) 转换成回车符CRLF(carriage-return character)。这时候我们本地的代码都是回车符。如果没有加eslint,提交代码的时候,项目的仓库默认是Linux环境下提交的代码,就会提示将会覆盖换行符为LF。使用了eslint并有进行规则配置或者prettier的.prettierrc有进行配置结尾换行符,那么就会直接在开发环境中进行验证。就会提示上述错误(警告)。
基本介绍
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
// 主路由
@Controller('zxj')
export class AppController {
constructor(private readonly appService: AppService) {}
// 分路由
@Get('nb')
// 下面的方法名可以随便起,无影响,但是要和服务中的一致
getHello(): string {
return this.appService.getHello();
}
}
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
//路由(控制器)和服务通过Module链接
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return '赵新杰nb!';
}
}
nest的基本命令
nest–help
nest --help命令用于获取关于Nest CLI(Command Line Interface)的帮助信息。它将显示Nest CLI的可用命令列表以及每个命令的详细描述。基本上命令都是nest g开头
nest g ???
命令
示例:
要在项目终端上使用
user是要放的目录,如果没有就会自动创建一个
nest g co user
name命令 | alias别名 | description 介绍 |
---|---|---|
application | application | Generate a new application workspace |
class | cl | Generate a new class |
configuration | config | Generate a CLI configuration file |
controller | co | 创建控制器路由 |
module | mo | 创建模型连接路由和服务 |
service | s | 创建服务 |
resource | res | 一键生成上面三个 |
decorator | d | Generate a custom decorator |
filter | f | Generate a filter declaration |
gateway | ga | Generate a gateway declaration |
guard | gu | Generate a guard declaration |
interceptor | itc | Generate an interceptor declaration |
interface | itf | Generate an interface |
library | lib | Generate a new library within a monorepo |
middleware | mi | 创建中间件 |
pipe | pi | 创建管道,用于检验和修改数据使用 |
provider | pr | Generate a provider declaration |
resolver | r | Generate a GraphQL resolver declaration |
sub-app | app | Generate a new application within a monorepo |
请求传值
GET请求
get请求的@Request()
这种在url中传参的方式叫做get的params传参,和get的接参prama不是一个类型,但是相似
127.0.0.1:3000/user?name=123
127.0.0.1:3000/you?name=123&id=123多参用&连接
参数在Request.query里
由@Request() req接参
接到{name:123}
@Get()
findAll(@Request() req) {
return req.query.name;
}
get请求的@Query()
语法糖
@Query()对比@Request()
就是query对比req.query
@Query(‘name’)里可以直接接参数,直接读值
@Get()
findAll(@Query() query) {
return query.name;
}
@Get()
findAll(@Query(name) name) {
return name;
}
动态请求
get请求的@Param(‘id’)
上面的相当于键值在url里都定义好了,这Param('id')相当于接收值,然后再后端命名
http://127.0.0.1:3000/you/1
参数在路由里@Get(‘:id’)
由@Param(‘id’)接受并命名
@Get(':id')
findOne(@Param('id') id: string) {
return this.youService.findOne(+id);
}
POST请求
post请求的@Request()
127.0.0.1:3000/you
{ “name”:“123”,“age”:18} 一般以json格式传送,基本都要"键":“值”
参数在请求体body里
@Post()
findAll(@Request() req) {
return req.body;
}
post请求的@Body()
语法糖
@Body()对比@Request()
就是body对比req.body
@Body(‘name’)里可以直接接参数,直接读值
@Post()
findAll(@Body() body) {
return body;
}
@Post()
findAll(@Body('name') name) {
return name;
}
设置静态资源
根目录下创建public文件夹,放入一张图片
在main.ts引入http平台
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
// 1.引入NestExpressApplication
import { NestExpressApplication } from '@nestjs/platform-express';
async function bootstrap() {
// 2.加入到create
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 3.配置静态资源目录
app.useStaticAssets('public');
await app.listen(3000);
}
bootstrap();
进阶:配置虚拟路径的2种方法
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { join } from 'path';
// 1.引入NestExpressApplication
import { NestExpressApplication } from '@nestjs/platform-express';
async function bootstrap() {
// 2.加入到create
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 3.配置静态资源目录
// app.useStaticAssets('public');
// 3.1 设置虚拟路径
// app.useStaticAssets('public', {
// prefix: '/static/'
// })
/*
* 3.1.2 设置虚拟路径2
* 注:需要引入import { join } from 'path';
* 访问连接:http://localhost:3000/static/2.jpg
*/
app.useStaticAssets(join(__dirname, '..', 'public'), {
prefix: '/static/'
})
await app.listen(3000);
}
bootstrap();
模板引擎
cookie
npm install cookie-parser --save
配置
main.ts 中引入 cookie-parser
import * as cookieParser from 'cookie-parser'
在 main.ts 配置中间件
app.use(cookieParser());
设置cookie
res.cookie('username', '我是cookie', {maxAge: 1000*60*10, httpOnly: true})
获取cookies
@Get('cookie')
getCookie(@Request() req){
console.log(req.cookies.username);
return req.cookies.username;
cookie加密
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { join } from 'path';
import * as cookieParser from 'cookie-parser';
async function bootstrap() {
// ...
// 配置cookie中间件,()里面写上东西
app.use(cookieParser('this signed cookies'));
await app.listen(3000);
}
bootstrap();
. 在article.controller中添加加密cookie
import { Controller, Get, Response } from '@nestjs/common';
@Controller('article')
export class ArticleController {
@Get()
index(@Response() res){
// 设置cookie,signed启用加密
res.cookie('username', '我是cookie', {maxAge: 1000*60*10, httpOnly: true, signed: true})
// 注:res和return不能同时使用,否则卡死
// return '这是文章页面';
res.send('这是文章页面');
}
}
3.读取cookie
import { Body, Controller, Get, Post, Render, Response, Request } from '@nestjs/common';
@Controller('user')
export class UserController {
// ...
// 获取cookie
@Get('cookie')
getCookie(@Request() req){
// 1. 获取普通cookie
// console.log(req.cookies.username);
// 2. 获取加密cookie
console.log(req.signedCookies.username);
return req.signedCookies.username;
}
}
session
session 是服务器 为每个用户的浏览器创建的一个会话对象 ,这个session 会记录到 浏览器的 cookie 用来区分用户
我们使用的是nestjs 默认框架express 他也支持express 的插件 所以我们就可以安装express的session
npm i express-session --save
需要智能提示可以装一个声明依赖
npm i @types/express-session -D
然后在main.ts 引入 通过app.use 注册session
import * as session from 'express-session'
app.use(session())
参数配置 | 详解 |
---|---|
secret | 成服务端session 签名 可以理解为加盐 |
name | 生成客户端cookie 的名字 默认 connect.sid |
cookie | 返回到前端 key 的属性,默认值为{ path: ‘/’, httpOnly: true, secure: false, maxAge: null }。 |
rolling | 在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false) |
nestjs 配置
import { NestFactory } from '@nestjs/core';
import { VersioningType } from '@nestjs/common';
import { AppModule } from './app.module';
import * as session from 'express-session'
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.URI
})
app.use(session({ secret: "加密的私钥", name: "session的名字", rolling: true, cookie: { maxAge: null } }))
await app.listen(3000);
}
bootstrap();
示例
<template>
<div class="wraps">
<el-form :label-position="labelPosition" label-width="100px" :model="formLabelAlign" style="max-width: 460px">
<el-form-item label="账号">
<el-input v-model="formLabelAlign.name" />
</el-form-item>
<el-form-item label="密码">
<el-input type="password" v-model="formLabelAlign.password" />
</el-form-item>
<el-form-item label="验证码">
<div style="display:flex">
<el-input v-model="formLabelAlign.code" />
<img @click="resetCode" :src="codeUrl" alt="">
</div>
</el-form-item>
<el-form-item>
<el-button @click="submit">登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang='ts'>
import { onMounted, reactive, ref } from 'vue';
const codeUrl = ref<string>('/api/user/code')
const resetCode = () => codeUrl.value = codeUrl.value + '?' + Math.random()
const labelPosition = ref<string>('right')
const formLabelAlign = reactive({
name: "",
password: "",
code: ""
})
const submit = async () => {
await fetch('/api/user/create', {
method: "POST",
body: JSON.stringify(formLabelAlign),
headers: {
'content-type': 'application/json'
}
}).then(res => res.json())
}
</script>
<style>
* {
padding: 0;
margin: 0;
}
.wraps {
display: flex;
justify-content: center;
align-items: center;
height: inherit;
}
html,
body,
#app {
height: 100%;
}
</style>
import { Controller, Get, Post, Body, Param, Request, Query, Headers, HttpCode, Res, Req } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import * as svgCaptcha from 'svg-captcha';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) { }
@Get('code')
createCaptcha(@Req() req, @Res() res) {
const captcha = svgCaptcha.create({
size: 4,//生成几个验证码
fontSize: 50, //文字大小
width: 100, //宽度
height: 34, //高度
background: '#cc9966', //背景颜色
})
req.session.code = captcha.text
//存储验证码记录到session,可以看到session是保持在服务器中,在请求中加入的,同时会给客户端下发一个加密的cookie
res.type('image/svg+xml')
res.send(captcha.data)
}
@Post('create')
createUser(@Req() req, @Body() body) {
console.log(req.session.code, body)
if (req.session.code.toLocaleLowerCase() === body?.code?.toLocaleLowerCase()) {
return {
message: "验证码正确"
}
} else {
return {
message: "验证码错误"
}
}
}
}
cookie和session的区别
Cookie和Session都是用于在Web应用中存储用户的状态信息的方式,但它们有一些不同之处。在下面,我将详细介绍Cookie和Session之间的区别。
-
存储位置:Cookie是存储在客户端(浏览器)的一小段文本数据,而Session是存储在服务器上的对象。
-
数据安全性:由于Cookie是存储在客户端,所以它可以被用户修改和篡改,因此它的安全性较低。而Session存储在服务器上,只有Session ID会被存储在Cookie中,所以Session的安全性较高。
-
存储容量:Cookie的存储容量通常较小,一般为4KB左右,而Session的存储容量通常没有明确的限制,可以根据服务器的配置进行调整。
-
存储方式:Cookie是将数据存储在客户端的浏览器中,每次请求时都会在HTTP请求头中携带Cookie信息。而Session是将数据存储在服务器上,每次请求时只需携带Session ID,服务器会根据Session ID来获取对应的Session数据。
-
生命周期:Cookie可以设置一个过期时间,在过期时间之前一直有效,即使用户关闭了浏览器,Cookie也可以保存下来。而Session的生命周期通常由服务器端控制,一般会在用户关闭浏览器或长时间不活动后过期。
总之,Cookie适用于存储较小的、不敏感的数据,并且需要在多个页面间传递;而Session适用于存储较大的、敏感的数据,并且仅在服务器端使用。
文件上传
单文件上传
npm i -D @types/multer
@types/multer 这两个需要安装,加强ts类型的
@nestjs/platform-express nestJs自带了
import {UploadedFile, UseInterceptors} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import * as fs from 'fs';
import * as path from 'path';
@Post('upload')
//import { FileInterceptor } from '@nestjs/platform-express';引入
//FileInterceptor('file')拦截器中的名字要和上传文件时的表单字段名一致
//@UploadedFile()即是拦截器从body中拦截的数据
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file) {
//在fs中创建一个写入流,将文件写入到指定路径
const writeStream = fs.createWriteStream(
//再引入path模块,实现路径的拼接
path.join(__dirname, '../../public/uploads/', file.originalname),
);
//将buffer文件写入到指定路径
writeStream.write(file.buffer);
return writeStream;
}
同名的多文件上传
此时的文件拦截器FilesInterceptor 和UploadedFiles修饰器已经是复数形式
@Post('uploads')
//import { FilesInterceptor } from '@nestjs/platform-express';引入
//FilesInterceptor('file')拦截器中的名字要和上传文件时的表单字段名一致
//@UploadedFiles()即是拦截器从body中拦截的数据
@UseInterceptors(FilesInterceptor('file'))
uploadFiles(@UploadedFiles() files) {
for (const file of files) {
//在fs中创建一个写入流,将文件写入到指定路径
const writeStream = fs.createWriteStream(
//再引入path模块,实现路径的拼接
path.join(__dirname, '../../public/uploads/', file.originalname),
);
//将buffer文件写入到指定路径
writeStream.write(file.buffer);
}
return '上传成功';
}
中间件
nest风格的中间件
nest g mi middleware/init
nest g mi创建中间件
middleware/init,指定位置
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class InitMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log(Date.now());
next();
}
}
在主模块引入
import { Module, NestModule, MiddlewareConsumer , RequestMethod } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { InitMiddleware } from './middleware/init/init.middleware';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(InitMiddleware)
.forRoutes(
{ path: 'new', method: RequestMethod.ALL },
{ path: 'id', method: RequestMethod.ALL },
);
}
}
函数式中间件
还有函数式中间件和全局中间件,使用方法类似express
先把 logger 转换成函数。
//logger.middleware.ts
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
Copy to clipboardErrorCopied
现在在 AppModule 中使用它。
app.module.ts
consumer
.apply(logger)
.forRoutes(CatsController);
当您的中间件没有任何依赖关系时,我们可以考虑使用函数式中间件。
多个中间件,为了绑定顺序执行的多个中间件,我们可以在 apply() 方法内用逗号分隔它们。
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
全局中间件
如果我们想一次性将中间件绑定到每个注册路由,我们可以使用由INestApplication实例提供的 use()方法:
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);