# 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-serverdb.json根节点的每一个属性 key 当作一个路由。

我们可以通过以下地址来访问对应接口数据,不过在访问前还需要启动json-server服务

  • http://localhost:3000/posts
  • http://localhost:3000/comments
  • http://localhost:3000/profile

# 3、启动 json-server

TIP

执行以下命令,就可以启动json-serverjson-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_gtexxx_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 库,用于模拟接口请求和生成随机数据。

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

image-20230914235221755

image-20230914235253254

# 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 中间件的调用流程

image-20231011194719395

# 4.2、express 中间件的格式

TIP

  • Express 的中间件,本质上就是一个 function 处理函数,函数的形参列表中,必须包含 next 参数。
  • next 函数是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
  • 自定义中间件,用来给响应体res添加 successfail两个方法,对响应成功和失败的数据添加状态码和提示信息。
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 插件

image-20230909125729808

# 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 请求,就可以在右边看到响应回来的内容

image-20230910223913307

上次更新时间: 10/16/2023, 1:52:39 AM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X