Eggjs筆記:RESTful API的設計

關於RESTful API

  • 網絡應用程序,分爲前端和後端兩個部分。當前的發展趨勢,就是前端設備層出不窮(手機、平板、桌面電腦、其他專用設備…)。
  • 因此,必須有一種統一的機制,方便不同的前端設備與後端進行通信。這導致 API 構架的流行,甚至出現"API First"的設計思想
  • RESTful API 是目前比較成熟的一套互聯網應用程序的API設計理論,在企業中用的也非常多。
  • RESTful API 也有不一些不足:(字段冗餘,擴展性差、無法聚合 api、無法定義數據類型、網絡請求次數多)等不足
  • GraphQL 繼承了 RESTful的優點彌補了 RESTful 的不足,我們這裏目前只討論RESTful API,暫不討論 GraphQL

RESTful API的設計

一個好的 RESTful API 我們從以下幾個方面考慮:

  • 協議: 建議使用更安全的https協議
  • 域名: 儘量部署在專屬域名下面
    • 比如: https://a.taobao.com
    • 比如: https://api.tmall.com
  • 版本:應該將api的版本號放入URl中:
    • 比如: https://a.baidu.com/api1/newslist https://a.baidu.com/api2/newslist
    • 比如: https://a1.tencent.com https://a2.tencent.com
  • 路徑: 在RESTful架構中,每個網址代表一種資源(resource),所以網址中建議不能有動詞,只能有名詞,而且所用的名詞往往與數據庫的表名對應
    • 一般來說,數據庫中的表都是同種記錄的"集合"(collection)
    • 所以 API 中的名詞也應該使用複數
  • 請求方式: http 請求數據的方式:(7 個 HTTP 方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS)
    • GET(SELECT): 從服務器取出資源(一項或多項)
    • POST(CREATE): 在服務器新建一個資源
    • PUT(UPDATE): 在服務器更新資源(客戶端提供改變後的完整資源)
    • DELETE(DELETE): 從服務器刪除資源
    • 還有三個不常用的 HTTP 請求方式
      • HEAD: 獲取資源的元數據
      • OPTIONS: 獲取信息,關於資源的哪些屬性是客戶端可以改變的
      • PATCH(UPDATE): 在服務器更新資源(客戶端提供改變的屬性)
  • 其他: 過濾方式、請求數據方式、返回數據方式、安全問題

Egg中配置API示例

路由

app/router/api.js

這裏演示:GET、POST、PUT、DELETE, 備註說明:這裏的POST可能會有csrf驗證, 我們需要針對API接口來關閉它,下面會有相關配置

'use strict';
module.exports = app => {
    const { router, controller } = app;

    router.get('/api/index', controller.api.default.index);
    router.get('/api/productList', controller.api.default.productList);
    router.post('/api/register', controller.api.default.register);
    router.put('/api/editUser', controller.api.default.editUser);
    router.delete('/api/deleteUser', controller.api.default.deleteUser);
};

app/router.js

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  // ...其他路由

  // api接口配置
  require('./router/api')(app);
};

控制器

app/controller/api/default.js

只做一些返回數據的演示

'use strict';

const Controller = require('egg').Controller;

class DefaultController extends Controller {
    async index() {
        this.ctx.body = "api接口"
    }

    //商品列表的api接口
    async productList() {
        let page = this.ctx.request.query.page || 1;
        let pageSize = this.ctx.request.query.pageSize || 10;
        const goodsResult = await this.ctx.model.Goods.find({}).skip((page - 1) * pageSize).limit(pageSize);
        this.ctx.body = {
            result: goodsResult
        }
    }

    //post 增加數據
    async register() {
        console.log(this.ctx.request.body)
        this.ctx.body = {
            result: 'success_post'
        }
    }

    //put 修改數據
    async editUser() {
        console.log(this.ctx.request.body)
        this.ctx.body = {
            result: 'success_put'
        }
    }

    async deleteUser() {
        // console.log(this.ctx.request.body);
        console.log(this.ctx.request.query);
        this.ctx.body = {
            result: 'success_delete'
        }
    }
}

module.exports = DefaultController;

前端請求

這裏是基於vue-resource(官方提供的vue的一個插件),當然可以使用axios, fetch-jsonp等其他庫

Home.vue 片段

<template>
    <!-- 所有的內容要被根節點包含起來 -->
    <div id="home">
         首頁組件
        <button @click="getData()">請求數據</button>
        <br><br><br>
        <button @click="doRegister()">執行註冊(增加)</button>
        <br><br><br>
        <button @click="doEdit()">執行修改</button>
        <br><br><br>
        <button @click="doDelete()">執行刪除</button>
    </div>
</template>

<script>
    export default{
        data(){
            return {
                msg:'我是一個首頁組件msg',
                flag:true,
                list:[]
            }
        },
        methods:{
            getData(){
                    //請求數據
                    var api='http://localhost:7001/api/productList';
                    this.$http.get(api).then((response)=>{
                        console.log(response);
                    },function(err){
                            console.log(err);
                    })                    
            },
            doRegister(){
                    var api='http://localhost:7001/api/register';
                    this.$http.post(api,{
                        username:'張三',
                        age:20
                    }).then((response)=>{
                        console.log(response);                
                    },(err)=>{
                            console.log(err);
                    })
            },
            doEdit(){
                    var api='http://localhost:7001/api/editUser';
                    this.$http.put(api,{
                        username:'張三1111',
                        age:20
                    }).then((response)=>{
                        console.log(response);      
                    },(err)=>{
                            console.log(err);
                    })
            },
            doDelete(){
                  var api='http://localhost:7001/api/deleteUser?_id=11111';
                    this.$http.delete(api).then((response)=>{
                        console.log(response);
                    },function(err){
                            console.log(err);
                    })
            }
        }       
    }

</script>

點擊頁面按鈕測試請求,可以看到出現問題了,eggjs默認是7001的端口,而vue打開的則是8080,於是出現了跨域問題, 如下

No 'Access-Control-Allow-Origin' header is present on the requested resource. Orgin 'http://localhost:8000' is therfore not allowed access.

我們來解決這個問題,一種是使用jsonp的方式,這種只支持get請求,有侷限; 一種是後臺允許跨域

下面我們來配置後臺支持跨域請求

Egg中通過 egg-cors配置服務器端允許 跨域

  • 安裝插件 $ npm i egg-cors --save

  • 配置插件

    // {app_root}/config/plugin.js 
    exports.cors = {
        enable: true,
        package: 'egg-cors', 
    };
    
  • 配置安全域名:

    config.security = {
        csrf: {
            // 判斷是否需要 ignore 的方法,請求上下文 context 作爲第一個參數
            ignore: ctx => {
                // 屏蔽csrf驗證的接口或路由
                let arr = [
                    '/pay/ali/notify', // 接收支付寶支付post接口
                    '/pay/wechat/notify', // 接收微信支付post接口
                ];
                let flag = false;
                let url = ctx.request.url;
                // 進行匹配
                arr.some((item) => {
                    // 通用的路由相關 和 API相關
                    if (url === item || url.indexOf('/api') !== -1) {
                        // console.log(item);
                        flag = true;
                        return true;
                    }
                });
                return flag;
            },
        },
        domainWhiteList: ['http://localhost:8080'] // 配置跨域 這裏支持這個8080的域,這個必須配置下,即使下面 origin爲* 了,也應該配置,這裏是客戶端的域
    };
    
    // 這個配置基於:https://www.npmjs.com/package/koa2-cors
    config.cors = {
        origin: '*',
        allowMethods: 'GET,PUT,POST,DELETE' // 一般默認的配置都是這幾個,其他有 OPTIONS,HEAD,PATCH 按需加入
    };
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章