Express不对Node.js已有的特性进行二次抽象,只是在它之上扩展了web应用所需的基本功能。内部使用的依旧是http模块,请求对象继承字http.IncomingMessage,响应对象继承自http.ServerResponse,所以node.js官网中的对应的方法可以通用
1.请求对象
2.响应对象
除了response.send(),response.write()也可以发送数据
也可以直接在结束响应的同时发送数据response.send(1)
路由设计Demo练习:
需求描述:实现对任务清单的CRUD接口服务
·查询任务列表 ---》GET/todos
·根据ID查询单个任务 ----》GET/todos/:id
·添加任务 ----》POST.todos
·修改任务 ---》PATCH/todos/:id
·删除任务 ---》DELETE/todos/:id
动态参数可以通过模板字符的写法,从请求参数中获取
app.get('/todos', (req, res) => {
res.send('get/todos');
});
// 动态路由的使用
app.get('/todos/:id', (req, res) => {
res.send(`get/todos/${req.params.id}`);
});
app.post('/todos', (req, res) => {
res.send(`post/todos`);
});
app.patch('/todos/:id', (req, res) => {
res.send(`patch/todos`);
});
app.delete('/todos', (req, res) => {
res.send(`delete/todos/${req.params.id}`);
});
express不限制数据的存储位置,模拟的时候咱们放在json文件中吧~
// 1.引入fs模块
const fs = require("fs");
const app = express();
// 2. 尽量使用异步读取json文件的数据,
// 异步:readFile('json文件路径','utf8',回调函数接口两个参数,一个err,一个data)
// 同步:readFileSync如果使用同步的话,则不接受回调函数作为参数,且会阻塞执行线程直到文件读取完成
//--------------接口1获取任务列表---------
// 同步写法如下:
app.get("/todos", (req, res) => {
try {
const data = fs.readFileSync("./db.json", "utf8");
const db = JSON.parse(data);
res.status(200).json(db.todos);
} catch (err) {
// 如果发生错误,返回500状态码并发送错误信息
res.status(500).json({ error: err.message });
}
});
// 异步写法如下:
app.get("/todos", (req, res) => {
const data = fs.readFile("./db.json", "utf8", (err, data) => {
if (err) {
// 如果发生错误,返回500状态码并发送错误信息
return res.status(500).json({ error: err.message });
}
const db = JSON.parse(data);
res.status(200).json(db.todos);
});
});
//--------------接口2根据参数传递的ID获取对应的列表的数据项---------
app.get("/todos/:id", (req, res) => {
// res.send(`get/todos/${req.params.id}`);
fs.readFile("./db.json", "utf8", (err, data) => {
if (err) {
return res.status(500).send({
error: err.message,
});
}
const db = JSON.parse(data);
const todo = db.todos.find(todo=>todo.id===Number.parseInt(req.params.id))
if (!todo) {
return res.status(404).send("找不到该条数据项");
}
res.status(200).send(todo)
});
});
插曲:
每次都要写fs.readFile ,json.parse太麻烦了,封装一下
将callback形式异步APi,通过node.js中的util工具中的promisify转换成promise的形式
- 根目录下创建db.js,进行代码封装
const fs = require("fs");
const { promisify } = require("util");
const path = require("path");
const readFile = promisify(fs.readFile);
const dbPath = path.join(__dirname, "./db.json");
exports.getDB = async () => {
const data = await readFile(dbPath, "utf8");
return JSON.parse(data);
};
- 在app.js中引入使用,且修改
const express = require("express");
// 1.引入fs模块
const fs = require("fs");
//2. 引入db.js中封装好的方法
const { getDB } = require("./db");
const app = express();
app.get("/todos", async (req, res) => {
try {
const db = await getDB();
res.status(200).json(db.todos);
} catch (err) {
return res.status(500).json({ error: err.message });
}
});
// 动态路由的使用
app.get("/todos/:id", async (req, res) => {
try {
const db = await getDB();
const todo = db.todos.find((todo) => todo.id === Number.parseInt(req.params.id));
if (!todo) {
return res.status(404).send("找不到该条数据项");
}
res.status(200).send(todo);
} catch (err) {
return res.status(500).json({ error: err.message });
}
});
//--------------接口3 写入列表新数据项---------
第三个接口,post请求体,1)获取客户端请求体参数 2)数据验证 3)验证通过则把数据储存到DB中 4)发送响应,告知成功
1)获取客户端请求体参数
a) application/json格式:
在app实例中挂载一下express.json()来配置解析表单请求体,然后通过request.body获取用户传递的请求体参数;
app.use(express.json())
b) application/x-www-form-urlencoded格式:
app.use(express.urlencoded())
2)获取客户端请求体参数 与 数据验证
// 1.引入express
const express = require("express");
//2. 引入db.js中封装好的方法
const { getDB, saveDB } = require("./db");
const app = express();
app.use(express.json());
app.use(express.urlencoded());
// 写入数据 req.body获取请求体数据, 配置先
app.post("/todos", async (req, res) => {
try {
const db = await getDB();
const reqBd = req.body;
console.log(req.body, "请求体");
// 如果传递的对象中不包含title,则提示用户输入不符合的参数了
if (!reqBd.title) {
return res.status(422).send({ error: "The field title is required" });
}
const lastTodoItem = db.todos[db.todos.length - 1];
db.todos.push({
id: lastTodoItem ? lastTodoItem.id + 1 : 1,
title: reqBd.title
});
await saveDB(db)
res.status(200).send("添加成功!");
} catch (err) {
res.status(500).send({ error: err.message });
}
});
3)存储封装
const fs = require("fs");
const { promisify } = require("util");
const path = require("path");
const readFile = promisify(fs.readFile);
const dbPath = path.join(__dirname, "./db.json");
const writeFile = promisify(fs.writeFile) //引入写入模块方法
// 读取封装
exports.getDB = async () => {
const data = await readFile(dbPath, "utf8");
return JSON.parse(data);
};
// 写入封装
exports.saveDB = async (db) => {
const data = JSON.stringify(db)
// 如果需要Json文件渲染有格式化则传递第三个参数,表示格式的空格
// const data = JSON.stringify(db,null,' ')
await writeFile(dbPath,data)
};
//--------------接口4 编辑列表数据项---------
app.patch("/todos/:id", async (req, res) => {
// 1.获取用户传入的数据
// 2.找到与之匹配的数据项
// 3.进行替换且告知客户端
try {
const reqBd = req.body;
const db = await getDB();
const todoItem = db.todos.find(
(todo) => todo.id === Number.parseInt(req.params.id)
);
if (!todoItem) {
return res.status(422).send("没查询到对应的数据项,无法进行修改~");
}
// Object.assign(要被修改的数据项,传入的数据项且与被修改匹配的数据项) 如果有找到就替换,没找到就新增一条,但我们的逻辑中不允许新增,直接return报错提示,如上
Object.assign(todoItem, reqBd);
await saveDB(db);
res.status(200).send("修改成功!");
} catch (err) {
res.status(500).send({ error: err.message});
}
});
//--------------接口5 删除列表数据项---------
app.delete("/todos/:id", async (req, res) => {
// res.send(`delete/todos/${req.params.id}`);
try {
const db = await getDB();
const todoItemIndex = db.todos.findIndex(
(todo) => todo.id === Number.parseInt(req.params.id)
);
if (todoItemIndex===-1) {
res.status(422).send("没有您要删除的数据项");
}
//根据下标删除
db.todos.splice(todoItemIndex,1);
await saveDB(db);
res.status(200).send('删除成功')
} catch (err) {
res.status(500).send({ error: err.message });
}
});