一. 初始化package.json
- 新建一个项目文件夹,在文件夹中启动终端,运行
npm init -y,会帮我们创建一个package.json文件 - 在package.json文件里加上一行
"type": "module",这样我们的node项目就是遵循ESM规范了 - 在package.json的scripts下面加上一行
"start": "nodemon index.js",如果之前没安装过nodemon,可以运行npm i -g nodemon全局安装,nodemon会监听项目文件变化并自动热更新,我们不需要每次修改代码后重新启动项目
二. 安装koa并启动http服务
- 运行
npm i koa - 通过koa创建并启动http服务,这里直接上代码了
// index.js
import koa from 'koa'
const host = 'localhost'
const port = 8080
// 创建一个 Koa 实例
const app = new koa()
/*
app.use() 方法用于注册中间件
中间件是处理 http 请求和响应的函数
当一个请求到达服务器时, 会从第一个中间件开始执行, 直到最后一个中间件
上下文对象 ctx(context) 包含了与当前 http 请求相关的所有信息
如: http方法、url、请求头、请求体、查询参数等
*/
app.use(async (ctx) => {
ctx.body = 'Hello, world!' // 响应体
})
// 启动服务器并在指定的 host 和 port 上监听连接请求
app.listen(port, host, () => {
console.log(`服务器已启动:http://${host}:${port}`)
})
- 关于koa中间件和洋葱模型
app.use的函数里除了ctx上下文对象外,还有一个next函数的参数,如果在中间件中调用了next(),那么就会暂停当前中间件的执行,将控制权传递给下一个中间件,直到下一个中间件运行完,才会继续执行next()后面的代码,这就像是剥洋葱一样,处理请求时从外到内一层层的往里面剥(层层解析,验证,处理),然后再从内到外逐层加调料(封装,格式化),最终形成一道精美的料理。
// 在这段代码里,打印的顺序是1 3 5 4 2
app.use(async (ctx, next) => {
console.log(1)
await next() // 暂停,执行下一个中间件
console.log(2)
})
app.use(async (ctx, next) => {
console.log(3)
await next()
console.log(4)
})
app.use(async (ctx, next) => {
console.log(5)
ctx.body = 'Hello Koa'
})
三. 安装和配置路由
路由是指根据客户端发送的请求(包括请求method url)来调用与之匹配的请求处理函数
- 运行
npm i @koa/router安装 - 引入router并创建get请求,这里直接贴代码了,里面包括了get请求,query传参和path传参,以及路由分组
import Router from '@koa/router'
import koa from 'koa'
const host = 'localhost'
const port = 8080
// 创建一个 Koa 实例
const app = new koa()
const router = new Router()
// get请求 http://localhost:8080/list
router.get('/list', async (ctx) => {
const result = [1, 2, 3]
ctx.body = result
})
// get请求带查询参数 http://localhost:8080/query?name=张三&age=20
router.get('/query', async (ctx) => {
const { name, age } = ctx.query
ctx.body = `name: ${name}, age: ${age}`
})
// get请求带路径参数 http://localhost:8080/path/:id/:age
router.get('/path/:id/:age', async (ctx) => {
const { id, age } = ctx.params
ctx.body = `id: ${id}, age: ${age}`
})
app.use(router.routes()) //将定义在 router 对象中的路由规则添加到 app 实例中
// 路由分组
const userRouter = new Router({ prefix: '/user' })
// get请求 http://localhost:8080/user/list
userRouter.get('/list', async (ctx) => {
const result = [
{ name: '张三', age: 20 },
{ name: '李四', age: 25 },
]
ctx.body = result
})
app.use(userRouter.routes())
// 启动服务器并在指定的 host 和 port 上监听连接请求
app.listen(port, host, () => {
console.log(`服务器已启动:http://${host}:${port}`)
})
四. 处理post请求
- 安装post请求的body解析器
npm i @koa/bodyparser,BodyParser用于解析post请求中以json和url编码格式发送的数据 - 引入和使用,直接上代码
import BodyParser from '@koa/bodyparser'
app.use(BodyParser()) // 解析post请求的json或url请求体
router.post('/login', async (ctx) => {
const { username, password } = ctx.request.body
if (username === 'admin' && password === '123456') {
ctx.body = 'success'
} else {
ctx.body = 'fail'
}
})
五.允许跨域请求
- 安装
npm i @koa/cors - 引入和使用,这样就不会出现跨域请求报错了
import Cors from '@koa/cors'
app.use(Cors()) // 允许跨域请求
六. 连接mysql数据库
- 安装mysql2库
npm i mysql2 - 连接数据库并写一个根据sql的查询函数
import mysql from 'mysql2'
const config = {
// 服务器地址
host: 'localhost',
// 端口
port: '3306',
// 数据库名
database: 'test-db',
// 用户名
username: 'root',
// 密码
password: 'password',
}
const pool = mysql.createPool({
host: config.host, // 地址
port: config.port, // 端口号
user: config.username, // 用户名
password: config.password, // 密码
database: config.database, // 目标数据库
})
const query = (sql) => {
return new Promise((resolve, reject) => {
pool.execute(sql, (err, results) => {
if (err) {
reject(err)
}
resolve(results)
})
})
}
export default query
- 在接口处使用sql语句调用query函数即可查询数据库
router.get('/list', async (ctx) => {
const sql = `select * from tb_user`
const res = await query(sql)
console.log(res)
ctx.body = res
})
七. 使用sequelize连接数据库并实现增删改查
上面那段是通过手写sql然后执行的方式操作数据库,这种方式有很多弊端,而且sql一旦写错了后果很严重,接下来会演示通过sequelize这个库去操作数据库
- 安装sequelize
npm i sequelize - 新建models文件夹,用来存放数据库模型相关的文件
- 在models文件夹下面新建user.js,用来定义用户表,user.js中写入如下代码
// models/user.js
// 定义了一个tb_user的数据模型,有id username password name这四个字段,对应数据库里的tb_user表
export default (sequelize, DataTypes) => {
return sequelize.define(
'tb_user',
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, // 自增主键
username: { type: DataTypes.STRING, allowNull: false },
password: { type: DataTypes.STRING, allowNull: false },
name: { type: DataTypes.STRING, allowNull: false },
},
{ tableName: 'tb_user' }
)
}
- 在models文件夹下面新建index.js文件,在里面配置sequelize连接
// models/index.js
import { Sequelize } from 'sequelize'
const database = 'test-db'
const username = 'test-db'
const password = '123456'
const sequelize = new Sequelize(database, username, password, {
host: 'localhost',
dialect: 'mysql',
logging: false,
})
const db = {}
db.Sequelize = Sequelize
db.sequelize = sequelize
// 加载模型
db.User = (await import('./user.js')).default(sequelize, Sequelize)
export default db
- 在app.js中连接并同步数据表
// app.js
try {
// 同步数据表,上面定义的tb_user表和表字段会同步到mysql数据库的tb_user表中
await db.sequelize.sync({ alter: true });
// 连接数据库
await db.sequelize.authenticate();
console.log('所有模型已成功同步,表已重新创建,数据库已连接成功');
} catch (err) {
console.error('数据库连接失败:', err);
}
- 通过sequelize实现增删改查
import db from './models/index.js';
// 创建用户
router.post('/users', async (ctx) => {
const { username, password, name } = ctx.request.body;
const user = await db.User.create({ username, password, name });
ctx.body = user;
});
// 获取所有用户
router.get('/users', async (ctx) => {
const users = await db.User.findAll();
ctx.body = users;
});
// 获取单个用户
router.get('/users/:id', async (ctx) => {
const user = await db.User.findByPk(ctx.params.id);
if (user) {
ctx.body = user;
} else {
ctx.status = 404;
ctx.body = { message: 'User not found' };
}
});
// 更新用户
router.put('/users/:id', async (ctx) => {
const { username, password, name } = ctx.request.body;
const updated = await db.User.update({ username, password, name }, {
where: { id: ctx.params.id }
});
if (updated[0] > 0) {
ctx.body = { message: 'User updated' };
} else {
ctx.status = 404;
ctx.body = { message: 'User not found' };
}
});
// 删除用户
router.delete('/users/:id', async (ctx) => {
const deleted = await db.User.destroy({
where: { id: ctx.params.id }
});
if (deleted > 0) {
ctx.body = { message: 'User deleted' };
} else {
ctx.status = 404;
ctx.body = { message: 'User not found' };
}
});