# json-server 服务端 API 接口开发,实现数据的增删查改
TIP
在前端项目开发中,数据一般通过后端接口获得。如果我们自己在写一些项目练手时,需要一些数据接口,而又没有后端支持,就可以通过json-server
以零代码的方式获得一个完整的 RESTFul API。
json-server 实现的接口,可以完成对数据的查询、添加、修改、删除操作。
通俗来说,json-server
就是用来模拟服务端接口数据,在本地搭建一个 JSON 服务,自已生产接口测试数据。
# 一、json-server 基础用法
TIP
本小节主要学习 json-server 的基础用法
- 安装 json-server
- 创建 json 数据源
- 启动 json-server
- CLI(命令行界面)使用
- json-server 配置文件
json-server 官方使用教程:https://www.npmjs.com/package/json-server (opens new window)
# 1、安装 json-server
执行以下命令, 全局安装 json-server
npm install -g json-server # 全局安装
在执行以上命令前,需先创建
json-server
文件夹,然后再当前目录执行npm init -y
初始化package.json
文件
执行以下命令来查看json-server
安装的版本,来测是否安装成功
json-server -v # 0.17.3
# 2、创建 json 数据
TIP
在当前目录下新建data/db.json
文件,db.json
文件用来存放需要模拟的接口数据。
如下:
{
"posts": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "json-server22",
"author": "清心"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
}
],
"profile": {
"name": "typicode"
}
}
注:
数据格式以对象开始,json-server
把db.json
根节点的每一个属性 key 当作一个路由。
我们可以通过以下地址来访问对应接口数据,不过在访问前还需要启动json-server
服务。
http://localhost:3000/posts
http://localhost:3000/comments
http://localhost:3000/profile
# 3、启动 json-server
TIP
执行以下命令,就可以启动json-server
,json-server
的默认端口是 3000。
# 启动json-server服务,服务的端口号默认是 3000
json-server data/db.json
#或
json-server --watch data/db.json # --watch 表示监听文件的变化,当内容更新后不用重启服务
服务启动后,我们访问如下地址
http://localhost:3000/posts
链接,响应结果如下:
[
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "json-server22",
"author": "清心"
}
]
http://localhost:3000/posts/2
链接,响应结果如下
{
"id": 2,
"title": "json-server22",
"author": "清心"
}
注:
http://localhost:3000/posts/2
表示返回posts
下 id 为 2 的那一条数据
你可以自行测试以下链接,看得返回结果
http://localhost:3000/comments
http://localhost:3000/comments/1
http://localhost:3000/profile
# 4、CLI(命令行界面)使用
TIP
在命令行界面中启动json-server
服务,可以添加对应的选项
语法格式
json-server [options] <source> # [options] 选项为可选的 <source> json数据源
- 如果希望
data/json
中数据发生更新后,不需要重新启动服务,数据可自行更新,可以执行以下命令
json-server --watch data/db.json # --watch 表示监听文件的变化
- 如果我们想修改启动服务的端口号,可以添加
--port
选项
json-server --watch --port 8899 data/db.json # --watch 监听文件 --port 修改服务端口
- 为了方便,我们可以在当前项目
package.json
配置文件,添加如下脚本配置,然后执行npm run server
启动服务
"scripts": {
"server":"json-server --watch --port 8899 data/db.json"
}
常见的配置选项如下表
参数 | 简写 | 说明 |
---|---|---|
--config | -c | 指定配置文件,默认配置文件为“ json-server.json” |
--port | -p | 端口号,默认端口号为:3000 |
--host | -H | 主机地址,默认主机为“localhost” |
--watch | -w | 监听文件 ,布尔值 |
--routes | -r | 指定路由文件 |
--middlewares | -m | 指定中间件 |
--static | -s | 设置静态文件目录 |
--read-only | --ro | 只读 |
--no-cors | --nc | 禁用跨源资源共享 |
--no-gzip | --ng | 禁止 GZIP |
--snapshots | -S | 设置快照目录 |
--delay | -d | 设置反馈延时(ms) |
--id | -i | 设置数据的 id 属性(e.g. _id) |
--foreignKeySuffix | --fks | 设置外键后缀(如 post_id 中的_id) |
--quiet | -q | 禁止输出日志消息 |
--help | -h | 显示帮助信息 |
--version | -v | 显示版本号 |
注:
详细的选项配置,具体可参考:https://www.npmjs.com/package/json-server (opens new window) 地址中的 CLI usage 版块
# 5、json-server 配置文件
TIP
在当前项目根目录下新建json-server.json
文件,该文件为json-server
的默认配置文件。
我们可以在json-server.json
配置文件中来添加对应的配置选项,如下:
{
"port": 7788,
"watch": true
}
注:
在命令行中执行json-server data/db.json
时,会默认读取json-server.json
文件中的配置来启动服务。
# 二、数据查询
TIP
本小节我们来学习如何使用json-server
来查询数据,主要内容如下
- 基本查询
- 分页查询
- 排序查询
- 多字段排序
- 取局部数据
- 取符合某个范围数据
- 模糊查询
- 全文检索
在学习查询之前,我们在data/db.json
中添加如下json
数据,确保每条数据的 id 是唯一的。
{
"fruits": [
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits": "补充能量、润肠通便"
},
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
},
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
},
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
},
{
"id": 7,
"name": "草莓",
"price": 10,
"benefits": "调节免疫、美容养颜、保护视力"
},
{
"id": 8,
"name": "蓝莓",
"price": 8.5,
"benefits": "抗氧化、改善视力、增强记忆"
}
],
"users": {
"token": "xxx",
"userinfo": {
"username": "admin",
"nickname": "qxin",
"pwd": "123456"
}
}
}
注意:
查询内容是通过 GET 方法来获取想要的资源
# 1、基本查询
TIP
基本查询主要涉及:查询所有信息、根据 id 查询单条信息、根据字段过滤查询
# 1.1、查询所有信息
接口 http://localhost:3000/fruits
表示查询所有水果 ,返回结果如下:
"fruits":[
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits":"补充能量、润肠通便"
},
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits":"生津消食、缓解疲劳、补血益气"
},
....... 部分省略
]
# 1.2、根据 id 查询
接口 http://localhost:3000/fruits/5
表示查询 id = 5
的水果,返回结果为如下:
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
}
接口 http://localhost:3000/fruits?id=5
表示查询 id = 5
的水果,返回结果为如下:
[
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
}
]
# 1.3、根据字段过滤查询
接口 http://localhost:3000/fruits?price=4
查询价格 price=4 的水果,返回结果如下:
[
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits": "补充能量、润肠通便"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
}
]
接口 http://localhost:3000/fruits?price=4&name=橘子
查询价格为 4 同时 name 值为橘子的水果
[
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
}
]
接口 http://localhost:3000/users?userinfo.username=admin
查询用户信息中用户名为 admin 的用户,查询结果如下:
{
"token": "xxx",
"userinfo": {
"username": "admin",
"nickname": "qxin",
"pwd": "123456"
}
}
# 2、分页查询
TIP
在地址中携带_page
和_limit
参数,实现对返回的数据进行分页
_page
表示当前页码_limit
表示每页要显示的条数
接口 http://localhost:3000/fruits?_page=1&_limit=2
表示返回第一页数据,当前页显示条数为 2 条。返回结果如下:
[
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits": "补充能量、润肠通便"
},
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
}
]
接口 http://localhost:3000/fruits?_page=3&_limit=2
表示返回第三页数据,当前页显示条数为 2 条。返回结果如下:
[
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
}
]
# 3、排序查询
TIP
在地址中携带_sort
和_order
参数,实现对返回的数据排序
_sort
表示排序的字段_order
表示排序类型,asc 为升序 desc 为降序。默认值为升序
接口 http://localhost:3000/fruits?_sort=price&_order=asc
表示按价格升序,查询结果为
[
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits": "补充能量、润肠通便"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
},
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
},
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
},
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
},
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 8,
"name": "蓝莓",
"price": 8.5,
"benefits": "抗氧化、改善视力、增强记忆"
},
{
"id": 7,
"name": "草莓",
"price": 10,
"benefits": "调节免疫、美容养颜、保护视力"
}
]
# 4、多字段排序查询
TIP
如果要按多个字段来排序,则多个字段之间用逗号隔开。
http://localhost:3000/fruits?_sort=price,id&_order=desc,asc
表示选按价格降序,再按 id 升序,最终查询结果如下:
[
{
"id": 7,
"name": "草莓",
"price": 10,
"benefits": "调节免疫、美容养颜、保护视力"
},
{
"id": 8,
"name": "蓝莓",
"price": 8.5,
"benefits": "抗氧化、改善视力、增强记忆"
},
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
},
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
},
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
},
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits": "补充能量、润肠通便"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
}
]
# 5、取局部数据
TIP
在地址中携带_start
和_end
或_limit
参数,实现截取返回数据中的一部分数据。其工作原理完全按照Array.slice
方法。
_start
表示数组的起始下标(包含)_end
表示数组的结束下标(不包含)_limit
表示查询从起始下标查询多少条
接口 http://localhost:3000/fruits?_start=1&_end=4
返回第 2-4 条数据,相当于 Array.slice(1,4)
,结果如下
[
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
},
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
}
]
接口 http://localhost:3000/fruits?_start=3&_limit=4
返回第 3-6 条数据,查询结果如下:
[
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
},
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
},
{
"id": 7,
"name": "草莓",
"price": 10,
"benefits": "调节免疫、美容养颜、保护视力"
}
]
# 6、按范围查询数据
TIP
在地址栏中添加xxx_gte
和xxx_lte
来查询符合某个范围(range)内的数据。
xxx_gte
表示xxx
字段的值大于等于xxx_lte
表示xxx
字段的值小于等于
接口 http://localhost:3000/fruits?price_gte=8
表示查询价格大于等于 8 的水果,查询结果如下:
[
{
"id": 7,
"name": "草莓",
"price": 10,
"benefits": "调节免疫、美容养颜、保护视力"
},
{
"id": 8,
"name": "蓝莓",
"price": 8.5,
"benefits": "抗氧化、改善视力、增强记忆"
}
]
接口 http://localhost:3000/fruits?price_gte=6&price_lte=8.5
表示查询价格大于等于 6 且小于等于 8.5 的水果,查询结果如下:
[
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
},
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
},
{
"id": 8,
"name": "蓝莓",
"price": 8.5,
"benefits": "抗氧化、改善视力、增强记忆"
}
]
# 7、排除值查询
TIP
在地址栏中添加xxx_ne
参数来实现排序查询,xxx
表示查询的字段
http://localhost:3000/fruits?price_ne=6
查询价格不等于 6 的水果,相当于过滤掉价格=6 的水果
[
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits": "补充能量、润肠通便"
},
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 5,
"name": "苹果",
"price": 5,
"benefits": "降胆固醇、宁神安眠、缓解疲劳"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
},
{
"id": 7,
"name": "草莓",
"price": 10,
"benefits": "调节免疫、美容养颜、保护视力"
},
{
"id": 8,
"name": "蓝莓",
"price": 8.5,
"benefits": "抗氧化、改善视力、增强记忆"
}
]
# 8、模糊查询
TIP
在地址栏中添加xxx_like
参数来实现模糊查询。模糊查询是指该字段中只要包含指定的数据就可以。
接口 http://localhost:3000/fruits?name_like=子
用于查询水果名中包含"子"的水果,返回结果如下:
[
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
},
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
}
]
接口 http://localhost:3000/fruits?name_like=莓
用于查询水果名中包含"莓"的水果,返回结果如下:
[
{
"id": 7,
"name": "草莓",
"price": 10,
"benefits": "调节免疫、美容养颜、保护视力"
},
{
"id": 8,
"name": "蓝莓",
"price": 8.5,
"benefits": "抗氧化、改善视力、增强记忆"
}
]
# 9、全文检索
TIP
在地址栏中添加q
参数来实现全文检索。全文检索会检索每条数据的每个字段,只要某个字段中包含了指定的值,该条数据就会被返回。
接口 http://localhost:3000/fruits?q=6
返回结果如下:
[
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
},
{
"id": 4,
"name": "梨子",
"price": 6,
"benefits": "生津、清热、润燥、化痰"
},
{
"id": 6,
"name": "橘子",
"price": 4,
"benefits": "延缓衰老、预防动脉硬化、通便"
}
]
注:
观察上面结果可以发现,不管是 id 中包含 6 还是 price 中包含 6 都被查询出来了。
# 10、关联查询
TIP
在学习关联查询前,我们先在data/db.json
中添加如下数据
"posts": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
},
{
"id": 2,
"title": "json-server222",
"author": "typicode2222"
}
],
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
},
{
"id": 2,
"body": "some comment111111",
"postId": 1
},
{
"id": 3,
"body": "some comment2222",
"postId": 2
}
]
注:
- 以上 posts 字段表示发送的邮件信息,comments 字段表示对应邮件的评论。
- posts 字段与 comments 字段是通过 postId 来产生关联。
# 10.1、获取包含下级资源
TIP
获取包含下级资源的数据,使用_embed
接口 http://localhost:3000/post/1?_embed=comments
表示查询邮件 id 为 1 的邮件信息和对应的评论信息(即 postId = 1
)的评论
查询结果如下:
{
"id": 1,
"title": "json-server",
"author": "typicode",
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
},
{
"id": 2,
"body": "some comment111111",
"postId": 1
}
]
}
接口 http://localhost:3000/post?_embed=comments
表示查询所有邮件和邮件对应的评论。
查询结果如下:
[
{
"id": 1,
"title": "json-server",
"author": "typicode",
"comments": [
{
"id": 1,
"body": "some comment",
"postId": 1
},
{
"id": 2,
"body": "some comment111111",
"postId": 1
}
]
},
{
"id": 2,
"title": "json-server222",
"author": "typicode2222",
"comments": [
{
"id": 3,
"body": "some comment2222",
"postId": 2
}
]
}
]
# 10.2、获取包含上级资源
TIP
获取包含上级资源的数据,使用_expand
接口 http://localhost:3000/comments/1?_expand=post
表示获取评论 id 为 1 对应的邮件及邮件评论。
查询结果如下:
{
"id": 1,
"body": "some comment",
"postId": 1,
"post": {
"id": 1,
"title": "json-server",
"author": "typicode"
}
}
接口 http://localhost:3000/comments/?_expand=post
获取所有评论及评论对应的邮件。
查询结果中下:
[
{
"id": 1,
"body": "some comment",
"postId": 1,
"post": {
"id": 1,
"title": "json-server",
"author": "typicode"
}
},
{
"id": 2,
"body": "some comment111111",
"postId": 1,
"post": {
"id": 1,
"title": "json-server",
"author": "typicode"
}
},
{
"id": 3,
"body": "some comment2222",
"postId": 2,
"post": {
"id": 2,
"title": "json-server222",
"author": "typicode2222"
}
}
]
# 10.3、获取嵌套资源
接口 http://localhost:3000/posts/1/comments
表示获取邮件 id=1
对应的所有评论,查询结果如下:
[
{
"id": 1,
"body": "some comment",
"postId": 1
},
{
"id": 2,
"body": "some comment111111",
"postId": 1
}
]
# 三、添加、更新、删除数据
TIP
json-server 里面明确要求
- 获取(查询)数据,发 get 请求
- 新增数据,发 post 请求
- 修改数据,发 put 或 patch 请求
- 删除数据,发 delete 请求
# 1、添加数据
TIP
post 请求,常用来创建一个新的资源,即新增一条数据
案例
添加一种新的水果信息到"fruits"对应的数组中。
<script setup>
import axios from "axios";
async function getData() {
// post 请求,用来向db.json的fruits字段中添加一条数据
const reslut = await axios.post("http://localhost:3000/fruits", {
// 添加的数据内容,不需要带id
name: "火龙果",
price: 6.3,
benefits: "排毒护胃、促进消化、清宿便",
});
console.log(reslut);
}
</script>
<template>
<button @click="getData">发请求</button>
</template>
请求发送成功后,data/db.json
的"fruits"字段中添加了如下数据
"fruits": [
.......省略部分
{
"name": "火龙果",
"price": 6.3,
"benefits": "排毒护胃、促进消化、清宿便",
"id": 9
}
],
新添数据的 id,会被自动计算出来,不需要指定新添加数据的 id 属性
# 2、更新数据
TIP
- put 方法,常用来更新已有资源,他会更新整个资源对象,前端没有给出的字段,会自动清空。
- patch 方法,常用来更新已有资源,他相当于打补丁方式,只在原有基础上更新传入的那部分数据
- 在发送请求的时候,请求的地址中要携带 id(唯一标识),这样才知道要修改的是那条数据。
- 请求的地址不能以
http://www.xxx.com/fruits?id=9
的形式,只能以http://www.xxx.com/fruits/9
的形式
# 2.1、put 请求
TIP
将 id = 9
的水果的价格修改成 0
<script setup>
import axios from "axios";
async function getData() {
// put 请求,用来修改db.json的fruits字段中的某条数据。
const reslut = await axios.put("http://localhost:3000/fruits/9", {
price: 0,
});
console.log(reslut);
}
</script>
<template>
<button @click="getData">发请求</button>
</template>
请求发送成功后,data/db.json
的 "fruits" 字段中 id = 9
的那一项数据变成如下:
"fruits": [
.......省略部分
{
"price": 0,
"id": 9
}
],
# 2.2、patch 请求
TIP
将 id = 9
的水果的价格修改成 0
<script setup>
import axios from "axios";
async function getData() {
// put 请求,用来修改db.json的fruits字段中的某条数据。
const reslut = await axios.patch("http://localhost:3000/fruits/9", {
price: 0,
});
console.log(reslut);
}
</script>
<template>
<button @click="getData">发请求</button>
</template>
请求发送成功后,data/db.json
的"fruits"字段中 id = 9
的那一项数据变成如下:
"fruits": [
.......省略部分
{
"name": "火龙果",
"price": 0,
"benefits": "排毒护胃、促进消化、清宿便",
"id": 9
}
],
# 3、删除数据
TIP
- delete 请求,用来删除数据,也是根据 id(唯一标识)来查找对应数据,并删除。
- 在发送请求的时候,请求的地址中要携带 id(唯一标识),这样才知道要删除的是那条数据。
<script setup>
import axios from "axios";
async function getData() {
// delete 请求,用来删除db.json的fruits字段中id=9的那一条数据
const reslut = await axios.delete("http://localhost:3000/fruits/9");
console.log(reslut);
}
</script>
<template>
<button @click="getData">发请求</button>
</template>
注:
请求发送成功后,最终 "fruits" 数组中 id=9
的那一条数据被删除。
# 四、静态资源
TIP
json-server 也提供了静态文件服务器,我们只需要在根目录下创建public
文件夹,把对应的静态资源(如:HTML、JS、CSS、图片等)存放在该文件夹下即可。
# 1、当前目录结构
server
├─ data
│ └─ db.json # json数据源
├─ json-server.json # json-server 配置文件
├─ package.json
└─ public # 静态资源目录
├─ basic.css
├─ imgs
│ └─ qq.png
└─ index.html
注:
执行 json-server data/db.json
命令启动服务后,我们就可以通过
http://localhost:3000/imgs/qq.png
访问到图片http://localhost:3000/basic.css
访问到basic.css
文件http://localhost:3000/
或http://localhost:3000/index.html
访问到index.html
页面
# 2、修改静态文件目录
TIP
可以使用--static
来修改静态文件的目录
json-server --static dist data/db.json # 静态资源目录为 dist目录
执行以上命令后,静态资源的访问目录由原来的public
改为了dist
。我们可以把 public 文件夹名改为 dist。这样我们依然可以通过
http://localhost:3000/imgs/course.jpg
访问到图片http://localhost:3000/basic.css
访问到basic.css
文件http://localhost:3000/
或http://localhost:3000/index.html
访问到index.html
页面
# 五、生成随机数据
TIP
在 json-server 中,我们除了用 JSON 来模拟返回的接口数据,我们还可以使用 JS 来编程式的创建数据。
有时我们需要创建大量的数据,为了方便我们创建数据,我们可以使用MockJS
来帮助我们生成大最的随机数据。
# 1、编程式地创建数据
TIP
我们也可以使用 JS 而不是 JSON 文件来编程式地创建数据。
在data/db.js
文件中创建数据,代码如下:
注意: 方法返回的数据为一个对象,对象的
key
为访问的路由
module.exports=()=>{
const data={
users:[]
}
for(let i=0;i<5;i++){
data.users.push({
id:i,
username:'user'+i
})
}
return data
}
执行json-server data/db.js
启动服务,访问接口 http://localhost:3000/users
返回数据结果如下:
[
{
"id": 0,
"username": "user0"
},
{
"id": 1,
"username": "user1"
},
{
"id": 2,
"username": "user2"
},
{
"id": 3,
"username": "user3"
},
{
"id": 4,
"username": "user4"
}
]
# 2、MockJS 生成数据
TIP
Mock.js 是一个功能强大的 JavaScript 库,用于模拟接口请求和生成随机数据。
- MockJS 官网地址:http://mockjs.com/ (opens new window)
- MockJS 使用教程:https://github.com/nuysoft/Mock/wiki/Getting-Started (opens new window)
- MockJS 生成数据示例:http://mockjs.com/examples.html (opens new window)
Mock 的安装和基本用法
- 执行以下命令,安装 mockjs
npm install mockjs
- 引入 mock.js (Node 中)
const Mock = require("mockjs");
- 利用 MockJS 随机生成 3-5 条用户信息 (
data/db.js
文件内容如下)
const Mock = require("mockjs");
module.exports = () => {
return Mock.mock({
"users|3-5": [
{
"id|+1": 1,
name: "@name",
"age|20-100": 100,
},
],
});
};
执行 json-server data/db.js
启动服务,访问 http://localhost:3000/user
接口,最终返回结果为
[
{
"id": 1,
"name": "Patricia Robinson",
"age": 79
},
{
"id": 2,
"name": "Brenda Gonzalez",
"age": 90
},
{
"id": 3,
"name": "Scott Anderson",
"age": 33
},
{
"id": 4,
"name": "Angela Thomas",
"age": 52
}
]
# 六、HTTP 状态码 与 业务状态码
TIP
了解实际开发中的 HTTP 状态码 与 业务状态码
# 1、HTTP 状态码
TIP
HTTP 响应状态码表明特定 HTTP 请求是否成功完成。状态码分为以下 5 类
- 信息响应:100-199
- 成功响应:200-299
- 重定向消息:300-399
- 客户端错误响应:400-499
- 服务端错误响应:500-599
# 2、业务状态码
TIP
用来表示返回的数据是有效还是无效。
有时我们需要拓展请求的状态。我们只需要在请求返回的数据中加一层,如下:
{
"code":0, // 业务状态码,名字和状态码自定义(一般公司都有自己的规范)
"message":"请求成功" // 状态码提示信息,表示当前状态码的意思
"data":{ // 业务需要用到的数据
"username":"admin",
}
}
在实际开发中,我们至少要定义两种业务状态码,一种表示成功,一种表示失败。
- 上面代码表示服务端响应了数据,响应状成为成功,能拿到有用的数据
- 下面代码表示服务端响应了数据,但时是失败状态,拿不到有用的数据。
{
"code":-1, // 业务状态码,名字和状态码自定义(一般公司都有自己的规范)
"message":"请求失败" // 状态码提示信息,表示当前状态码的意思
"data":null
}
# 七、json-server 作为模块使用
TIP
如果我们需要添加身份验证、验证或任何行为,可以将 json-server 作为模块与其他Express
(基于 NodeJs 平台的 web 应用开发框架)中间件结合使用,实现自定义路由和自定义逻辑处理(根据条件来决定响应数据)
# 1、基本用法
TIP
我们通过搭建一个简单的 json 服务器来讲解 json-server 作为模块该如何使用。
项目目录结构
server
├─ app.js // 项目入口文件
├─ data
│ └─ getFruits.js // 返回数据
├─ db.js // 数据源
├─ package-lock.json
└─ package.json
项目创建步骤
- 新建 server 文件夹作为项目的根目录,然后在根目录下执行以下命令,初始化
package.json
文件
npm init -y
- 在当前项目中,执行以下命令安装
json-server
包
npm install json-server --save-dev
- 新建
/server/db.js
文件,用来创建数据
// getFruits方法会返回想要的数据
const getFruits = require("./data/getFruits");
// 响应数据添加响应状态字段
function responseData(data) {
return {
code: 0, // 业务状态码
msg: "请求成功",
data,
};
}
module.exports = () => {
return {
fruits: responseData(getFruits()),
};
};
- 为了方便后期数据的维护与管理,我们将生成不同接口的数据放在
/server/data
目录下,然后在db.js
文件中引入
// /server/data/getFruits.js 文件
module.exports = () => {
return [
{
id: 1,
name: "香蕉",
price: 4,
benefits: "补充能量、润肠通便",
},
{
id: 2,
name: "葡萄",
price: 6.6,
benefits: "生津消食、缓解疲劳、补血益气",
},
{
id: 3,
name: "柚子",
price: 6,
benefits: "降低血糖、助消化",
},
];
};
- 在根目录下新建
app.js
文件,作为项目的入口文件,用来创建服务器
const db = require("./db.js");
// 导入json-server
const jsonServer = require("json-server");
// 返回express服务器
const server = jsonServer.create();
// 返回json服务器路由
// router中的参数可以是一个json文件地址,也可以是一个对象。即: (path | object)
const router = jsonServer.router(db());
// 返回JSON服务器使用的中间件(用来处理静态资源的-默认为public目录下的静态资源)
const middlewares = jsonServer.defaults();
// 使用中间件
server.use(middlewares);
// 使用默认路由器
// server.use(router);
// 可以在路由前添加前缀,以下表示访问路由以/api开头
server.use("/api", router);
// 添加监听的端口
server.listen(8899, () =>
console.log("JSON Server is running at http://127.0.0.1:8899")
);
注意事项:
为jsonServer.router
函数提供的路径是相对于启动节点进程的目录的。
如果你从另一个目录运行上面的代码,最好使用绝对路径:
const path = require("path");
const router = jsonServer.router(path.join(__dirname, "db.json"));
- 执行
node app.js
来启动服务器,启动后访问http://127.0.0.1:8899/api/fruits接口
,最终返回内容如下:
{
"code": 0,
"msg": "请求成或",
"data": [
{
"id": 1,
"name": "香蕉",
"price": 4,
"benefits": "补充能量、润肠通便"
},
{
"id": 2,
"name": "葡萄",
"price": 6.6,
"benefits": "生津消食、缓解疲劳、补血益气"
},
{
"id": 3,
"name": "柚子",
"price": 6,
"benefits": "降低血糖、助消化"
}
]
}
# 2、nodemon 工具
TIP
nodemon 是一个工具,它可以帮助开发基于 Node.js 的应用程序,当检测到目录中的文件更改时,它会自动重新启动节点应用程序。
nodemon 不需要对您的代码或开发方法进行任何额外的更改。nodemon 是node
的替代包装器。要使用nodemon
,请在执行脚本时替换命令行中的单词node
。
nodemon 详细教程:https://www.npmjs.com/package/nodemon (opens new window)
- 执行以下命令安装 nodemon 作为开发依赖
npm install --save-dev nodemon
- 执行以下命令来启动服务器,就可以实现文件更新后,会自动重新启动节点应用程序
npx nodemon app.js # 启动服务器
# 3、自定义路由
TIP
我们可以使用 jsonServer.create()
方法返回的 express 服务器来自定义路由
// get请求 http://127.0.0.1:8899/api/getinfo
server.use("/api/getinfo", (req, res) => {
res.send({
msg: "自定义路由,get请求",
});
});
// post 请求 http://127.0.0.1:8899/api/addinfo
server.post("/api/addinfo", (req, res) => {
res.send({
msg: "自定义路由,post请求",
});
});
完整示例
在原示例的基础上,加上上面代码
const db = require("./db.js");
// 导入json-server
const jsonServer = require("json-server");
// 返回express服务器
const server = jsonServer.create();
// 返回json服务器路由
// router中的参数可以是一个json文件地址,也可以是一个对象。即: (path | object)
const router = jsonServer.router(db());
// 返回JSON服务器使用的中间件(用来处理静态资源的-默认为public目录下的静态资源)
const middlewares = jsonServer.defaults();
// 使用中间件
server.use(middlewares);
// 自定义路由
// get请求 http://127.0.0.1:8899/api/getinfo
server.use("/api/getinfo", (req, res) => {
res.send({
msg: "自定义路由,get请求",
});
});
// post 请求 http://127.0.0.1:8899/api/addinfo
server.post("/api/addinfo", (req, res) => {
res.send({
msg: "自定义路由,post请求",
});
});
// 使用默认路由器
// server.use(router);
// 可以在路由前添加前缀,以下表示访问路由以/api开头
server.use("/api", router);
// 添加监听的端口
server.listen(8899, () =>
console.log("JSON Server is running at http://127.0.0.1:8899")
);
# 4、自定义中间件
TIP
中间件(Middleware)是指业务流程的中间处理环节。
# 4.1、Express 中间件的调用流程
# 4.2、express 中间件的格式
TIP
Express
的中间件,本质上就是一个function
处理函数,函数的形参列表中,必须包含 next 参数。next
函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。- 自定义中间件,用来给响应体
res
添加success
和fail
两个方法,对响应成功和失败的数据添加状态码和提示信息。
server.use((req, res, next) => {
res.success = function (data) {
res.send({
code: 0,
msg: "请求成功!!",
data: data,
});
};
res.fail = function (data) {
res.send({
code: -1,
msg: "请求失败",
data: data,
});
};
next();
});
在处理 POST、PUT 和 PATCH 请求时,需要使用 body 解析器,将请求体 JSON 解析为 JS 对象
使用 json-server 自带的 bodyParse 解析器(中间件)
server.use(jsonServer.bodyParser);
# 4.3、完整应用
const db = require("./db.js");
// 导入json-server
const jsonServer = require("json-server");
// 返回express服务器
const server = jsonServer.create();
// 返回json服务器路由
// router中的参数可以是一个json文件地址,也可以是一个对象。即: (path | object)
const router = jsonServer.router(db());
// 返回JSON服务器使用的中间件(用来处理静态资源的-默认为public目录下的静态资源)
const middlewares = jsonServer.defaults();
// 使用中间件
server.use(middlewares);
// body解析器中间件,如果不添加,在post等请求中,没有办法通过req.body得到请求体数据
server.use(jsonServer.bodyParser);
// 自定义中间件
server.use((req, res, next) => {
res.success = function (data) {
res.send({
code: 0,
msg: "请求成功!!",
data: data,
});
};
res.fail = function (data) {
res.send({
code: -1,
msg: "请求失败",
data: data,
});
};
next();
});
// 自定义路由
// get请求 http://127.0.0.1:8899/api/getinfo
server.use("/api/getinfo", (req, res) => {
res.success({
msg: "自定义路由,get请求",
});
});
// post 请求 http://127.0.0.1:8899/api/addinfo
server.post("/api/addinfo", (req, res) => {
res.fail({
msg: "自定义路由,post请求",
});
});
// 使用默认路由器
// server.use(router);
// 可以在路由前添加前缀,以下表示访问路由以/api开头
server.use("/api", router);
// 添加监听的端口
server.listen(8899, () =>
console.log("JSON Server is running at http://127.0.0.1:8899")
);
执行npx nodemon app.js
命令启动服务器后
发送 get 请求 http://127.0.0.1:8899/api/getinfo
响应内容如下:
{
"code": 0,
"msg": "请求成功!!",
"data": {
"msg": "自定义路由,get请求"
}
}
发送 post 请求 http://127.0.0.1:8899/api/addinfo
响应内容如下:
{
"code": -1,
"msg": "请求失败",
"data": {
"msg": "自定义路由,post请求"
}
}
# 5、按功能模块化拆分
TIP
深入浅出按功能模块化拆分,按路由拆分,controller 自定义逻辑,数据源
# 5.1、拆分中间件
TIP
将中间件拆分到一个单独的文件夹middlewares
中单独管理
middlewares/responseData.middlewares.js
module.exports = (req, res, next) => {
res.success = function (data) {
res.send({
code: 0,
msg: "请求成功!!",
data: data,
});
};
res.fail = function (data) {
res.send({
code: -1,
msg: "请求失败",
data: data,
});
};
next();
};
然后在 app.js 中导入中间件,并使用
// 导入对响应数据添加状态和提示信息的中间件
const responseDataMiddlewares = require("./middlewares/responseData.middlewares.js");
// 自定义中间件
server.use(responseDataMiddlewares);
# 5.2、拆分路由
TIP
我们需要自定义的路由很多,我们可以把自定义路由单独放到一个routes.js
文件中
在 server/routes.js
中
// 对外暴露一个函数,函数接受express服务器作为参数
module.exports = (server) => {
// 自定义路由
// get请求 http://127.0.0.1:8899/api/getinfo
server.use("/api/getinfo", (req, res) => {
res.success({
msg: "自定义路由,get请求",
});
});
// post 请求 http://127.0.0.1:8899/api/addinfo
server.post("/api/addinfo", (req, res) => {
res.fail({
msg: "自定义路由,post请求",
});
});
};
- 然后在 app.js 中导入
routes.js
,调用该函数,并将 server 作为参数传入
// 导入routes.js
const customRouter = require("./routes.js");
// 自定义路由
customRouter(server);
# 5.3、controller 自定义逻辑
TIP
每个路由返回的数据不一样,有时需要根据请求的参数来做相关的筛选、验证等,我们把对数据的筛选、验证等功能在 controller 文件下单独来处理。
在 controller/getInfo.js
中
module.exports = (req, res) => {
// 相关逻辑处理
// 此处省略.......
res.success({
msg: "自定义路由,post请求",
});
};
在 controller/postInfo.js
中
module.exports = (req, res) => {
(req, res) => {
// 相关逻辑处理
// 此处省略.......
res.fail({
msg: "自定义路由,get请求",
});
};
};
server/routes.js
文件需要被修改成如下
const getInfo = require("./controller/getInfo.js");
const postInfo = require("./controller/postInfo.js");
module.exports = (server) => {
// get请求 http://127.0.0.1:8899/api/getinfo
server.use("/api/getinfo", getInfo);
// post 请求 http://127.0.0.1:8899/api/addinfo
server.post("/api/addinfo", postInfo);
};
# 5.4、数据源
TIP
每个路由返回的数据都不一样,我们可以把数据源分类存放在server/data
目录下,然后在对应的 controller 文件中引入。
在 server/data/getInfo.js
中
module.exports = () => {
return {
msg: "自定义路由,get请求",
};
};
在 server/data/postInfo.js
中
module.exports = () => {
return {
msg: "自定义路由,post请求",
};
};
此时 controller 下的文件内容需要修改如下
在 controller/getInfo.js
中
const getInfo = require("../data/getInfo.js");
module.exports = (req, res) => {
res.success(getInfo());
};
- controller/postInfo.js
const postInfo = require("../data/postInfo");
module.exports = (req, res) => {
res.fail(postInfo());
};
# 6、项目完整代码
项目的目录结构
server
├─ app.js // 项目入口
├─ controller // 数据逻辑处理
│ ├─ getInfo.js
│ └─ postInfo.js
├─ data // 数据源
│ ├─ getFruits.js
│ ├─ getInfo.js
│ └─ postInfo.js
├─ db.js // json数据源(json-server默认json数据)
├─ middlewares // 中件间
│ └─ responseData.middlewares.js
├─ package-lock.json
├─ package.json
├─ public // 静态资源
│ ├─ avatar1.png
│ ├─ avatar2.png
│ └─ avatar3.png
└─ routes.js // 自定义路由
app.js
项目入口
// 导入数据
const db = require("./db.js");
// 导入对响应数据添加状态和提示信息的中间件
const responseDataMiddlewares = require("./middlewares/responseData.middlewares.js");
// 导入自定义路由
const customeRouter = require("./routes.js");
// 导入json-server
const jsonServer = require("json-server");
// 返回express服务器
const server = jsonServer.create();
// 返回json服务器路由
// router中的参数可以是一个json文件地址,也可以是一个对象。即: (path | object)
const router = jsonServer.router(db());
// 返回JSON服务器使用的中间件(用来处理静态资源的-默认为public目录下的静态资源)
const middlewares = jsonServer.defaults();
// 使用中间件,处理静态资源
server.use(middlewares);
// 自定义中间件 为响应数据添加响应状态和提示文字
server.use(responseDataMiddlewares);
// body解析器中间件
server.use(jsonServer.bodyParser);
// 自定义路由
customeRouter(server);
// 使用默认路由器
// server.use(router);
// 可以在路由前添加前缀,以下表示访问路由以/api开头
server.use("/api", router);
// 添加监听的端口
server.listen(8899, () =>
console.log("JSON Server is running at http://127.0.0.1:8899")
);
db.js
编式生成 json-server 默认路由
const getFruits = require("./data/getFruits");
// 响应数据添加响应状态字段
function responseData(data) {
return {
code: 0,
msg: "请求成功",
data,
};
}
module.exports = () => {
return {
fruits: responseData(getFruits()),
};
};
routes.js
自定义路由
const getInfo = require("./controller/getInfo.js");
const postInfo = require("./controller/postInfo.js");
module.exports = (server) => {
// get请求 http://127.0.0.1:8899/api/getinfo
server.use("/api/getinfo", getInfo);
// post 请求 http://127.0.0.1:8899/api/addinfo
server.post("/api/addinfo", postInfo);
};
data/getInfo.js
数据源
module.exports = () => {
return {
msg: "自定义路由,get请求",
};
};
data/postInfo.js
数据源
module.exports = () => {
return {
msg: "自定义路由,post请求",
};
};
controller/getInfo.js
逻辑处理
const getInfo = require("../data/getInfo.js");
module.exports = (req, res) => {
// 相关逻辑处理
// 此处省略.......
res.success(getInfo());
};
controller/postInfo.js
逻辑处理
const postInfo = require("../data/postInfo");
module.exports = (req, res) => {
// 相关逻辑处理
// 此处省略.......
res.fail(postInfo());
};
middlewares/responseData.middlewares.js
中间件
module.exports = (req, res, next) => {
res.success = function (data) {
res.send({
code: 0,
msg: "请求成功!!",
data: data,
});
};
res.fail = function (data) {
res.send({
code: -1,
msg: "请求失败",
data: data,
});
};
next();
};
# 7、REST Client 插件
TIP
- 通过 json-server 来发送请求对数据做添加、更新、删除操作,需要发出 POST、PUT、PATCH、DELETE 请求。
- 为了更方便我们来发送 HTTP 请求,这里推荐使用 VSCode 的 REST Client 插件。
- REST Client 是一款简单实用的 HTTP 请求插件,可以帮助我们在日常的接口开发过程中简化接口的请求调试方式,并加速开发进程。
# 7.1、插件安装
TIP
按下图所示的步骤安装好 REST Client 插件
# 7.2、创建以 .http 结尾的文件
TIP
在当前目录下新建以.http
或.rest
结尾的文件,如: request.http
REST Client
插件会识别后缀为这两种类型的文件并对文件中内容处理。
# 7.3、编写请求内容
TIP
在request.http
文件编写请求内容,编写的格式要求如下:
- 所有请求前一行需要以 3 个 ### 开头,用来标记下面的内容是一个请求。
- 请求内容可以以 HTTP 风格进行编写
- 单个
#
号 用来标记注释 - 请求体与请求头之间要有一个空行
###
GET http://127.0.0.1:8899/api/getinfo
###
POST http://127.0.0.1:8899/api/addinfo
Content-Type: application/json
# 请求体与请求头之间要有一个空行
# 以下为模拟的请求体
{
"a":1,
"b":2
}
按下图步骤来发送对应的 http 请求,就可以在右边看到响应回来的内容
大厂最新技术学习分享群
![大厂最新技术学习分享群](/bulletin-box.jpg)
微信扫一扫进群,获取资料
X