自建-模擬JSON REST API-從入門到精通-前端必備技能-你還在等你後臺API嗎?
ofollow,noindex">部落格地址:有實時的目錄閱讀體驗更好
#Linxu/Mac os sudo npm install json-server -g #window npm install json-server -g 複製程式碼
新建db.json用於測試json-server db.json -p 3003
,-p 指定埠號為3003
{ "news":[ { "id": 1, "title": "曹縣宣佈昨日晚間登日成功", "date": "2016-08-12", "likes": 55, "views": 100086 }, { "id": 2, "title": "長江流域首次發現海豚", "date": "2016-08-12", "likes": 505, "views": 9800 } ], "comments":[ { "id": 1, "news_id": 1, "data": [ { "id": 1, "content": "支援黨中央決定" }, { "id": 2, "content": "抄寫黨章勢在必行!" } ] } ] } 複製程式碼
出現錯誤:
/usr/bin/env: node: No such file or directory
解決
ln -s /usr/bin/nodejs /usr/bin/node 複製程式碼
json-server requires at least version 6 of Node, please upgrade
解決
sudo npm cache clean -f sudo npm install -g n sudo n stable 複製程式碼
快速啟動指令碼
db.json 目錄下新建package.json
, 執行npm run mock
{ "scripts": { "mock": "json-server db.json --port 3003" } } 複製程式碼
簡單的資料操作
- json-server和postman 簡單教程 postman是模擬請求的開發神器
-
POST, PUT, PATCH or DELETE 這些操作會自動的儲存在db.json檔案裡,請求必須是Json格式,包含標頭檔案
Content-Type: application/json
否則返回的雖然是200 OK,但是不會修改資料
GET
http://localhost:3003/db ==> db.json
http://localhost:3003/news ==> news節點
POST
Postman向http://localhost:3003/news
傳引數
"id": 3, "title": "我是新加入的新聞", "date": "2016-08-12", "likes": 0, "views": 0 複製程式碼
json-server 後臺反應
POST /news?id=3&title=%E6%88%91%E6%98%AF%E6%96%B0%E5%8A%A0%E5%85%A5%E7%9A%84%E6%96%B0%E9%97%BB&date=2016-08-12&likes=0&views=0 201 2.622 ms - 13 複製程式碼
使用url encode 解碼點選UrlDecode解碼
POST /news?id=3&title=我是新加入的新聞&date=2016-08-12&likes=0&views=0 201 2.622 ms - 13 複製程式碼
PUT
json-server 後臺列印
PUT /news?title=我是新加入的新聞&date=2016-08-12&likes=55&views=100086 404 2.430 ms - 2 複製程式碼
模擬動態資料
簡單的返回陣列
/mock/db.js
module.exports = function() { var data = { users: [] } // Create 1000 users for (var i = 0; i < 1000; i++) { data.users.push({ id: i, name: 'user' + i }) } return data } 複製程式碼
執行
json-server db.js -p 3003 複製程式碼
訪問
http://localhost:3003/users 複製程式碼
返回
[ { "id": 0, "name": "user0" }, { "id": 1, "name": "user1" } ...... ] 複製程式碼
拒絕僵硬的資料,引入mockjs
安裝mockjs 在/mock
目錄下安裝
npm install mockjs --save 複製程式碼
示例:返回100條新聞資料
// # /mock/db.js let Mock= require('mockjs'); let Random = Mock.Random; module.exports = function() { var data = { news: [] }; var images = [1,2,3].map(x=>Random.image('200x100', Random.color(), Random.word(2,6))); for (var i = 0; i < 100; i++) { var content = 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)) }) } return data } 複製程式碼
執行
json-server db.js -p 3000 複製程式碼
訪問http://localhost:3000/news
返回
[ { "id": 0, "title": "元小總小把清保住影辦歷戰資和總由", "desc": "共先定製向向圓適者定書她規置鬥平相。要廣確但教金更前三響角面等以白。眼查何參提適", "tag": "值集空", "views": 3810, "images": [ "http://dummyimage.com/200x100/79f2a5&text=別角置", "http://dummyimage.com/200x100/f28279&text=收面幾容受取", "http://dummyimage.com/200x100/7993f2&text=做件" ] }, ...... ] 複製程式碼
Mock 語法
Mock.mock
repeat 方法(部分)
Mock.mock({ "string|5": "★"=>"string": "★★★★★" "string|1-10": "★"=>"string": "★★" "number|1-100": 100=>"number": 85 "number|1-100.2": 100=>"number": 25.69 }) 複製程式碼
Mock.Random
Random.boolean()=> true false 各50% Random.integer(60, 100)=> 78 Random.float(60, 100)=> 89.565475 Random.range(60, 100)=> [60,61,62,...,99] Random.date()=> "2018-12-28" Random.image('200x100','#396') => "http://dummyimage.com/200x100/396" Random.color()=> "#79d8f2"(預設使用hex顏色) Random.county(true)=> "浙江省 舟山市 岱山縣" 複製程式碼
- Rnadom.image
Random.image() // => "http://dummyimage.com/125x125" Random.image('200x100') // => "http://dummyimage.com/200x100" Random.image('200x100', '#fb0a2a') // => "http://dummyimage.com/200x100/fb0a2a" Random.image('200x100', '#02adea', 'Hello') // => "http://dummyimage.com/200x100/02adea&text=Hello" Random.image('200x100', '#00405d', '#FFF', 'Mock.js') // => "http://dummyimage.com/200x100/00405d/FFF&text=Mock.js" Random.image('200x100', '#ffcc33', '#FFF', 'png', '!') // => "http://dummyimage.com/200x100/ffcc33/FFF.png&text=!" 複製程式碼
- Text
paragraph-> centence -> word -> title #中文 Random.cparagraph() Random.cparagraph( len ) Random.cparagraph( min, max ) #隨機生成一段中文文字 Random.csentence() Random.csentence( len ) Random.csentence( min, max ) # 隨機生成一個漢字 Random.cword() Random.cword( pool ) Random.cword( length ) Random.cword( pool, length ) Random.cword( min, max ) Random.cword( pool, min, max ) 複製程式碼
進階
加工資料
Filter
對應的資料九宮格
comments:[ { "id": 1, "news_id": 1, "author":{ "name":"a1", "age":32 }, "data": [ { "id": 1, "content": "支援黨中央決定" }, { "id": 2, "content": "抄寫黨章勢在必行!" } ] } ] 複製程式碼
查詢語句:
GET /comments?id=1r&new_id=2 GET /comments?author.name=a1 複製程式碼
Paginate 分頁
GET /posts?_page=7 GET /posts?_page=7&_limit=20 複製程式碼
- 預設返回 10 items
- 表頭會出現 first, prev, next and last 的直接訪問地址,x-total-count資料總數資訊
access-control-expose-headers →X-Total-Count, Link link →<http://localhost:3000/news?_page=1>; rel="first", <http://localhost:3000/news?_page=2>; rel="next", <http://localhost:3000/news?_page=2>; rel="last" x-total-count →20 複製程式碼
關係圖譜
posts id 和comments id 是關聯的
{ "posts": [ { "id": 1, "title": "post的第一個title", "author": "typicode" }, { "id": 2, "title": "post的第二個title", "author": "tangcaiye" } ], "comments": [ { "id": 1, "body": "some comment1111", "postId": 2 }, { "id": 2, "body": "some comment2222", "postId": 1 } ], "profile": { "name": "typicode" } } 複製程式碼
-
_embed
http://localhost:3000/posts/2?_embed=comments
返回posts/2
下級關聯資料comments
{ "id": 2, "title": "post的第二個title", "author": "tangcaiye", "comments": [ { "id": 1, "body": "some comment1111", "postId": 2 } ] } 複製程式碼
-
_expand
http://localhost:3000/comments/2?_expand=post
返回comments上級關聯資料post
{ "id": 2, "body": "some comment2222", "postId": 1, "post": { "id": 1, "title": "post的第一個title", "author": "typicode" } } 複製程式碼
其他
排序
預設升序
GET /posts?_sort=views&_order=asc GET /posts/1/comments?_sort=votes&_order=asc 複製程式碼
切片 Slice
包括_star,不包括_end
GET /posts/1/comments?_start=20&_end=30 GET /posts/1/comments?_start=20&_limit=10 複製程式碼
彙總
# _gte > || _lte < GET /posts?views_gte=10&views_lte=20 # _ne != GET /posts?id_ne=1 # _like 注意title的欄位名稱 GET /posts?title_like=server # 全域性搜尋 GET /posts?q=internet # 直接獲取資料 GET /db 複製程式碼
路由設定
注意:沒有設定路由(主要是: "host": "0.0.0.0" 屬性)之前json-server --watch db.json
只有本機能訪問,也就是區域網外網都不能訪問
- 可以用-H 命令指定
json-server --watch db.json -H 0.0.0.0 複製程式碼
-
host 設定為
127.0.0.1
或者本地區域網IP地址 如192.168.1.168
無效 ,(伺服器指定127.0.0.1/ 192.168.1.168是本地除錯模式)具體解釋網址 大意: 0.0.0.0指的是本機上的所有IPV4地址
IPV4中,0.0.0.0地址被用於表示一個無效的,未知的或者不可用的目標。* 在伺服器中,0.0.0.0指的是本機上的所有IPV4地址,如果一個主機有兩個IP地址,192.168.1.1 和 10.1.2.1,並且該主機上的一個服務監聽的地址是0.0.0.0,那麼通過兩個ip地址都能夠訪問該服務。 * 在路由中,0.0.0.0表示的是預設路由,即當路由表中沒有找到完全匹配的路由的時候所對應的路由。
設定的兩種方法
- 直接命令列指定路由檔案
json-server db.js -p 3003 -d 500 -q -r ./routes.json 複製程式碼
-
json-server.json
檔案進行配置後,直接json-server db.json
# /mock/json-server.json { "host": "0.0.0.0", "port": "3003", "watch": false, "delay": 500, "quiet": true, "routes": "./routes.json" } 複製程式碼
自定義路由,可以指定訪問連結返回指定的資料,而且可以動態修改
routes.json
{ "/api/*": "/$1", "/:resource/:id/show": "/:resource/:id", "/posts/:category": "/posts?category=:category", "/articles\\?id=:id": "/posts/:id" } 複製程式碼
對應的效果
/api/posts # → /posts /api/posts/1# → /posts/1 /posts/1/show # → /posts/1 /posts/javascript # → /posts?category=javascript /articles?id=1 # → /posts/1 複製程式碼
"/api/*": "/$1" /:resource /
增加中介軟體
// 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 複製程式碼
NodejsModule 不用等後臺同事API
為什麼學了上面還要學nodejs的案例呢? 因為上面的技能還不能很好的滿足開發中REST API的需求
- rount.json 儲存在檔案中修改比較麻煩
- 只用配置檔案,不很好使用Mock的相關功能
- 把配置放在js中相對靈活,能動態的攔截返回和處理相關邏輯
最簡單案例
node 安裝 json-server模組
$ npm install json-server --save-dev 複製程式碼
// server.js const jsonServer = require('json-server') const server = jsonServer.create() const router = jsonServer.router('db.json') //預設為當前目錄 //const path = require('path') 指定其他目錄 //const router = jsonServer.router(path.join(__dirname, 'db.json')) const middlewares = jsonServer.defaults() server.use(middlewares) server.use(router) server.listen(3000, () => { console.log('JSON Server is running') }) // 執行 $ node server.js 複製程式碼
自定義路由
const jsonServer = require('json-server') const server = jsonServer.create() const router = jsonServer.router('db.json') const middlewares = jsonServer.defaults() // Set default middlewares (logger, static, cors and no-cache) server.use(middlewares) // Add custom routes before JSON Server router server.get('/echo', (req, res) => { res.jsonp(req.query) }) // 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() }) // Use default router server.use(router) server.listen(3000, () => { console.log('JSON Server is running') }) 複製程式碼
對上面的幾點進行說明
res.jsonp res ==> respose
res.status(500).jsonp({ error: "error message here" "id": 21, "title": "長江", "date": "2016-09-12", "likes": 509, "views": 9900 }) 複製程式碼
請求的返回Json格式
{ "error": "error message here" "id": 21, "title": "長江", "date": "2016-09-12", "likes": 509, "views": 9900 } 複製程式碼
req.body.createdAt = Date.now() 往返回的jons中插入createAt欄位,如本應返回
{ "id": 21, "title": "長江", "date": "2016-09-12", "likes": 509, "views": 9900 } 複製程式碼
加入req.body.createdAt = Date.now()後返回
{ "id": 21, "title": "長江", "date": "2016-09-12", "likes": 509, "views": 9900, "createdAt": 1536476508883, } 複製程式碼
js 增加router.json 配置
// Add this before server.use(router) server.use(jsonServer.rewriter({ '/api/*': '/$1', '/blog/:resource/:id/show': '/:resource/:id' })) 複製程式碼
訪問控制
const jsonServer = require('json-server') const server = jsonServer.create() const router = jsonServer.router('db.json') const middlewares = jsonServer.defaults() server.use(middlewares) server.use((req, res, next) => { if (isAuthorized(req)) { // add your authorization logic here next() // continue to JSON Server router } else { res.sendStatus(401) } }) server.use(router) server.listen(3000, () => { console.log('JSON Server is running') }) 複製程式碼
自定義輸出
// In this example, returned resources will be wrapped in a body property router.render = (req, res) => { res.jsonp({ body: res.locals.data }) //res.jsonp => 包裹資料成json格式 //res.send=> 直接傳送字串 } 複製程式碼
訪問本應返回
{ "id": 21, "title": "長江", "date": "2016-09-12" } 複製程式碼
增加 body: res.locals.data後返回
{ body: { id: 1, title: "曹縣宣佈昨日晚間登日成功", date: "2016-08-12", likes: 55, views: 100086 } } 複製程式碼
終極Demo
訪問http://localhost:3000/echo
返回模擬資料
const jsonServer = require('json-server') const server = jsonServer.create() const router = jsonServer.router('db.json') const middlewares = jsonServer.defaults() let Mock= require('mockjs'); server.use(middlewares) var template = { 'titles': 'Syntax Demo', 'string1|1-10': '★', 'string2|3': 'value', 'number1|+1': 100, 'number2|1-100': 100, 'number3|1-100.1-10': 1, 'number4|123.1-10': 1, 'number5|123.3': 1, 'number6|123.10': 1.123, 'boolean1|1': true, 'boolean2|1-2': true, 'object1|2-4': { '110000': '北京市', '120000': '天津市', '130000': '河北省', '140000': '山西省' }, 'object2|2': { '310000': '上海市', '320000': '江蘇省', '330000': '浙江省', '340000': '安徽省' }, 'array1|1': ['AMD', 'CMD', 'KMD', 'UMD'], 'array2|1-10': ['Mock.js'], 'array3|3': ['Mock.js'], 'function': function() { return this.titles } } server.get('/echo', (req, res) => { var data = Mock.mock(template) var json = JSON.stringify(data, null, 4) console.log('-----------------\n' + json) res.send(json) }) // 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) => { console.log('--------------'+req.method) if (req.method === 'POST') { req.body.createdAt = Date.now() req.body.test = 1 } // Continue to JSON Server router next() }) // 返回錯誤碼,和指定json格式的資料 //router.render = (req, res) => { //res.status(500).jsonp({ //error: "error message here" //"id": 21, //"title": "長江", //"date": "2016-09-12", //"likes": 509, //"views": 9900 //}) //} // Add this before server.use(router) server.use(jsonServer.rewriter({ '/api/*': '/$1', '/blog/:resource/:id/show': '/:resource/:id' })) router.render = (req, res) => { res.jsonp({ body: res.locals.data }) } server.use(router) server.listen(3000, () => { console.log('JSON Server is running') }) 複製程式碼
附錄
json-server 命令引數
json-server index.js [options] <source> 選項: --config, -cPath to config file[預設值: "json-server.json"] --port, -pSet port[預設值: 3000] --host, -HSet host[預設值: "localhost"] --watch, -wWatch file(s)[布林] --routes, -rPath to routes file --middlewares, -mPaths to middleware files[陣列] --static, -sSet static files directory --read-only, --roAllow only GET requests[布林] --no-cors, --ncDisable Cross-Origin Resource Sharing[布林] --no-gzip, --ngDisable GZIP Content-Encoding[布林] --snapshots, -SSet snapshots directory[預設值: "."] --delay, -dAdd delay to responses (ms) --id, -iSet database id property (e.g. _id)[預設值: "id"] --foreignKeySuffix, --fksSet foreign key suffix (e.g. _id as in post_id) [預設值: "Id"] --quiet, -qSuppress log messages from output[布林] --help, -h顯示幫助資訊[布林] --version, -v顯示版本號[布林] 示例: index.js db.json index.js file.js index.js http://example.com/db.json https://github.com/typicode/json-server Missing <source> argument 複製程式碼