前言
- 很久、很久、很久没有更新技术文章了!
- 突然想起自己本来就是做技术的,那就利用自己会的东西,给我女儿留一些有意义的东西。
- 于是打算搭建一个网站,用来记录她的成长历程。
后端基础搭建
环境准备
js
// 系统
Windows 10
// 编辑器
VS Code
// 接口测试
Postman
// node
v21.7.1
// npm
10.5.0
// Terminal
Git Bash
- 之前都是使用的
Mac
,所以终端使用的是Git Bash
,你也可以使用Windows
自带的或者按自己习惯安装其他的。
项目初始化
js
mkdir koa-app
cd koa-app
npm init -y
- 安装 Koa
js
// 2.15.2
npm i Koa
- 创建最简单的
Koa
项目,创建index.js
文件,作为项目的入口文件。
js
// index.js
const Koa = require('koa')
const app = new Koa()
app.use(async (ctx) => {
ctx.type = 'text/plain'
ctx.body = 'Hello Koa!'
})
const port = process.env.PORT || 3000
app.listen(port, () => {
console.log(`Server is running on port ${port}`)
})
- 在项目根目录执行
node ./index.js
启动项目,可在浏览器打开 http://localhost:3000/ 查看运行结果,或使用Postman
用GET
请求访问http://localhost:3000/
。
添加路由
- 安装路由
js
// 12.0.1
npm i @koa/router
- 修改
index.js
文件
js
// index.js
const Koa = require('koa')
+ const Router = require('@koa/router')
+ const router = new Router()
- app.use(async (ctx) => {
- ctx.type = 'text/plain'
- ctx.body = 'Hello Koa!'
- })
+ router.get('/hello', async (ctx) => {
+ ctx.body = 'Hello Koa!!!'
+ })
+ app.use(router.routes()).use(router.allowedMethods())
...
- 重新启动项目,可在浏览器打开 http://localhost:3000/hello 查看运行结果,或使用
Postman
用GET
请求访问http://localhost:3000/hello
。
添加热更新
- 每次修改文件后都需要停止并重启服务,比较麻烦。接入
nodemon
可以监听文件变化自动重启服务。 - 注:nodemon 是全局安装,不需要安装在项目中。
js
// 3.1.0
npm i -g nodemon
- 修改
package.json
文件中scripts
如下:
js
// package.json
{
...,
"scripts": {
+ "start": "nodemon ./index.js",
- "test": "echo \"Error: no test specified\" && exit 1"
},
...
}
- 使用
npm start
或yarn start
启动项目即可。
- 没有安装
yarn
的,执行npm i -g yarn
全局安装即可。
添加 ESlint
和 Prettier
- 为了后续开发方便、增强代码可读性,先把代码风格定好。
- 首先在
VS Code
中把相应的扩展装上。 - 在项目中安装相关的依赖
js
// 8.57.0、3.2.5、9.1.0、5.1.3
npm i eslint prettier eslint-config-prettier eslint-plugin-prettier --save-dev
- 在根目录添加
.eslintrc.js
文件并添加以下配置:
js
// .eslintrc.js
module.exports = {
env: {
node: true,
es2021: true,
},
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
parserOptions: {
ecmaVersion: 12,
},
rules: {
'prettier/prettier': ['error', { endOfLine: 'auto' }],
},
}
- 在根目录添加
.prettierrc
文件并添加以下配置
js
// .prettierrc
{
"singleQuote": true,
"semi": false,
"tabWidth": 2,
"trailingComma": "all",
"proseWrap": "never"
}
- 在
VS Code
中可开启保存自动格式化,也可每次自己手动格式化,看个人习惯。
支持接收 POST
数据
Postman
在Headers
配置中添加Content-Type
为application/json
,请求方式为POST
。- 在项目需要安装依赖
js
// 4.4.1
npm i koa-bodyparser
- 修改 index.js 文件
js
// index.js
...
+ const bodyParser = require('koa-bodyparser')
const app = new Koa()
+ app.use(bodyParser())
+ router.post('/login', async (ctx) => {
+ const { username, password } = ctx.request.body
+ ctx.body = { username, password }
+ })
- 使用
Postman
用POST
请求访问http://localhost:3000/login
。
调整目录结构
- 在根目录新建
src
文件夹,移动index.js
到src
目录; - 在
src
目录下新建routes
文件,用于存放不同模块的接口文件; - 在
routes
目录下新建user.js
文件,用于存放用户相关的接口。
- 调整后目录结构如下:
js
|- node_modules
|- src
|- routes
|- user.js
|- index.js
|- .eslint.js
|- .prettierrc
|- package-lock.json
|- package.json
- 注:由于调整了入口文件的位置,所以
package.json
中的启动命令需要修改。
js
// package.json
{
...,
"scripts": {
- "start": "nodemon ./index.js",
+ "start": "nodemon ./src/index.js",
},
...
}
- 拆分原来的
index.js
文件 - 拆分后的
src/index.js
文件,如下:
js
// src/index.js
const Koa = require('koa')
const bodyParser = require('koa-bodyparser')
const userRouter = require('./routes/user')
const app = new Koa()
app.use(bodyParser())
app.use(userRouter.routes()).use(userRouter.allowedMethods())
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})
- 新建的
src/routes/user.js
,如下:
js
// src/routes/user.js
const Router = require('@koa/router')
const router = new Router()
router.get('/hello', async (ctx) => {
ctx.body = 'Hello Koa!!!'
})
router.post('/login', async (ctx) => {
const { username, password } = ctx.request.body
ctx.body = { username, password }
})
module.exports = router
配置路径别名
- 在项目中我们会拆分不同的文件,拆分后就涉及到文件导入,如果没有路径别名的话,我们可能会写很多像下面这样的导入语句:
js
// 示例
const test = require('../../../test')
const mock = require('../../../../mock')
...
- 像上面这样的导入语句我们在看的时候就很难知道原始文件在哪个目录下,可读性非常的差。于是我们可以添加路径别名相关的配置。
Koa
中我们可以安装module-alias
来配置路径别名
js
// 2.2.3
npm i module-alias --save-dev
- 修改
package.json
文件
js
// package.json
{
...,
"_moduleAliases": {
"@src": "src",
"@constants": "src/constants",
"@routes": "src/routes",
"@utils": "src/utils"
}
}
- 修改
src/index.js
文件,文件顶部添加require('module-alias/register')
js
// src/index.js
+ require('module-alias/register')
...
- 配置好后,我们就可以使用刚才声明好的路径别名来导入对应路径下的文件了。
js
// 示例
// old
const test = require('../../../test')
const mock = require('../../../../mock')
// new
const test = require('@xxx/test')
const mock = require('@xxx/mock')
目录结构延伸
- 在配置别名时,可以看到我在
routes
同级又添加了两个目录constants
:用于存放项目中多处(2处及以上)使用的一些常量;utils
:用于存放一些项目中使用的通用函数(日期处理、数据处理等)。
使用环境变量
- 由于考虑一些变量在项目中使用,但又不想导入(例如:服务启动的
PORT
、全局路由的prefix
、token
加密的secret_key等
),所以我引入了dotenv
:
js
// 16.4.5
npm i dotenv
- 在项目根路径新建
.env
文件,并添加以下代码:
js
// .env
PORT = 3000
SEREAT_KEY = ''
- 修改
src/index.js
文件,文件顶部添加require('dotenv').config()
js
// src/index.js
+ require('dotenv').config()
...
- 在项目中使用
process.env[key]
即可拿到对应的值,不需要引入文件。
添加登录及登录验证
- 既然是网站那一般就会涉及到登录,网站的后台管理端、网站管理员登录。先使用
mock
的用户数据实现一个简易的登录流程。 - 我用的是
jsonwebtoken
来做登录及登录验证相关的操作,安装jsonwebtoken
:
js
// 9.0.2
npm i jsonwebtoken
- 封装登录及登录验证相关的方法:
js
// utils/tokenOpt.js
const jwt = require('jsonwebtoken')
// '10s': 十秒
// '30m': 三十分钟
// '1d': 一天
// '2h': 两小时
// '1w': 一周(与 '7d' 等效)
// '2d 6h': 两天六小时
// '1w 3d': 一周三天
const createToken = (user) => {
return jwt.sign(
{ id: user.id, username: user.username },
process.env.SEREAT_KEY,
{ expiresIn: '1d' },
)
}
const verifyToken = (ctx, token, resolve, reject) => {
jwt.verify(token, process.env.SEREAT_KEY, (err, decoded) => {
if (err) {
reject(err)
} else {
resolve(decoded)
}
})
}
module.exports = { createToken, verifyToken }
- 修改
src/routes/user.js
文件为用户相关接口
js
// src/routes/user.js
const Router = require('koa-router')
const router = new Router({
prefix: '/user', // 设置前置路由
})
router.post('/info', async (ctx) => {
const { username, password, query } = ctx.request.body
ctx.body = { username, password, query }
})
module.exports = router
- 新建
src/routes/login.js
文件用于登录
js
// src/routes/login.js
const Router = require('koa-router')
const { createToken } = require('@utils/tokenOpt')
const router = new Router()
const users = [ // 模拟的用户数据
{ id: 1, username: 'user1', password: 'password1' },
{ id: 2, username: 'user2', password: 'password2' },
]
router.post('/login', async (ctx) => {
const { username, password } = ctx.request.body
const user = users.find(
(user) => user.username === username && user.password === password,
)
if (user) {
const token = createToken(user)
ctx.body = { token }
} else {
ctx.status = 401
ctx.body = { error: 'Invalid username or password' }
}
})
module.exports = router
- 修改
src/index.js
文件,引入新增的routes
文件,注册路由。注:Koa
执行是洋葱模型,所以这里我们要注意注册路由的顺序。
js
// src/index.js
const userRouter = require('@routes/user')
+ const loginRouter = require('@routes/login')
app.use(bodyParser())
+ app.use(loginRouter.routes(), loginRouter.allowedMethods())
app.use(userRouter.routes(), userRouter.allowedMethods())
- 到这儿,只是用到生成
token
,其他接口并没有做token
验证,所以我们在登录后的接口还需要加一个token
验证拦截。当然,如果有不需要验证的接口,你可以放到验证中间件之前,或者写到token
验证中间的白名单中。添加src/utils/authenticate.js
用户验证token
。
js
// src/utils/authenticate.js
const { verifyToken } = require('@utils/tokenOpt')
const authenticate = async (ctx, next) => {
const { authorization } = ctx.headers
if (!authorization) {
ctx.throw(401, 'Authentication token is required')
}
if (authorization && authorization.startsWith('Bearer ')) {
try {
const token = authorization.substring(7)
ctx.token = token
const decodedInfo = await new Promise((resolve, reject) => {
verifyToken(ctx, token, resolve, reject)
})
ctx.userInfo = decodedInfo
} catch (error) {
ctx.throw(401, 'Invalid token')
}
await next()
} else {
ctx.throw(401, 'Invalid token')
}
}
module.exports = authenticate
- 在
src/index.js
文件中使用token
验证中间件
js
app.use(loginRouter.routes(), loginRouter.allowedMethods())
+ app.use(authenticate)
app.use(userRouter.routes(), userRouter.allowedMethods())
- 运行项目试试
- 登录获取
token
- 不携带
token
获取用户信息 - 使用无效
token
获取用户信息 - 使用有效
token
获取用户信息
总结
- 至此,我们后端基础的框架已经完成了,回头看看我们主要做了些什么
- 初始化
Koa
项目框架 - 支持热更新启动
- 代码支持
ESlint
和Prettier
美化 - 文件导入支持别名配置
- 支持多种请求方式
- 可使用环境变量
- 登录、登录验证
- 初始化
- 最终的目录结构:
js
|- node_modules
|- src
|- constants
|- routes
|- user.js
|- index.js
|- utils
|- authenticate.js
|- createOpt.js
|- .env
|- .eslint.js
|- .prettierrc
|- package-lock.json
|- package.json
后续计划
- 支持
GraphQL
- 环境区分
- 全局路由
prefix
- 返回
JSON
数据 - 数据库
- ...
最后
- 后台框架的源码我将在所有需要用到的东西都搭建好之后,放在我的 github 上。如果需要这个版本的,也可以私信我,私信可能回复不及时,还请理解。
- 有什么问题或想要在项目中增加什么配置欢迎大家评论区留言交流。
往期精彩
- Node.js 版本管理工具 n 最全使用手册
- 磕磕绊绊的 4 年前端er,一次含泪总结
- 前端还不会 Nginx 吗?快来学起来
- 金九前端面试总结!
- 从0搭建Vite + Vue3 + Element-Plus + Vue-Router + ESLint + husky + lint-staged
- 「前端进阶」JavaScript手写方法/使用技巧自查
- 公众号打开小程序最佳解决方案(Vue)
「点赞、收藏和评论」
❤️关注+点赞收藏+评论+分享❤️,手留余香,谢谢🙏大家。