在开发过程中,接口多半是滞后于页面开发的。利用JSON Server快速搭建模拟返回REST风格的后台数据,保证前后端开发的分离。前后端开发只要设定好接口以及数据的定义,剩下的就可以各自开发,最后集成测试。
JSON Server 作为工具,基于Express开发,而且它足够简单,写少量数据,即可使用,支持CORS和JSONP跨域请求,支持GET, POST, PUT, PATCH 和 DELETE 方法,更提供了一系列的查询方法,如limit,order等
安装
npminstalljson-server-g
全局安装,可以在命令行下独立执行。安装完成后可以用 json-server -h 命令检查是否安装成功。
json-server[options]
Options:
--config,-c指定config文件[默认: "json-server.json"]
--port,-p设置端口号[default: 3000]
--host,-H设置主机[默认: "0.0.0.0"]
--watch,-w监控文件[boolean]
--routes,-r指定路由文件
--static,-s设置静态文件
--read-only,--ro只允许GET请求[boolean]
--no-cors,--nc禁止跨域资源共享[boolean]
--no-gzip,--ng禁止GZIP[boolean]
--snapshots,-S设置快照目录[默认: "."]
--delay,-d设置反馈延时 (ms)
--id,-i设置数据的id属性 (e.g. _id)[默认: "id"]
--quiet,-q不输出日志信息[boolean]
--help,-h显示帮助信息[boolean]
--version,-v显示版本号[boolean]
启动
创建一个目录server,在该目录下创建一个json文件,db.json.
{
"list": [
{
"id":1,
"name":"张三",
"tel":"15223810923"
},
{
"id":2,
"name":"李四",
"tel":"15223810923"
},
{
"id":3,
"name":"王二",
"tel":"15223810923"
},
{
"id":4,
"name":"陈五",
"tel":"15223810923"
},
{
"name":"赵六",
"tel":"123454323",
"id":5
},
{
"name":"赵六",
"tel":"123454323",
"id":6
},
{
"name":"赵六",
"tel":"123454323",
"id":7
}
],
"users": [
{
"id":1,
"name":"陈五",
"sex":"male",
"tel":"12345678",
"auther":{
"name":"陈五",
"age":"25"
}
},
{
"id":2,
"name":"王二",
"sex":"male",
"tel":"15223810923",
"auther":{
"name":"王二",
"age":"22"
}
}
],
"user": {
"id":1,
"name":"陈五",
"tel":"15223810923"
}
,
"posts": [
{"id":1,"title":"json-server","author":"typicode"}
],
"comments": [
{"id":1,"body":"some comment","postId":1}
],
"profile": {"name":"typicode"}
}
在server目录下执行
json-serverdb.json-p3003
打开浏览器,http://localhost:3003,查看页面。如果要监控json文件的变化,启动的时候加上参数--watch。
支持的方法
以APIhttp://localhost:3003/list 为例
GET /list 获取列表
GET /list/1 获取id=1的数据
POST /list 创建一个项目
PUT /list/1 更新一个id为1的数据
PATCH /list/1 部分更新id为1的数据
DELETE /list/1 删除id为1的数据
对于对象,例如http://localhost:3003/user,地址是相同的。
GET /user
POST /user
PUT /user
PATCH /user
当你发送POST,PUT,PATCH 或者 DELETE请求时,变化会自动安全的保存到你的db.json文件中。
你的请求体body应该是封闭对象。比如{"name": "Foobar"}
id不是必须的,在PUT或者PATCH方法中,任何的id值将会被忽略。
在POST请求中,id是可以被添加的,如果该值没有没占用,会使用该值,否则自动生成。
POST,PUT或者PATCH请求应该包含一个Content-Type:application/json的header,来确保在请求body中使用json。
高级查找
Filter
用.来访问深层属性,比如
GET /users?set=male&tel=12345678
GET /list?id=1&id=2
GET /users?author.age=25
Paginate
使用 _page 和可选的 _limit来对返回数据定制(不设置默认返回10条)。
在返回的header中,有一个属性Link,里面会有first, prev, next and last links。
header中还有一个属性X-Total-Count,用来存储满足条件的数据的条数。
GET /list?_page=1
GET /list?_page=2&_limit=2
Sort
使用 _sort 和 _order (默认是ascending)
GET /list?_sort=id&_order=asc
对于多字段的排序,可以参考下面的格式:
GET /list?_sort=id,name&_order=desc,asc
Slice
使用_start和_end或_limit(一个 X-Total-Count 自定义Header在Response里面),就像数据的slice的方法一样。
GET /list?_start=2&_end=5
GET /list?_start=2&_limit=5
Operators
用 _gte或_lte来得到一个范围。
GET /list?id_gte=2&id_lte=5
用_ne来不包含(exclude)一个值
GET /posts?id_ne=1
GET /list?id_gte=2&id_lte=5&id_ne=4
用 _like 来 filter (RegExp 支持)
GET /list?name_like=王
search
使用q
GET /list?q=1
Relationships
关联子资源, 添加_embed
GET/posts?_embed=comments
GET/posts/1?_embed=comments
包含父资源, 添加_expand
GET/comments?_expand=post
GET/comments/1?_expand=post
要获得或创建nested resources(默认一层,多层的话,自定义routes)
GET/posts/1/comments
POST /posts/1/comments
Database
GET/db
Homepage
返回默认的index文件,或者./public目录
GET/
拓展功能
静态文件服务器
你也可以用json server来托管你的静态HTML,JS和CSS文件。仅仅需要简单的创建一个./public目录。或者用--static来指定一个不同的静态文件路径。
mkdirpublic
echo'hello world'>public/index.html
json-server db.json
json-server db.json --static./some-other-dir
改变端口号
你可以改变端口号使用--port
$ json-server --watch db.json --port3004
从任何地方访问
你可以使用CORS和JSONP从任何地方访问你的API。
远程文件
你可以加载远程文件
$ json-serverhttp://example.com/file.json
$ json-serverhttp://jsonplaceholder.typicode.com/db
生成随机数据
使用js文件替代json文件,可以动态生成数据。还可以借助其他模块生成,比如Faker, Casual, Chance or JSON Schema Faker。
// index.js
module.exports =() =>{
constdata = {users: [] }
// Create 1000 users
for(leti =0; i <1000; i++) {
data.users.push({id: i,name:`user${i}`})
}
returndata
}
$ json-server index.js
HTTPS
有很多方式在开发中使用SSL。一个简单的方式就是使用hotel.
自定义路由
创建routes.json文件。注意每个路由文件应该以/开头。
{
"/api/*":"/$1",
"/:resource/:id/show":"/:resource/:id",
"/posts/:category":"/posts?category=:category",
"/articles\\?id=:id":"/posts/:id"
}
启动json server时加上--routes选项。
json-serverdb.json--routesroutes.json
现在你可以用附加路由访问资源了。
/api/posts# → /posts
/api/posts/1# → /posts/1
/posts/1/show# → /posts/1
/posts/javascript# → /posts?category=javascript
/articles?id=1# → /posts/1
增加中间件
命令行中,你可以使用--middlewares选项。
// hello.js
module.exports =(req, res, next) =>{
res.header('X-Hello','World')
next()
}
json-serverdb.json --middlewares ./hello.js
json-serverdb.json --middlewares ./first.js ./second.js
CLI usage
你可以将设置放在json-server.json配置文件里。
将json server作为一个模块
在项目中,如何你需要增加认证,验证或者其他功能,你可以将json server作为一个模块,合并其他的Express中间件。
一个简单的例子
// server.js
constjsonServer =require('json-server')
constserver = jsonServer.create()
constrouter = jsonServer.router('db.json')
constmiddlewares = jsonServer.defaults()
server.use(middlewares)
server.use(router)
server.listen(3003, () => {
console.log('JSON Server is running')
})
$ node server.js
你提供给jsonServer.router方法的路径,是相对于你的node的。
如果你从另外一个路径运行上面的代码,最好用绝对路径。
constpath =require('path')
constrouter = jsonServer.router(path.join(__dirname,'db.json'))
对于内存数据库,传递一个对象给jsonServer.router()。注意,jsonServer.router()可以被用在一个已经存在的Express项目中。
自定义路由动态数据的例子
//db.js
letMock =require('mockjs');
letRandom = Mock.Random;
module.exports =function(){
vardata = {
news: [],
type:{
"a":"a",
"b":"b",
}
};
varimages = [1,2,3].map(x=>Random.image('200x100', Random.color(), Random.word(2,6)));
for(vari =0; i <10; i++) {
varcontent = Random.cparagraph(0,10);
data.news.push({
id: i,
title: Random.cword(8,20),
desc: content.substr(0,40),
tag: Random.cword(2,6),
views: Random.integer(100,5000),
images: images.slice(0,Random.integer(1,3))
})
}
returndata
}
//server.js
constpath =require('path');
constconfig =require('./config');
constjsonServer =require('json-server');
construles =require('./routes');
constdbfile =require(config.DB_FILE);
constip = config.SERVER;
constport = config.PORT;
constdb_file = config.DB_FILE;
constserver = jsonServer.create();
constrouter = jsonServer.router(dbfile());
constmiddlewares = jsonServer.defaults();
server.use(jsonServer.bodyParser);
// Set default middlewares (logger, static, cors and no-cache)
server.use(middlewares);
server.use((req, res, next) =>{
res.header('X-Hello','World');
next();
})
router.render =(req, res) =>{
res.jsonp({
body: res.locals.data,
code:0
})
}
server.use("/api",router);
server.use(jsonServer.rewriter(rules));
server.use(router);
server.listen({
host: ip,
port: port,
},function(){
console.log(JSON.stringify(jsonServer));
console.log(`JSON Server is running in http://${ip}:${port}`);
});
//routes.js
module.exports= {
"/api/":"/",
"/:id":"/news/:id",
"/news/show/:id":"/news/:id",
"/topics/:id/show":"/news/:id"
}
添加一个路由输出query parameters
// Add custom routes before JSON Server router
server.get('/echo', (req, res) => {
res.jsonp(req.query)
})
添加一个时间戳
支持post请求需要使用bodyParser
// To handle POST, PUT and PATCH you need to use a body-parser
// You can use the one used by JSON Server
server.use(jsonServer.bodyParser)
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)
}
})
修改response数据结构
需要override router.render方法。
// In this example, returned resources will be wrapped in a body property
router.render =(req, res) =>{
res.jsonp({
body: res.locals.data
})
}
自定义reponse状态码
// In this example we simulate a server side error response
router.render =(req, res) =>{
res.status(500).jsonp({
error:"error message here"
})
}
Rewriter例子
添加rewrite rules,使用jsonServer.rewriter():
// Add this before server.use(router)
server.use(jsonServer.rewriter({
'/api/*':'/$1',
'/blog/:resource/:id/show':'/:resource/:id'
}))
MountingJSONServer on another endpoint example
更改挂载点
挂在/api上
server.use('/api', router)