当前位置: 首页 > 工具软件 > Eggjs > 使用案例 >

Eggjs笔记:RESTful API的设计

柯曦
2023-12-01

关于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 按需加入
    };
    
 类似资料: