Why json-server?

  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    在所有的项目开发中,前端开发都需要后台提供接口和数据,才能进行下一步的开发,但是后台开发多半是滞后于前端开发的,那么我们怎么才能自己临时地来模拟数据,并行开发呢?答案就是MOCK数据,对比过多种mock工具后,最终选择了使用 json server ,因为它足够简单,且足够强大,支持CORS和JSONP跨域请求,支持GET, POST, PUT, PATCH 和 DELETE 方法,更提供了一系列的查询方法,如limit,order等。

    安装使用

    npm install -g json-server
    

    然后在根目录下面新建一个文件db.json:

    {
      "posts": [
        { "id": 1, "title": "json-server", "author": "typicode" }
      ],
      "comments": [
        { "id": 1, "body": "some comment", "postId": 1 }
      ],
      "profile": { "name": "typicode" }
    }
    

    然后开始即可:

    json-server --watch db.json
    

    在浏览器里输入http://localhost:3000/posts/1你会看到下面的结果:

    { "id": 1, "title": "json-server", "author": "typicode" }
    

    下面四点需要注意:

    • POST, PUT, PATCH ,DELETE等请求提交时,会使用lowdb自动改变db.json并保存
    • 所有提交的JSON数据都必须闭合在花括号里面
    • ID无法修改,只能使用POST新增
    • POST, PUT , PATCH等请求如果使用JSON数据提交时必需包含Content-Type: application/json,否则虽然会返回200但是数据不会改变

    所有的使用选项如下:

    json-server [options] <source>
    
    Options:
      --config, -c       配置文件路径           [default: "json-server.json"]
      --port, -p         端口号                 [default: 3000]
      --host, -H         主机地址               [default: "localhost"]
      --watch, -w        是否监听文件           [boolean]
      --routes, -r       路由文件路径
      --middlewares, -m  中间件文件路径         [array]
      --static, -s       静态文件目录
      --read-only, --ro  只允许GET请求          [boolean]
      --no-cors, --nc    禁止跨域               [boolean]
      --no-gzip, --ng    禁止GZIP压缩           [boolean]
      --snapshots, -S    设置快照目录            [default: "."]
      --delay, -d        响应延迟(ms)
      --id, -i           设置数据库ID属性 (e.g. _id)   [default: "id"]
      --foreignKeySuffix, --fks  设置前缀, (e.g. _id as in post_id)
                                                       [default: "Id"]
      --quiet, -q        不输出日志信息                 [boolean]
      --help, -h         Show help                    [boolean]
      --version, -v      Show version number          [boolean]
    
    Examples:
      json-server db.json
      json-server file.js
      json-server http://example.com/db.json
    
    https://github.com/typicode/json-server
    

    默认路由

    • 复数路由,针对数组数据:

      GET    /posts
      GET    /posts/1
      POST   /posts
      PUT    /posts/1
      PATCH  /posts/1
      DELETE /posts/1
      
    • 单数路由,针对对象数据:

      GET    /profile
      POST   /profile
      PUT    /profile
      PATCH  /profile
      
    • 过滤器,你可以使用.进行深层次的属性匹配:

      GET /posts?title=json-server&author=typicode
      GET /posts?id=1&id=2
      GET /comments?author.name=typicode
      
    • 分页,默认是返回10条数据

      GET /posts?_page=7
      GET /posts?_page=1&_limit=2
      
      返回除了获取的数据,在头部还会有一个Link头,包括了first, prev, next,last 链接:
      <http://localhost:3000/posts?_page=1&_limit=1>; rel="first", <http://localhost:3000/posts?_page=1&_limit=1>; rel="prev", <http://localhost:3000/posts?_page=3&_limit=1>; rel="next", <http://localhost:3000/posts?_page=5&_limit=1>; rel="last"
      
    • 排序

      GET /posts?_sort=views&_order=asc
      GET /posts/1/comments?_sort=votes&_order=asc
      GET /posts?_sort=user,views&_order=desc,asc
      
    • 切片

      GET /posts?_start=20&_end=30
      GET /posts/1/comments?_start=20&_end=30
      GET /posts/1/comments?_start=20&_limit=10
      

      除了返回数据外,在返回的消息头部,还有一个X-Total-Count字段,代表了所有的数据的总量

    • 操作符

      _gte: 大于等于
      _lte: 小于等于
      _ne : 不等于
      _like : 正则匹配
      GET /posts?views_gte=10&views_lte=20
      GET /posts?id_ne=1
      GET /posts?title_like=server
      
    • 全文查询

      使用q
      GET /posts?q=internet
      
    • 关系

      嵌入子属性使用_embed,嵌入父属性使用_expand:
      GET /posts?_embed=comments
      GET /posts/1?_embed=comments
      GET /comments?_expand=post
      GET /comments/1?_expand=post
      
      获取或者创建嵌套属性:
      GET  /posts/1/comments
      POST /posts/1/comments
      
    • 获取所有数据

      GET /db
      
    • 首页

      GET /
      

    其他

    • 静态文件服务器

      只需要在根目录创建一个public的目录即可,或者使用--static手动设置别的目录
    • 使用远程数据

      $ json-server http://example.com/file.json
      $ json-server http://jsonplaceholder.typicode.com/db
      
    • 使用动态生成的数据

      // index.js
      module.exports = () => {
      const data = { users: [] }
      // Create 1000 users
      for (let i = 0; i < 1000; i++) {
        data.users.push({ id: i, name: `user${i}` })
      }
      return data
      }
      
      $ json-server index.js
      
    • 自定义路由(路由改写以及映射)

      新建一个routes.json文件:

      {
      "/api/*": "/$1",
      "/:resource/:id/show": "/:resource/:id",
      "/posts/:category": "/posts?category=:category",
      "/articles\\?id=:id": "/posts/:id"
      }
      
      json-server db.json --routes routes.json
      

      现在这些路由就会被映射成:

      /api/posts # → /posts
      /api/posts/1  # → /posts/1
      /posts/1/show # → /posts/1
      /posts/javascript # → /posts?category=javascript
      /articles?id=1 # → /posts/1
      

      !!!注意,重要的事情说三遍:
      !!!注意,重要的事情说三遍:
      !!!注意,重要的事情说三遍:
      上一个被映射的URL会重新匹配下面的映射规则,因此很有可能被再次映射:例如:

      /api/posts/1  # → /posts/1
      

      会继续匹配下面的/posts/javascript因而最终变成了/posts?category=1,和顺序有关,只会继续往下匹配改写,不会往上匹配,而且匹配后显示的URL并不改变,因此需要非常注意谨慎。

    • 使用中间件

      // hello.js
      module.exports = (req, res, next) => {
      res.header('X-Hello', 'World')
      next()
      }
      
      json-server db.json --middlewares ./hello.js
      json-server db.json --middlewares ./first.js ./second.js
      
    • 使用配置文件

      新建一个json-server.json或者使用--config, -c来指定:
      {
      "port": 3000,
      "host": 'http://127.0.0.1'
      ...
      }
      

    模块使用

    • 安装使用

      你也可以把json-server当做一个模块和其他的express中间件一起使用:
      $ npm install json-server --save-dev
      
      // server.js
      const jsonServer = require('json-server')
      const server = jsonServer.create()
      const router = jsonServer.router('db.json')
      const middlewares = jsonServer.defaults()
      server.use(middlewares)
      server.use(router)
      server.listen(3000, () => {
      console.log('JSON Server is running')
      })
      
      $ node server.js
      
      注意:jsonServer.router()也可以在现成的express项目中进行使用
    • 自定义json-server之外的路由

      // Add custom routes before JSON Server router
      server.get('/echo', (req, res) => {
      res.jsonp(req.query)
      })
      server.use((req, res, next) => {
      if (req.method === 'POST') {
        req.body.createdAt = Date.now()
      }
      // Continue to JSON Server router
      next()
      })
      
    • 访问控制

      server.use((req, res, next) => {
      if (isAuthorized(req)) { // add your authorization logic here
       next() // continue to JSON Server router
      } else {
       res.sendStatus(401)
      }
      })
      
    • 自定义输出

      其实所有的要返回的匹配的数据都在res.locals.data里,这样我们就可以自定义输出了:
      router.render = (req, res) => {
      res.jsonp({
        body: res.locals.data
      })
      }
      router.render = (req, res) => {
      res.status(500).jsonp({
        error: "error message here"
      })
      }
      
    • 自定义路由(路由改写以及映射)

      const router1 = jsonServer.router('db1.json')
      const router2 = jsonServer.router('db2.json')
      server.use(jsonServer.rewriter({
      '/api/*': '/$1',
      '/blog/:resource/:id/show': '/:resource/:id'
      }))
      server.use('/post', jsonServer.rewriter({
      '/api/*': '/$1',
      '/blog/:resource/:id/show': '/:resource/:id'
      }))
      server.use('/post1',router1)
      server.use('/post2',router2)
      
      注意,路由改写应该写在server.use(router)之前,否则无效

    API

    • jsonServer.create()返回一个express服务器
    • jsonServer.defaults([options])返回使用的中间件
      • static path to static files
      • logger enable logger middleware (default: true)
      • bodyParser enable body-parser middleware (default: true)
      • noCors disable CORS (default: false)
      • readOnly accept only GET requests (default: false)
    • jsonServer.router([path|object])返回router

    配合Mockjs使用

    // data
    const Mock  = require('mockjs')
    const Random = Mock.Random
    module.exports = () => {
      const data = {products: []}
      data.products.push({
        name: Random.name()
      })
      return data
    }
    
    // server.js
    const jsonServer = require('json-server')
    const server = jsonServer.create()
    const middlewares = jsonServer.defaults()
    const products_db = require('./data/products.js')
    const products_router = jsonServer.router(products_db())
    server.use(middlewares)
    server.use(jsonServer.bodyParser)
    server.use(products_router)
    server.listen(9000, () => {
      console.log('JSON Server is running')
    })