都做前端开发了,接口自己还不会写嘛?💘

发布于:2024-04-26 ⋅ 阅读:(11) ⋅ 点赞:(0)

搭建框架

mkdir egg-example && cd egg-example
npm init egg --type=simple
npm i
npm i egg-bag-framework // 安装框架覆盖多种模块和工具

image.png

完整代码下载

链接: 提取码:ai46

下载完成后,安装框架

npm i egg-bag-framework
// package.json
"egg": {  
    "framework": "egg-bag-framework"  // 作为启动入口
},

可选配置 config.default.js

/* eslint valid-jsdoc: "off" */

'use strict';

/**
 * @param {Egg.EggAppInfo} appInfo app info
 */
module.exports = appInfo => {
    const config = exports = {};

    config.mysql = {
        client: {
            host: '127.0.0.1',
            port: '3306',
            user: 'itnavs',
            password: '123456',
            database: 'itnavs',
        },
        app: true,
        agent: false,
    };


    config.redis = {
        client: {
            port: 6379,
            host: '127.0.0.1',
            password: 'auth',
            db: 0,
        },
    };

    config.sequelize = {
        dialect: 'mysql',
        database: 'itnavs',
        host: '127.0.0.1',
        port: '3306',
        username: 'itnavs',
        password: '123456',
        underscored: false,
        timezone: '+08:00',
        define: {
            timestamps: true,
            freezeTableName: true,
        },
    };

    return {
        ...config,
    };
};

本地安装mysql

本地redis

egg-bag-framework 框架说明

内置多种模块,中间件以及工具

,

中间件

校验sing签名是否合法,防止随意发起请求

'use strict';
module.exports = (option, app) => {
    return async function sing(ctx, next) {
        const sing = ctx.request.header.sing;
        const { domain, expireTime, cache } = ctx.app.config.website;
        const default_cache = 'redis';
        if (sing) {
            let getSing = null;
            if (cache === default_cache) {
                getSing = await app.redis.get(sing);
            } else {
                getSing = await app.lru.get(sing);
            }
            if (getSing) {
                ctx.body = ctx.resultData({ msg: 'sing签名已过期' }); // 在存在说明既过期
            } else {
                try {
                    const decSing = ctx.helper.aesDecrypt(sing);
                    const singData = JSON.parse(decSing);
                    if (singData.domain === domain) {
                        if (cache === default_cache) {
                            await app.redis.set(sing, 1);
                        } else {
                            await app.lru.set(sing, 1);
                        }
                        await app.redis.set(sing, 1);
                        await app.redis.expire(sing, expireTime);
                        await next();
                    } else {
                        ctx.body = ctx.resultData({ msg: 'sing签名不合法,缺少字符串' });
                    }
                } catch (e) {
                    ctx.body = ctx.resultData({ msg: 'sing签名不合法' });
                }
            }
        } else {
            ctx.body = ctx.resultData({ msg: '缺少sing签名' });
        }
    };
};

限流中间件

防止接口被恶意盗刷,如果被攻击了,请求使用nginx或者服务器配置白黑名单

'use strict';
const { RateLimiterMemory } = require('rate-limiter-flexible'); // 限流中间件
module.exports = () => {
    // 创建一个基于内存的令牌桶速率限制器,每秒限制 12 次请求
    const opts = {
        points: 12,
        duration: 1,
    };
    const rateLimiter = new RateLimiterMemory(opts);
    return async function limiter(ctx, next) {
        rateLimiter.consume(ctx.request.ip)
            .then(rateLimiterRes => {
                next();
            })
            .catch(rateLimiterRes => {
                ctx.body = ctx.resultData({ msg: '触发限流了', code: 2001 });
            });
    };
};

验证jwt

'use strict';

module.exports = () => {
    return async function authority(ctx, next) {
        const authorization = ctx.request.header.authorization;
        if (authorization) {
            try {
                ctx.helper.verifyToken(authorization); // 验证jwt
                await next();
            } catch (err) {
                ctx.body = ctx.resultData({ msg: 'access_token过期', code: 1003 });
            }
        } else {
            ctx.body = ctx.resultData({ msg: '缺少access_token', code: 1003 });
        }
    };
};

内置模块

jwt

(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的

// plugin.js
exports.jwt = {  
    enable: true,  
    package: 'egg-jwt',  
};
// config.default.js
config.jwt = {  
    secret: 'ABCD20231017QWERYSUNXSJL', // 可以自定义  
    sign: {  
        expiresIn: 8 * 60 * 60, // 过期时间8小时  
    },  
};

Validate

参数校验模块

// plugin.js
exports.validate = {  
    enable: true,  
    package: 'egg-validate',  
};
// config.default.js
config.validate = {  
    convert: true,  
    translate() {  
        const args = Array.prototype.slice.call(arguments);  
        return I18n.__.apply(I18n, args);  
    },  
};

redis

Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理

// plugin.js
exports.redis = {  
    enable: true,  
    package: 'egg-redis',  
};
// config.default.js
config.redis = {  
    client: {  
        port: 6379,  
        host: '127.0.0.1',  
        password: 'auth',  
        db: 0,  
    },  
}

lru

本地缓存

exports.lru = {  
    enable: true,  
    package: 'egg-lru', // 本地缓存  
};
config.lru = {  
    client: {  
        max: 3000, // 所有lru缓存配置可用  
        maxAge: 1000 * 60 * 30, // 60 min cache  
    },  
    app: true, // 加载到app中,默认是打开的  
    agent: false, // 加载到代理中,默认为关闭  
};

上传模式

// config.default.js
config.multipart = {  
    mode: 'file',  
    fileSize: '50mb', // 接收文件大小  
    whitelist: [ // 允许接收的文件类型  
        '.png',  
        '.jpg',  
        '.webp',  
        '.gif',  
        '.zip',  
        '.doc',  
        '.docx',  
        '.txt',  
        '.xlsx',  
        '.pdf',  
        '.mp4',  
        '.webm',  
        '.mov',  
        '.flv',  
        '.avi',  
        '.f4v',  
        '.mov',  
        '.m4v',  
        '.rmvb',  
        '.rm',  
        '.mpg',  
        '.mpeg',  
    ],  
};

Sequelize

Sequelize 是一个基于 promise 的 Node.js , 目前支持 , , ,  以及 . 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能

// plugin.js
exports.sequelize = {  
    enable: true,  
    package: 'egg-sequelize',  
};
config.sequelize = {  
    dialect: 'mysql',  
    database: 'pm_webleading',  
    host: '127.0.0.1',  
    port: '3306',  
    username: 'pm_webleading',  
    password: '123456',  
    underscored: false,  
    timezone: '+08:00',  
    define: {  
        timestamps: true,  
        freezeTableName: true,  
    },  
};

Mysql

MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一

// plugin.js
exports.mysql = {  
    enable: true,  
    package: 'egg-mysql',  
};
// config.default.js
config.mysql = {  
    client: {  
    // host  
    host: '127.0.0.1',  
    // 端口号  
    port: '3306',  
    // 用户名  
    user: 'pm_webleading',  
    // 密码  
    password: '123456',  
    // 数据库名  
    database: 'pm_webleading',  
    },  
    // 是否加载到 app 上,默认开启  
    app: true,  
    // 是否加载到 agent 上,默认关闭  
    agent: false,  
};

内置工具

已挂在egg.js,ctx对象上调用方法如下

'use strict';  
  
const {Controller} = require('egg');  
  
class HomeController extends Controller {  
    async index() {  
       const {ctx} = this;  
        try {  
            for (const file of ctx.request.files) {  
               const filePath = await ctx.helper.uploadLocaFile({file})  // cxt.helper.xxxx
            }  
        } finally {  
           await ctx.cleanupRequestFiles();  
        }  
    }
}
module.exports = HomeController;

上传文件本地

可以上传以上配置类型的文件

uploadLocaFile({ file, filePath }) {  
    const { ctx } = this;  
    return new Promise(async (resolve, reject) => {  
        try {  
            const filename = file.filename;  
            const extname = path.extname(filename);  
            const _filePath = filePath || `public/upload/${ctx.helper.nanoid()}${extname}`;  
            const localPath = path.join(ctx.app.baseDir, 'app', _filePath);  
            // 读取文件  
            const source = fs.createReadStream(file.filepath);  
            // 创建写入流  
            const target = fs.createWriteStream(localPath);  
            await pump(source, target);  
            resolve(_filePath);  
        } catch (err) {  
            reject(err);  
        }  
    });  
}

上传图片-带压缩

只能上传图片

uploadLocalImage({ file, filePath, width = 500, quality = 75 }) {
        const { ctx } = this;
        const extname = path.extname(file.filename);
        const _filePath = filePath || `public/image/${ctx.helper.nanoid()}${extname}`;
        const localPath = path.join(ctx.app.baseDir, 'app', _filePath);
        return new Promise((resolve, reject) => {
            Jimp.read(file.filepath)
                .then(image => {
                    image.resize(width, Jimp.AUTO)
                        .quality(quality)
                        .write(localPath);
                    resolve(_filePath);
                })
                .catch(err => {
                    reject(err);
                });
        });
    }

对称加密

aesEncrypt(data, options) {
        options = Object.assign({ key: this.app.config.website.key, iv: this.app.config.website.iv }, options);
        let str = data;
        if (typeof data === 'object') {
            str = JSON.stringify(data);
        }
        str = CryptoJS.enc.Utf8.parse(str);
        const crypto = CryptoJS.AES.encrypt(str, CryptoJS.enc.Utf8.parse(options.key), {
            iv: CryptoJS.enc.Utf8.parse(options.iv),
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7,
        });
        return crypto.toString(); // 对称加密内容
    },

对称解密

aesDecrypt(data, options) {
        options = Object.assign({ key: this.app.config.website.key, iv: this.app.config.website.iv }, options);
        const decrypt = CryptoJS.AES.decrypt(data, CryptoJS.enc.Utf8.parse(options.key), {
            iv: CryptoJS.enc.Utf8.parse(options.iv),
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7,
        });
        return CryptoJS.enc.Utf8.stringify(decrypt); // 对称解密内容
    },

非对称加密

 encrypt(str, options) {
        options = Object.assign({ publicKey: this.app.config.website.publicKey }, options);
        const encrypted = new JSEncrypt();
        encrypted.setPublicKey(options.publicKey.toString());
        return encrypted.encrypt(str); // 非对称加密字符串
    },

非对称解密

decrypt(str, options) {
        options = Object.assign({ privateKey: this.app.config.website.privateKey }, options);
        const decrypted = new JSEncrypt(); // 创建解密对象实例
        decrypted.setPrivateKey(options.privateKey.toString()); // 设置私钥
        return decrypted.decrypt(str); // 非对称解密内容
    },

md5加密

md5(data) {
        let str = data;
        if (typeof data === 'object') {
            str = JSON.stringify(data);
        }
        return CryptoJS.MD5(str)
            .toString();
    },

随机ID

nanoid(size = 12) {
        const nanoid = customAlphabet(alphabet.join(''), size);
        if (size >= 12) {
            return dayjs()
                .format('YYYYMMDD') + nanoid(); // 获取不重复随机ID
        }
        return nanoid(); // 获取重复随机ID

    },

jwt-生成token-校验token

generateToken(data) {
        return this.app.jwt.sign(data, this.app.config.jwt.secret); // 生成token
    },
    verifyToken(token) {
        return this.app.jwt.verify(token, this.app.config.jwt.secret); // 验证token
    },

源码地址