一年经验的全栈程序员,目前头发健在,但不知道能撑多久。
目录
2. 预处理阶段 preParsing、preValidation
前言
作为前端开发者,当你需要搭建一个后端服务时,SpringBoot可能不是你最自然的选择。本文将介绍如何使用Fastify这个轻量级、高性能的Node.js框架来构建后端API,特别适合那些不想学习Java生态的前端开发者。
为什么选择Fastify而不是SpringBoot?
-
JavaScript/TypeScript原生支持 - 使用你已经熟悉的语言
-
开发效率高 - 不需要复杂的配置,快速启动项目
-
性能优异 - Fastify是Node.js中最快的web框架之一
-
前端友好 - JSON API开发体验流畅
-
轻量级 - 不需要庞大的Java生态系统
OK那么话不多说让我们开始吧
一、快速搭建Fastify项目
由于我之前写过一篇博客介绍Fastify项目帅哥美女请移步简单阅读一下,顺便给我点个赞吧
Express过时了?Fastify用3倍性能重新定义Node.js开发体验
谢谢帅哥美女们!!!!!!!
二、项目结构进行调整
1.创建一个src目录
这个目录主要存放路由目录(routes),插件目录(plugins),数据库目录(model),实体(entity)等后端主要代码。
2.config目录存放系统相关配置
index.js文件
export default {
host:"localhost",
port:3000,
}
redis.js
export default {
redis:{
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD || null,
db: process.env.REDIS_DB || 1
},
ttl: process.env.REDIS_TTL || 60 * 60 * 24, // 1 day
}
3 .app.js项目启动入口
4. server.js统一注册插件
server.js这个地方是统一注册插件的,app.js项目启动入口需要导入这个文件。
里面注册了fastify/jwt登录生成token
await fastify.register(fastifyJwt, {
secret: '722a9ef5e0e6d03776624f9ccba743e260bbda8dada80e2f40fb98abc2afd24d',
})
注册了fastify/cookie便于登陆验证
await fastify.register(cookie, {})
5.安装nodemon
nodemon 是 Node.js 的一个开发工具,用于在开发过程中自动重启 Node 应用,以提高开发效率。
npm install nodemon
需要在package.json加上
"scripts": {
"start": "node app.js",
"dev": "nodemon ./app.js"
},
这样就可以不属命令也可以启动项目,直接点击这个地方
三、登录验证以及接口验证
1.注册redis
在文件夹/src/plugins/0_redis.js
import fastifyRedis from "@fastify/redis";
import redisConfig from "../../config/redis.js";
import fp from 'fastify-plugin' //将普通函数包装成 Fastify 插件。
async function redis(fastify) {
await fastify.register(fastifyRedis, redisConfig.redis);
}
export default fp(redis, {
name: 'redis'
})
2.编写登录接口
在/src/routes/index.js文件下
代码如下:
import crypto from 'crypto';
export default async function (fastify, opts) {
const { redis } = fastify
fastify.post('/api/login',async (request, reply) => {
let body = request.body
let user = {username:"admin",password:"123456"} // 模拟用户信息
if (body.username != user.username) throw '用户不存在'
if (body.password != user.password) throw '登陆密码错误'
let token = fastify.jwt.sign({ username: body.username }) //生成token
user.token = token
reply.cookie('token', token,{ path: '/' });
reply.cookie('user', user.username,{ path: '/' });
await redis.set(user.username,JSON.stringify(user),'EX',3600)
return { code: 0, msg: '登陆成功' }
}).name = "用户登录"
fastify.post('/api/secretKey',async (request, reply) => {
// 生成一个 256 位的随机密钥(32 字节)
const secretKey = crypto.randomBytes(32).toString('hex');
console.log(secretKey);
return { secretKey };
}).name = "获取密钥"
}
3.编写接口路由验证
在文件夹/src/plugins/auth.js
代码如下
import fp from 'fastify-plugin'
export default fp(async function (fastify, opts) {
fastify.addHook('onRequest', async function (req, res) {
try{
if(req.url.startsWith('/api/login')) { //如果是登录接口,则不进行登录校验
res.user = {};
return;
}
if(req.url.indexOf('.') > -1) return;
let key = req.cookies['user'];
if (!key) throw '用户未登录,请先登录';
let user = await fastify.redis.get(key);
if(!user) throw '用户登录信息已过期,请重新登录';
res.user = JSON.parse(user);
if(res.user.token != req.cookies['token']) throw '用户已在其他机器登录,请重新登录';
}catch (e) {
throw e;
}
})
})
这样不管请求哪个接口除了登录接口全部都需要验证token并且也完成了单点登录功能。如果想知道为什么能这样实现的请看第四章内容。
四、fastify的生命周期
Fastify 的请求生命周期可以分为几个明确的阶段,每个阶段都有对应的钩子(hooks)让你可以插入自定义逻辑。下面我用通俗易懂的方式解释每个阶段:
钩子的定义:钩子使用 fastify.addHook
方法注册,允许你监听应用或请求/响应生命周期中的特定事件。你必须在事件触发之前注册一个钩子,否则事件将丢失。通过使用钩子,你可以直接与 Fastify 的生命周期进行交互。
1. 请求进入阶段onRequest
- 这是第一个触发的钩子
- 请求刚到达服务器,还没被路由匹配
- 适合做全局的请求日志记录、基础验证等
fastify.addHook('onRequest', (request, reply, done) => {
console.log('收到新请求:', request.url)
done()
})
2. 预处理阶段 preParsing、preValidation
preParsing
- 在解析请求体之前执行
- 可以修改原始请求数据流
- 适合做内容解压、解密等操作
preValidation
- 在验证请求参数之前执行
- 可以修改请求数据或添加额外验证
- 适合添加自定义验证逻辑
3. 处理阶段preHandler
- 在执行路由处理函数之前
- 最后一个可以修改请求或阻止请求的钩子
- 常用作权限检查、认证等
fastify.addHook('preHandler', (request, reply, done) => {
if (!request.user) {
return reply.code(401).send({ error: '未授权' })
}
done()
})
4. 路由处理User
- 这是你写的主要业务逻辑
- Fastify 会执行匹配路由的处理函数(就是到接口的地方)
5. 响应准备阶段preSerialization
- 在数据序列化(如转为JSON)之前
- 可以修改响应数据
- 适合添加统一的数据包装
fastify.addHook('preSerialization', (request, reply, payload, done) => {
done(null, { data: payload }) // 统一包装响应数据
})
6. 响应发送阶段onSend、onResponse
onSend
- 在发送响应给客户端之前
- 可以修改最终响应或添加头信息
- 适合添加缓存头、日志等
onResponse
- 响应已经发送给客户端
- 适合记录最终日志、清理资源等
fastify.addHook('onResponse', (request, reply, done) => {
console.log(`请求完成: ${request.method} ${request.url} - ${reply.statusCode}`)
done()
})
7. 错误处理onError
- 在整个生命周期中任何地方出错时触发
- 适合统一错误处理和日志记录
fastify.addHook('onError', (request, reply, error, done) => {
console.error('发生错误:', error)
done()
})
理解这些生命周期阶段可以帮助你在适当的位置添加中间件、验证、日志记录等逻辑,构建更健壮的应用程序
五、数据统一返回格式和日志记录
通过第四章的理解生命周期的定义所以我们想统一设置数据返回格式和日志记录就需要找到一个生命周期是请求完成并且拿到了返回数据的时间点,经过研究我们发现在响应准备阶段preSerialization以及拿到完整数据所以我是这样坐的
finally是留着写日志到数据库的,这样这个接口请求不够有没有问题都可以成功记录这个接口被访问的日志记录
总结
使用Fastify搭建后端项目,实现高效、低开销的API服务。博客内容涵盖JWT登录功能,确保用户身份安全验证;接口权限验证,保障数据访问控制;集成日志记录功能,便于问题排查与系统监控。整体架构简洁清晰,性能优越,适合现代化后端开发需求。
🙌 求点赞、收藏、关注!
如果这篇文章对你有帮助,不妨:
👍 点个赞 → 让更多人看到这篇干货!
⭐ 收藏一下 → 方便以后随时查阅!
🔔 加关注 → 获取更多 前端/后端/全栈技术深度解析!
你的支持,是我持续创作的最大动力! 🚀