skip to content
寻找莉莉丝

Nest.js(二)

/ 4 min read / 次阅读

Session

服务器为用户创建的一个会话对象,记录到 cookie 区分用户。

应用场景:注册功能(发送验证码)

后端

# session 插件
pnpm add express-session @types/express-session
# 验证码插件
pnpm add svg-captcha

main.ts

// ...
import * as session from "express-session";

async function bootstrap() {
	const app = await NestFactory.create(AppModule);
	app.enableVersioning({
		type: VersioningType.URI,
	});
	// app 实例使用 session,通过装饰器 @Session 可以拿到
	app.use(
		session({
			name: "nest.sid", // 会话名称
			cookie: { maxAge: 800000 }, // cookie 属性设置
			secret: "nest cats", // 加盐
			rolling: true, // 每次请求重置 cookie 过期时间
		}),
	);
	await app.listen(3000);
}
bootstrap();

user.controller.ts

import { Body, Controller, Get, Post, Req, Res } from "@nestjs/common";
import { UserService } from "./user.service";
import * as svgCaptcha from "svg-captcha";

@Controller("user")
export class UserController {
	constructor(private readonly userService: UserService) {}

	@Get("code")
	createCaptcha(@Req() req, @Res() res) {
		// 用户每次访问该页面时,就会触发 code 更新
		const captcha = svgCaptcha.create({
			size: 4, // 生成几个验证码
			fontSize: 50, // 文字大小
			width: 100, // 宽度
			height: 32, // 高度
			background: "#eaeaea", // 背景颜色
		});
		req.session.code = captcha.text; // 存储验证码记录到 session
		res.type("image/svg+xml");
		res.send(captcha.data);
	}

	@Post("create")
	createUser(@Req() req, @Body() body) {
		if (req.session.code.toLocaleLowerCase() === body?.validationCode?.toLocaleLowerCase()) {
			return {
				code: 200,
				message: "验证码正确",
			};
		} else {
			return {
				code: 400,
				message: "验证码错误",
			};
		}
	}
}

前端

技术栈:vite + react + ts + antdesign

App.tsx

import { useState } from "react";
import "./App.css";
import { Button, Checkbox, Form, Input, message } from "antd";

type FieldType = {
	username?: string;
	password?: string;
	remember?: string;
	validationCode?: string;
};

function App() {
	const [messageApi, contextHolder] = message.useMessage();
	const [codeUrl, setCodeUrl] = useState("/api/user/code");

	const onFinish = async (values: any) => {
		await fetch("/api/user/create", {
			method: "POST",
			body: JSON.stringify(values),
			headers: {
				"content-type": "application/json",
			},
		})
			.then((res) => res.json())
			.then((res) => {
				if (res.code !== 200) {
					messageApi.error(res.message);
				} else {
					messageApi.success(res.message);
				}
			})
			.catch((err) => messageApi.error(err.message));
	};

	const onFinishFailed = (errorInfo: any) => {
		messageApi.error("请正确填写表单");
	};

	const resetCode = () => {
		setCodeUrl(codeUrl + "?" + Math.random());
	};

	return (
		<>
			{contextHolder}
			<Form
				name="basic"
				labelCol={{ span: 10 }}
				wrapperCol={{ span: 16 }}
				style={{
					maxWidth: 600,
					backgroundColor: "#fff",
					padding: 20,
					borderRadius: 4,
				}}
				initialValues={{ remember: true }}
				onFinish={onFinish}
				onFinishFailed={onFinishFailed}
				autoComplete="off"
			>
				<Form.Item<FieldType>
					label="Username"
					name="username"
					rules={[{ required: true, message: "Please input your username!" }]}
				>
					<Input />
				</Form.Item>

				<Form.Item<FieldType>
					label="Password"
					name="password"
					rules={[{ required: true, message: "Please input your password!" }]}
				>
					<Input.Password />
				</Form.Item>

				<Form.Item<FieldType>
					label="ValidationCode"
					name="validationCode"
					rules={[{ required: true, message: "Please input validation code!" }]}
				>
					<div>
						<Input />
						<img onClick={resetCode} src={codeUrl} alt="validationCode" />
					</div>
				</Form.Item>

				<Form.Item<FieldType>
					name="remember"
					valuePropName="checked"
					wrapperCol={{ offset: 8, span: 16 }}
				>
					<Checkbox>Remember me</Checkbox>
				</Form.Item>

				<Form.Item wrapperCol={{ offset: 8, span: 16 }}>
					<Button type="primary" htmlType="submit">
						Submit
					</Button>
				</Form.Item>
			</Form>
		</>
	);
}

export default App;

Prisma

数据库连接:https://docs.nestjs.com/recipes/prisma#set-the-database-connection

setup

pnpm add prisma
npx prisma init

设置数据库连接(Postgres)

初始化生成的 prisma/schema.prisma:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

.env

DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public"

使用 Prisma Migrate 创建数据库表

schema.prisma

model User {
  id    Int     @default(autoincrement()) @id
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int      @default(autoincrement()) @id
  title     String
  content   String?
  published Boolean? @default(false)
  author    User?    @relation(fields: [authorId], references: [id])
  authorId  Int?
}
npx prisma migrate dev --name init

prisma migrate dev 指令会生成 SQL 文件并在数据库中运行。

生成 Prisma Client

Prisma Client 提供 CRUD 接口来操作数据库中的模型字段。

pnpm add @prisma/client

安装时,Prisma 自动地调用了 prisma generate 命令。以后,每次更新 Prisma 模型字段时,都要使用该命令来更新 Prisma Client(node_modules/@prisma/client)。

在 NestJS 服务中使用 Prisma Client