
在这次演示中,我们来搭建一个支持 MongoDB 数据库 CRUD 操作的 Web 接口服务,用来进行博客文章的管理。
通过本实战案例,希望你会对数据库及 Web 开发有更深一步的理解。
接口设计
基于 RESTful 接口规范。
创建文章
- 请求路径:POST /articles
- 请求参数:Body
- title 标题
- description 简介
- body 内容
- tagList 标签列表
- 数据格式:application/json
请求体示例:
{
“article”: {
“title”: “How to train your dragon”,
“description”: “Ever wonder how?”,
“body”: “You have to believe”,
“tagList”: [“reactjs”, “angularjs”, “dragons”]
}
}

后端返回成功数据示例:
- 状态码:201
- 响应数据:
{
“article”: {
“_id”: 123,
“title”: “How to train your dragon”,
“description”: “Ever wonder how?”,
“body”: “It takes a Jacobian”,
“tagList”: [“dragons”, “training”],
“createdAt”: “2016-02-18T03:22:56.637Z”,
“updatedAt”: “2016-02-18T03:48:35.824Z”
}
}
获取文章列表
- 请求路径:GET /articles
- 请求参数(Query)
- _page:页码
- _size:每页大小
响应数据示例:
- 状态码:200
- 响应数据:
{
“articles”:[
{
“_id”: “how-to-train-your-dragon”,
“title”: “How to train your dragon”,
“description”: “Ever wonder how?”,
“body”: “It takes a Jacobian”,
“tagList”: [“dragons”, “training”],
“createdAt”: “2016-02-18T03:22:56.637Z”,
“updatedAt”: “2016-02-18T03:48:35.824Z”
},
{
“_id”: “how-to-train-your-dragon-2”,
“title”: “How to train your dragon 2”,
“description”: “So toothless”,
“body”: “It a dragon”,
“tagList”: [“dragons”, “training”],
“createdAt”: “2016-02-18T03:22:56.637Z”,
“updatedAt”: “2016-02-18T03:48:35.824Z”
}
],
“articlesCount”: 2
}
获取单个文章
- 请求路径:GET /articles/:id
响应数据示例:
- 状态码:200
- 响应数据:
{
“article”: {
“_id”: “dsa7dsa”,
“title”: “How to train your dragon”,
“description”: “Ever wonder how?”,
“body”: “It takes a Jacobian”,
“tagList”: [“dragons”, “training”],
“createdAt”: “2016-02-18T03:22:56.637Z”,
“updatedAt”: “2016-02-18T03:48:35.824Z”
}
}
更新文章
- 请求路径:PATCH /artilces/:id
- 请求参数(Body)
- title
- description
- body
- tagList
请求体示例:
- 状态码:201
- 响应数据:
{
“article”: {
“title”: “Did you train your dragon?”
}
}
响应示例:
{
“article”: {
“_id”: 123,
“title”: “How to train your dragon”,
“description”: “Ever wonder how?”,
“body”: “It takes a Jacobian”,
“tagList”: [“dragons”, “training”],
“createdAt”: “2016-02-18T03:22:56.637Z”,
“updatedAt”: “2016-02-18T03:48:35.824Z”
}
}
删除文章
- 接口路径:DELETE /articles/:id
响应数据:
- 状态码:204
- 数据:
{}
准备工作
mkdir article-bedcd api-servenpm init -ynpm i express mongodb
使用 Express 快速创建 Web 服务
const express = require('express')const app = express()const port = 3000app.get('/', (req, res) => {res.send('Hello World!')})app.listen(port, () => {console.log(`Example app listening at http://localhost:${port}`)})
路由设计
app.get('/', (req, res) => {res.send('Hello World!')})app.post('/articles', (req, res) => {res.send('post /articles')})app.get('/articles', (req, res) => {res.send('get /articles')})app.get('/articles/:id', (req, res) => {res.send('get /articles/:id')})app.patch('/articles/:id', (req, res) => {res.send('patch /articles/:id')})app.delete('/articles/:id', (req, res) => {res.send('delete /articles/:id')})
处理 Body 请求数据
// 配置解析请求体数据 application/json// 它会把解析到的请求体数据放到 req.body 中// 注意:一定要在使用之前就挂载这个中间件app.use(express.json())
错误处理
配置 CORS
封装数据库操作模块
创建文章
app.post('/articles', async (req, res, next) => {try {// 1. 获取客户端表单数据const { article } = req.body// 2. 数据验证if (!article || !article.title || !article.description || !article.body) {return res.status(422).json({error: '请求参数不符合规则要求'})}// 3. 把验证通过的数据插入数据库中// 成功 -> 发送成功响应// 失败 -> 发送失败响应await dbClient.connect()const collection = dbClient.db('test').collection('articles')article.createdAt = new Date()article.updatedAt = new Date()const ret = await collection.insertOne(article)article._id = ret.insertedIdres.status(201).json({article})} catch (err) {// 由错误处理中间件统一处理next(err)// res.status(500).json({// error: err.message// })}})
获取文章列表
app.get('/articles', async (req, res, next) => {try {let { _page = 1, _size = 10 } = req.query_page = Number.parseInt(_page)_size = Number.parseInt(_size)await dbClient.connect()const collection = dbClient.db('test').collection('articles')const ret = await collection.find() // 查询数据.skip((_page - 1) * _size) // 跳过多少条 10 1 0 2 10 3 20 n.limit(_size) // 拿多少条const articles = await ret.toArray()const articlesCount = await collection.countDocuments()res.status(200).json({articles,articlesCount})} catch (err) {next(err)}})
获取单个文章
app.get('/articles/:id', async (req, res, next) => {try {await dbClient.connect()const collection = dbClient.db('test').collection('articles')const article = await collection.findOne({_id: ObjectID(req.params.id)})res.status(200).json({article})} catch (err) {next(err)}})
更新文章
app.patch('/articles/:id', async (req, res, next) => {try {await dbClient.connect()const collection = dbClient.db('test').collection('articles')await collection.updateOne({_id: ObjectID(req.params.id)}, {$set: req.body.article})const article = await await collection.findOne({_id: ObjectID(req.params.id)})res.status(201).json({article})} catch (err) {next(err)}})
删除文章
app.delete('/articles/:id', async (req, res, next) => {try {await dbClient.connect()const collection = dbClient.db('test').collection('articles')await collection.deleteOne({_id: ObjectID(req.params.id)})res.status(204).json({})} catch (err) {next(err)}})
