精华内容
下载资源
问答
  • 2021-09-30 12:50:40
    <form method="POST" action="http://localhost:7001/upload?_csrf={{ ctx.csrf | safe }}" encType="multipart/form-data">
              title: <input name="title" />
      file: <input name="file" type="file" />
              <button type="submit">Upload</button>
            </form>
    'use strict';
    const path = require('path');
    const sendToWormhole = require('stream-wormhole');
    const fs = require('mz/fs');
    const Controller = require('egg').Controller
    class UploadController extends Controller {
      async index() {
        const { ctx } = this;
        // 获取 steam
        const stream = await ctx.getFileStream();
        // 生成文件名
        const filename = Date.now() + '' + Number.parseInt(Math.random() * 10000) + path.extname(stream.filename);
        // 写入路径
        const target = path.join(this.config.baseDir, 'app/public/upload/', filename);
        const writeStream = fs.createWriteStream(target);
        try {
          // 写入文件
          await stream.pipe(writeStream)
          // await stream.pipe(writeStream);
        } catch (err) {
          // 必须将上传的文件流消费掉,要不然浏览器响应会卡死
          await sendToWormhole(stream);
          throw err;
        }
        ctx.body = {
          a: stream.fields,
          b: `http://localhost:7001/public/upload/${filename}`
        };
      }
    }
    
    
    module.exports = UploadController;
    
    
          let studentIDs = JSON.stringify([userId]);
          let Row = {
            id: jobId,
            get_people: studentIDs,
            now_nums: 1
          }
          //直接插入string
          let isUpdate = await this.app.mysql.update('jobs', Row);
          const updateSuccess = isUpdate.affectedRows === 1;
          //检验插入是否成功
          if (!updateSuccess) {
            return {
              msg: '网络错误',
              status: -80
            }
          }
    
          //先修改jobs 再修改students
          //查询兼职表,找到对应id now_nums+1
          //
          let JobIDs = JSON.stringify([jobId]);
          //查询学生当前兼职任务
          let current_jobs = await this.app.mysql.query(`select current_job from student where id=${userId};`);
          //当前兼职任务为空,直接修改
          if (!current_jobs) {
            let row = {
              JobIDs,
              id: userId
            }
            let update_current_jobs = await this.app.mysql.update('student', row);
            const up_cu_jobs_su = update_current_jobs.affectedRows === 1;
            //修改students表
            if (!up_cu_jobs_su) {
              return {
                msg: '网络错误',
                status: -89
              }
            }
            if (up_cu_jobs_su) {
              return {
                msg: true,
                status: 89
              }
            }
          }
          //当前兼职任务不为空 查询兼职任务 转换出来,查找相同id 如果有return
          //没有相同id,修改当前任务
          if (current_jobs) {
            let existed_jobs = JSON.parse(current_jobs);
    
            const current_jobs_arr = await this.app.mysql.update('student', Row); // 更新 posts 表中的记录
            const updateSuccess = result.affectedRows === 1;
            if (updateSuccess) {
              await this.app.mysql.update('student',);
            }
            [].findIndex(function (value, index) {
              return value === 
            })
          }
          if (now_nums) {
            //组合插入
    
          }
          return result[0];
    更多相关内容
  • OAuth授权服务器的nod​​ejs基于eggjs实现 快速开始 有关更多详细信息,请参见 。 发展历程 $ npm i $ npm run dev $ open http://localhost:7001/ 部署 $ npm start $ npm stop npm脚本 使用npm run lint检查代码...
  • 由redis支持的优先级作业队列,是为eggjs构建的。 安装 $ npm i egg-kue --save 用法 // {app_root}/config/plugin.js exports . kue = { enable : true , package : 'egg-kue' , } ; 配置 // {app_root}/config/...
  • Node.js & MidwayJS(EggJS) TypeScript Redis MySQL 5.7 以上 Sequelize TypeORM 底层还在进行大重构,暂时不适宜用于生产环境 Socket.io GraphQL ... 注意事项 因为 midwayjs 使用了 IoC 容器进行依赖解耦,所以...
  • linux eggjs项目发布配置
  • 基于eggjs框架搭建的代码生成器, 目前支持CRUD生成, 生成内容包括 服务端代码: eggjs restful 代码骨架文件 app/controller/api/v1/{name}.js app/model/{name}.js app/service/{name}.js app/router.js // 路由注册...
  • egg-mp 常规的后端服务,献给了不起的 特性 微信小程序 小程序登录 小程序授权 小程序支付 推送模板消息 检测是否含有敏感词 生成二维码/小程序码 接入在线客服消息 ... mchId: '', // 商户平台
  • 作者:苏辰 你好小伙伴们,记得点个赞或者给个star 包含功能 使用 下载依赖 本项目使用mysql基础sql文件在db目录 前 -用了一个星期星期业余时间开发的,大家可以在此基础上开发!
  • vue-hackernews-2.0 + eggjs = egg-vue-ssr-demo ,可以简单地理解为使用eggjs代替server.js 您也可以将其用作webpack 4.x升级参考 为什么要用鸡蛋来做SSR? 一个编写前端和后端的项目 鸡蛋集群很方便 在项目中...
  • Nodejs+eggjs+mariadb 编写的一套内容管理系统 Nodejs+eggjs+mariadb 编写的一套内容管理系统 Nodejs+eggjs+mariadb 编写的一套内容管理系统 Nodejs+eggjs+mariadb 编写的一套内容管理系统 Nodejs+eggjs+...
  • 主要介绍了React+EggJs实现断点续传的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • eggjs-demo

    2021-02-24 05:37:04
    鸡蛋的例子 鸡蛋的例子 快速开始 有关更多详细信息,请参见。 发展 $ npm i $ npm run dev ...部署 $ npm start ...使用npm run lint检查代码样式。... 使用npm run autod自动检测依赖项升级,有关更多详细信息,请参见 。
  • 按照这个链接进行安装 https://github.com/eggjs/egg-sofa-rpc/wiki/Eggjs-和-SOFA-的跨语言互调 在配置好基本环境之后,运行npm run rpc,生成调用的 proxy 文件时提示以下错误: 这个是阿里文档没有提到了一个坑...
  • Egg-Vue-Admin EggJS + Mysql + sequelize + Vue后台管理系统
  • egg-qcloud-weapp-sdk Install $ npm i egg-qcloud-weapp-sdk --save ...// {app_root}/config/plugin.js ...Why and What: wafer-node-server-sdk is for Express,but eggjs is based on Koa. so,
  • Eggjs

    2019-11-06 14:33:15
    如果了解java的可以看出eggjs的项目目录结构以及处理请求的模式和javaweb很像,controller,service,view;不陌生的是都是MVCde 开发模式,不同就是如何拿数据给请求者 项目目录 app文件夹: 项目开发...

    Egg.js

    简介

    Egg.js 为企业级框架和应用而生,帮助开发团队和开发人员降低开发和维护成本。

    专注于提供 Web 开发的核心功能和一套灵活可扩展的插件机制,不会做出技术选型,因为固定的技术选型会使框架的扩展性变差,无法满足各种定制需求。

    Egg 的插件机制有很高的可扩展性,一个插件只做一件事,Egg 通过框架聚合这些插件,并根据自己的业务场景定制配置,这样应用的开发成本就变得很低。

    Egg 奉行『约定优于配置』,按照一套统一的约定进行应用开发,Egg 有很高的扩展性,可以按照团队的约定定制框架。使用 Loader 可以让框架根据不同环境定义默认配置,还可以覆盖 Egg 的默认约定。

    特性

    • 提供基于 Egg 定制上层框架的能力
    • 高度可扩展的插件机制
    • 内置多进程管理
    • 基于 Koa 开发,性能优异
    • 框架稳定,测试覆盖率高
    • 渐进式开发(根据设备的支持情况来提供更多功能,提供离线能力,推送通知,甚至原生应用的外观和速度,以及对资源进行本地缓存。)

    Egg和koa

    如上述,Koa 是一个非常优秀的框架,然而对于企业级应用来说,它还比较基础。

    而 Egg 选择了 Koa 作为其基础框架,在它的模型基础上,进一步对它进行了一些增强。

    • 在 Express 和 Koa 中,经常会引入许许多多的中间件来提供各种各样的功能,而 Egg 提供了一个更加强大的插件机制,让这些独立领域的功能模块可以更加容易编写。
      • extend:扩展基础对象的上下文,提供各种工具类、属性。
      • middleware:增加一个或多个中间件,提供请求的前置、后置处理逻辑。
      • config:配置各个环境下插件自身的默认配置项。
      • 一个独立领域下的插件实现,可以在代码维护性非常高的情况下实现非常完善的功能,而插件也支持配置各个环境下的默认(最佳)配置,让我们使用插件的时候几乎可以不需要修改配置项。

    快速搭建环境

    • 运行环境

      • 建议选择Node.js稳定版本,最低要求 8.x
    • 初始化项目

      • npm init egg --type=simple   //--type=simple可以去掉然后自己配置
        cd demo
        npm install
        
    • 启动项目

      • npm run dev
        浏览器打开:localhost:7001
        

    注意:实际项目中,推荐使用脚手架直接初始化,所以这里不做手动初始化项目了。

    • 如果了解java的可以看出eggjs的项目目录结构以及处理请求的模式和javaweb很像,controller,service,view;不陌生的是都是MVCde 开发模式,不同就是如何拿数据给请求者

    • 项目目录

      • app文件夹:
        	项目开发文件,程序员主要操作的文件,项目的大部分代码都会写在这里。
        config文件夹:
        	这个是整个项目的配置目录,项目和服务端的配置都在这里边进行设置。
        logs文件夹:
        	日志文件夹,正常情况下不用修改和查看里边内容。
        node_modules:
        	项目所需要的模块文件,这个前端应该都非常了解,不多作介绍。
        run文件夹:
        	运行项目时,生成的配置文件,基本不修改里边的文件。
        test文件夹:
        	测试使用的配合文件,这个在测试时会使用。
        .autod.conf.js: 
        	egg.js自己生成的配置文件,不需要进行修改。
        eslinttrc和eslintignore:
        	代码格式化的配置文件。
        gitgnore:
        	git设置忽略管理的配置文件。
        package.json:
        	包管理和命令配置文件,这个文件经常进行配置。
        
      • app目录中

        controller文件夹:
        	控制器,渲染和简单的业务逻辑都会写道这个文件里。配置路由时也会用到(路由配置需要的文件都要写在控制器里)。
        public文件夹:
        	公用文件夹,把一些公用资源都放在这个文件夹下。
        router.js: 
        	项目的路由配置文件,当用户访问服务的时候,在没有中间件的情况下,会先访问router.js文件。
        service文件夹:
        	这个是当我们的业务逻辑比较复杂或和数据库打交道时,会把业务逻辑放到这个文件中。
        view文件夹:
        	模板文件夹,相当于表现层的专属文件夹,这个项目,我们使用接口的形式,所以不需要建立view文件夹。
        extend文件:
        	当我们需要写一些模板中使用的扩展方法时,我们会放到这个文件夹里。
        middleware:
        	中间件文件夹,用来写中间件的,比如最常用的路由首位。
        

    扩展插件

    在基于 Egg 的框架或者应用中,我们可以通过定义 app/extend/{application,context,request,response}.js
    这里表示可以创建application,context,request,response四个js文件

    来扩展 Koa 中对应的四个对象的原型,通过这个功能,我们可以快速的增加更多的辅助方法,例如我们在 app/extend/context.js 中写入下列代码:

    // ./app/extend/context.js
    module.exports = {
      get isIOS() {//get表示通过这个isIOS得到什么记得添加
        const iosReg = /iphone|ipad|ipod/i;//正则
        return iosReg.test(this.get('user-agent'));
          //User Agent显示使用的浏览器类型及版本、操作系统及版本、浏览器内核、等信息的标识。
      },
    };
    

    在 Controller 中,我们就可以使用到刚才定义的这个便捷属性了:

    // ./app/controller/home.js
    const Controller = require('egg').Controller;//从egg上引入控制器
    class HomeController extends Controller {//声明一个类并从constroller继承
        async index() {//声明一个函数
            this.ctx.body = this.ctx.isIOS
                ? '你的操作系统是IOS.'
                : '你的操作系统不是IOS.';
        }
    }
    module.exports = HomeController;//把这个类默认暴露出去
    
    • 静态资源

      • Egg 内置了static插件static 插件默认映射 app/public/ 目录,我们把静态资源都放到 app/public 目录即可:
    • 模板渲染

      • 框架并不强制你使用某种模板引擎,只是约定了 View 插件开发规范,开发者可以引入不同的插件来实现差异化定制。
    • 更多用法参见 View,在本例中,我们使用 Nunjucks 来渲染,先安装对应的插件 egg-view-nunjucks

    npm i egg-view-nunjucks --save
    

    开启插件:

    // ./config/plugin.js
    exports.nunjucks = {
      enable: true,//使用
      package: 'egg-view-nunjucks'//使用什么插件
    };
    // ./config/config.default.js
    exports.keys = '此处改为你自己的 Cookie 安全字符串';
    // 添加 view 配置
    exports.view = {
      defaultViewEngine: 'nunjucks',//默认视图引擎
      mapping: {//.tpl结尾的文件
        '.tpl': 'nunjucks',
      },
    };
    

    为列表页编写模板文件,一般放置在 ./app/view 目录下

    <!-- ./app/view/news/list.tpl -->
    <!-- {% %} 来当做模板,与现有的html标记混用。和php干的事情是一样的。-->
    <html>
      <head>
        <title>Hacker News</title>
        <link rel="stylesheet" href="/public/css/news.css" />
      </head>
      <body>
        <ul class="news-view view">
          {% for item in list %}<!-- 这里可以直接拿到dataList中的list但无法拿到dataList -->
            <li class="item">
              <a href="{{ item.url }}">{{ item.title }}</a>
            </li>
          {% endfor %}<!-- 结束for循环 -->
        </ul>
      </body>
    </html>
    

    添加 Controller 和 Router

    // ./app/controller/news.js
    const Controller = require('egg').Controller;
    class NewsController extends Controller {
      async list() {
        const dataList = {
          list: [
            { id: 1, title: 'this is news 1', url: '/news/1' },
            { id: 2, title: 'this is news 2', url: '/news/2' }
          ]
        };
        await this.ctx.render('news/list.tpl', dataList);
          //渲染list.tpl文件且把dataList的内容传过去
      }
    }
    
    module.exports = NewsController;
    // ./app/router.js
    module.exports = app => {
      const { router, controller } = app;
      router.get('/', controller.home.index);
      router.get('/news', controller.news.list);
    };
    

    启动浏览器,访问 http://localhost:7001/news 即可看到渲染后的页面。

    • 编写 service
      • 在实际应用中,Controller 一般不会自己产出数据,也不会包含复杂的逻辑,复杂的过程应该交给业务逻辑层 Service

    我们来添加一个 Service 抓取页面的数据 ,如下:这里只是demo写法,根据实际情况再改变

    // ./app/service/news.js
    const Service = require('egg').Service;
    class NewsService extends Service {
      async list(page) {//得到传来的页码参数
        // 读取配置拿到API接口
        const { serverUrl } = this.config.news;
    
        //使用内置的curl发出请求拿回数据
        //结构出来result,在这里给他改名叫做idList
        const { result: idList } = await this.ctx.curl(`${serverUrl}/topstories.json`, {
          data: {//携带信息
            orderBy: '"$key"',//根据什么排序
            startAt: `${page}`,//起始页
            endAt: `${page+1}`,//结束页
          },
          dataType: 'json',//需要返回的数据类型
        });
    
        // 获取详细信息
        const newsList = await Promise.all(
            //获取对象的所有键名
          Object.keys(idList).map(key => {
            const url = `${serverUrl}/item/${idList[key]}.json`;
            return this.ctx.curl(url, { dataType: 'json' });//再去请求每一个的详细信息
          })
        );
        return newsList.map(res => res.data);//把每一条的数据的data返回出去。
      }
    }
    module.exports = NewsService;
    

    框架提供了内置的 HttpClient 来方便开发者使用 HTTP 请求。
    this.ctx.curl(api地址:string,配置:json)

    然后稍微修改下之前的 Controller:

    // ./app/controller/news.js
    const Controller = require('egg').Controller;
    
    class NewsController extends Controller {
      async list() {
        const ctx = this.ctx;
        const page = ctx.query.page || 1;//获取用户url里面的page值没有就返回1
        const newsList = await ctx.service.news.list(page);//传过去,声明newsList接受return回来的数据
        await ctx.render('news/list.tpl', { list: newsList });//把list传过去,他的数据是newsList
      }
    }
    module.exports = NewsController;
    

    还需增加 app/service/news.js 中读取到的配置:

    // ./config/config.default.js
    // 添加 news 的配置项
    exports.news = {
      serverUrl: 'API接口地址'
    };
    
    • 编写扩展

      • 如果时间的数据是 UnixTime 格式的,我们希望显示为便于阅读的格式。框架提供了一种快速扩展的方式,只需在 app/extend 目录下提供扩展脚本即可,具体参见扩展。在这里,我们可以使用 View 插件支持的 Helper 来实现:

        npm i moment --save
        // ./app/extend/helper.js
        const moment = require('moment');
        exports.relativeTime = time => moment(new Date(time * 1000)).fromNow();
        
    • 在模板里面使用:

      <!-- ./app/view/news/list.tpl -->
      <html>
        <head>
          <title>Hacker News</title>
          <link rel="stylesheet" href="/public/css/news.css" />
        </head>
        <body>
          <ul class="news-view view">
            {% for item in list %}
              <li class="item">
                <a href="{{ item.url }}">{{ item.title }}</a>
              </li>
            {% endfor %}
            {{ helper.relativeTime(item.time) }}<!--通过这样的方法使用 -->
          </ul>
        </body>
      </html>
      
    • 编写中间

      • 假设我们的新闻站点,禁止百度爬虫访问。可以通过 Middleware 判断 User-Agent,如下:

        // ./app/middleware/robot.js
        // options === app.config.robot //这里的robot是这个文件的名字
        module.exports = (options, app) => {
          return async function robotMiddleware(ctx, next) {
            const source = ctx.get('user-agent') || '';
            const match = options.ua.some(ua => ua.test(source));
            //some()会让ua数组中的每一项去执行()里面的函数,如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。
            if (match) {
              ctx.status = 403;
              ctx.message = 'Go away, robot.';//别用中文
            } else {
              await next();//放行
            }
          }
        };
        // ./config/config.default.js
        // 添加中间件
        exports.middleware = [
          'robot'
        ];
        // 添加配置
        exports.robot = {
          ua: [
            /Baiduspider/i,
          ]
        };
        

    现在可以使用 curl http://localhost:7001/news -A "Baiduspider" 看看效果。

    如果你是window用户在CMD下是无法执行此命令的,推荐你安装Git Bash运行此命令

    • 配置文件

      写业务的时候,不可避免的需要有配置文件,框架提供了强大的配置合并管理功能:

      • 支持按环境变量加载不同的配置文件,如 config.local.jsconfig.prod.js 等等。
      • 应用/插件/框架都可以配置自己的配置文件,框架将按顺序合并加载。
      • 具体合并逻辑可参见配置文件
    // ./config/config.default.js
    // 这里是默认值
    exports.robot = {
      ua: [
        /curl/i,
        /Baiduspider/i,
      ],
    };
    // ./config/config.local.js
    // 仅在开发模式下读取,将覆盖默认值
    exports.robot = {
      ua: [
        /Baiduspider/i,
      ],
    };
    // ./app/service/some.js
    const Service = require('egg').Service;
    
    class SomeService extends Service {
      async list() {
        const rule = this.config.robot.ua;// /Baiduspider/i,
      }
    }
    module.exports = SomeService;
    
    • 单元测试

    单元测试非常重要,框架也提供了 egg-bin 来帮开发者无痛的编写测试。

    测试文件应该放在项目根目录下的 test 目录下,并以 test.js 为后缀名,即 ./test/**/*.test.js

    **表示任何文件夹
    *表示任何文件

    npm i egg-mock --save-dev
    

    然后配置依赖和 npm scripts配置:

    {
      "scripts": {
        "test": "egg-bin test",
      }
    }
    // ./test/app/middleware/robot.test.js
    const { app, mock, assert } = require('egg-mock/bootstrap');
    //他会去找到./app/middleware/robot.js进行测试
    describe('test/app/middleware/robot.test.js', () => {
      it('阻止机器人爬虫', () => {
        return app.httpRequest()
          .get('/')
          .set('User-Agent', "Baiduspider")
          .expect(403);
      });
    });
    
    展开全文
  • HzBookBack【eggjs + sequelize + mysql】 简单能用的预设api接口,搭配前端【HzBookInfo】 技术栈 框架:eggjs 数据库:mysql ORM:egg-sequelize,mysql2,sequelize-cli 鉴权:egg-passport,egg-passport-jwt,...
  • egg:一个Eggjs后端项目

    2021-03-30 05:07:39
    egg:一个Eggjs后端项目
  • 基于Nodejs+eggjs+mongodb编写的一套内容管理系统,结构简单,较目前一些开源的cms,doracms易于拓展,特别适合前端开发工程师做二次开发。 基于Nodejs+eggjs+mongodb编写的一套内容管理系统,结构简单,较目前一些...
  • 卵流入数据库 安装 $ npm i egg-influxdb --save 用法 // {app_root}/config/plugin.js exports .... enable : true , package : 'egg-influxdb' , } ;...// {app_root}/config/config.default.js exports ....
  • eggjs 项目实践

    2021-11-10 11:33:48
    架构:eggjs + egg-jwt + egg-sequelize + egg-validate 数据库:mysql 实现功能:登录、验证码、权限菜单、鉴权、角色、用户、上传、下载、错误统一处理 api格式: restful + json 项目目录 app->...

    架构:eggjs + egg-jwt + egg-sequelize + egg-validate

    数据库:mysql

    实现功能:登录、验证码、权限菜单、鉴权、角色、用户、上传、下载、错误统一处理

    api格式: restful + json

    项目目录

     app->controller->base_controller.js

    const { Controller } = require('egg');
    class BaseController extends Controller {
        // get user() {
        //     return this.ctx.session.user;
        // }
    
        success(msg = '', status = 200, data = {}) {
            const { ctx } = this;
            ctx.body = {
                msg,
                status,
                data
            };
        }
    
        error(msg = '', status = 500, data = {}) {
            const { ctx } = this;
            ctx.body = {
                msg,
                status,
                data
            };
        }
    
        // notFound(msg) {
        //     msg = msg || 'not found';
        //     this.ctx.throw(404, msg);
        // }
    }
    module.exports = BaseController;

    app->controller->home.js

    'use strict';
    
    const Controller = require('./base_controller');
    
    class HomeController extends Controller {
      async index() {
        this.success('hi, egg!', 200);
      }
    }
    
    module.exports = HomeController;
    

    app->controller->menu.js

    'use strict';
    const Controller = require('./base_controller');
    
    class menuController extends Controller {
      async index() {
        const { ctx } = this;
    
        ctx.validate({ isMenu: 'boolean' });
    
        const { isMenu } = ctx.request.body;
        this.success('查询成功', 200, {
          list: await ctx.service.menu.getMenu(isMenu)
        });
      }
      
      async update() {
        const { ctx } = this;
    
        ctx.validate({ name: 'string', pid: 'number' });
        ctx.validate({ id: 'string' }, ctx.params);
        
        const { name, pid } = ctx.request.body;
        await ctx.service.menu.editMenu(ctx.params.id, name, pid);
        if (ctx.status === 400) {
          this.error('编辑失败', 400);
        } else {
          this.success('编辑成功', 200);
        }
      }
    }
    
    module.exports = menuController;

    app->controller->role.js

    'use strict';
    
    const Controller = require('./base_controller');
    
    class RoleController extends Controller {
      async index() {
        const { ctx } = this;
        this.success('查询成功', 200, {
          list: await ctx.service.role.getRole()
        });
      }
      async show() {
        const { ctx } = this;
    
        ctx.validate({ id: 'string' }, ctx.params);
    
        this.success('查询成功', 200, {
          list: await ctx.service.role.getRole(ctx.params.id)
        });
      }
      // 插入角色
      async create() {
        const { ctx } = this;
    
        ctx.validate({ name: 'string', pid: 'number'});
    
        const { name, pid } = ctx.request.body;
        await ctx.service.role.addRole(name, pid);
        this.success('新增成功', 200);
      }
      // 更新角色
      async update() {
        const { ctx } = this;
    
        ctx.validate({ name: 'string' });
        ctx.validate({ pid: 'string' }, ctx.params);
    
        const { name, pid } = ctx.request.body;
        await ctx.service.role.editRole(ctx.params.id, name, pid);
        if (ctx.status === 400) {
          this.error('修改失败', 400);
        } else {
          this.success('修改成功', 200);
        }
      }
      // 移除角色
      async remove() {
        const { ctx } = this;
    
        ctx.validate({ id: 'string' }, ctx.params);
    
        await ctx.service.role.removeRole(ctx.params.id);
        if (ctx.status === 400) {
          this.error('删除失败', 400);
        } else {
          this.success('删除成功', 200);
        }
      }
    }
    module.exports = RoleController;

    app->controller->upload.js

    const Controller = require('./base_controller');
    const path = require('path');
    const fs = require("fs");
    const dayjs = require('dayjs');
    
    function mkdirsSync(dirname) {
      if (fs.existsSync(dirname)) {
        return true;
      } else {
        if (mkdirsSync(path.dirname(dirname))) {
          fs.mkdirSync(dirname);
          return true;
        }
      }
    }
    
    module.exports = class extends Controller {
      // 上传单个文件
      async uploadOne() {
        const { ctx } = this;
        const file = ctx.request.files[0]
        console.log('-----------获取数据 start--------------');
        console.log(file);
        console.log('-----------获取数据 end--------------');
        // 基础的目录
        const uplaodBasePath = '../public/uploads';
        // 生成文件名
        const filename = `${Date.now()}${Number.parseInt(
          Math.random() * 1000,
        )}${path.extname(file.filename).toLocaleLowerCase()}`;
        // 生成文件夹
        const dirname = dayjs(Date.now()).format('YYYY/MM/DD');
        mkdirsSync(path.join(uplaodBasePath, dirname));
        // 生成写入路径
        const url = `/public/uploads/${dirname}/${filename}`
        try {
          //异步把文件流 写入
          fs.writeFileSync(`app/${url}`, fs.readFileSync(file.filepath));
        } catch (err) {
          console.error(err)
        }
    
        await ctx.service.upload.addUploadFile(filename, file.filename, url);
    
        this.success('上传成功', 200, {
          name: filename,
          org_name: file.filename,
          url
        })
      }
    
      // 上传多个文件
      async uploadMulti() {
        const { ctx } = this;
        const files = ctx.request.files
        console.log('-----------获取数据 start--------------');
        console.log(files);
        console.log('-----------获取数据 end--------------');
        // 基础的目录
        const uplaodBasePath = '../public/uploads';
    
        let uplist = [];
        for (let i = 0; i < files.length; i++) {
          let element = files[i];
          // 生成文件名
          const filename = `${Date.now()}${Number.parseInt(
            Math.random() * 1000,
          )}${path.extname(element.filename).toLocaleLowerCase()}`;
          // 生成文件夹
          const dirname = dayjs(Date.now()).format('YYYY/MM/DD');
          mkdirsSync(path.join(uplaodBasePath, dirname));
          // 生成写入路径
          const url = `/public/uploads/${dirname}/${filename}`
          try {
            //异步把文件流 写入
            fs.writeFileSync(`app/${url}`, fs.readFileSync(element.filepath));
          } catch (err) {
            console.error(err)
          }
          
          uplist.push({
            name: filename,
            org_name: element.filename,
            url
          })
    
          await ctx.service.upload.addUploadFile(filename, element.filename, url);
        }
    
        this.success('上传成功', 200, {
          list: uplist
        })
      }
    
      // 通过id获取文件信息
      async show() {
        const { ctx } = this;
    
        ctx.validate({ id: 'string'}, ctx.params)
    
        const data = await ctx.service.upload.getUpload(ctx.params.id);
        this.success('查询成功', 200, data);
      }
    
    
    };

    app->controller->user.js

    'use strict';
    
    const Controller = require('./base_controller');
    const svgCaptcha = require('svg-captcha');
    
    class UserController extends Controller {
      // 登录
      async login() {
        const { ctx, app } = this;
        const data = ctx.request.body;
    
        ctx.validate({ captcha: 'string', username: 'string', password: 'string'})
    
        // 判断验证码是否可用
        if(ctx.session.captcha === undefined){
          this.error('验证码失效,请重新获取', 400)
          return;
        }
        if(ctx.session.captcha!==data.captcha){
          this.error('请正确输入验证码', 400)
          return;
        }
        // 判断该用户是否存在并且密码是否正确
        const getUser = await ctx.service.user.validUser(data.username, data.password);
        if (getUser) {
          const token = app.jwt.sign({ username: data.username }, app.config.jwt.secret);
          this.success('登录成功', 200, {
            token,
            roleid: getUser.roleid
          });
        } else {
          this.error('用户名或密码错误', 400)
        }
      }
    
      // 获取验证码
      async captcha() {
        const { ctx } = this;
        const captcha = svgCaptcha.create({
          size: 4,              //图片验证码的字符数
          fontSize: 50,
          ignoreChars: 'Ooli',    //忽略的一些字符
          width: 100,
          height: 40,
          noise: 2,
          color: true,
          background: '#ffffff',
        });
        ctx.session.maxAge = 60 * 1000;
        ctx.session.captcha = captcha.text;       //text及data都是函数返回的string属性的对象  将图片验证码中的text传到session里边 
        ctx.response.type = 'image/svg+xml';     //返回的类型
        this.success('查询成功', 200, {
          svg: captcha.data,
          captcha: captcha.text
        });
      }
    
      // 获取所有用户
      async index() {
        const { ctx } = this;
        const data = await ctx.service.user.getUser();
        this.success('查询成功', 200, data);
      }
      // 通过id获取用户
      async show() {
        const { ctx } = this;
    
        ctx.validate({ id: 'string'}, ctx.params)
    
        const data = await ctx.service.user.getUser(ctx.params.id);
        this.success('查询成功', 200, data);
      }
      // 创建用户
      async create() {
        const { ctx } = this;
    
        ctx.validate({ username: 'string', roleid: 'number' });
    
        const { username, roleid } = ctx.request.body;
        await ctx.service.user.addUser(username, roleid);
        this.success('新增成功', 200);
      }
      // 修改密码
      async updatePwd() {
        const { ctx } = this;
    
        ctx.validate({ password: 'string' });
        ctx.validate({ password: 'string' }, ctx.params);
    
        const { password } = ctx.request.body;
        await ctx.service.user.editPwd(ctx.params.id, password);
        this.success('修改成功', 200);
      }
    
      // 明文密码得到加密密码
      async getMd5Data() {
        const { ctx } = this;
    
        ctx.validate({ data: 'string' }, ctx.params);
    
        this.success('查询成功', 200, {
          password: await ctx.service.user.getMd5Data(ctx.params.data)
        });
      }
    }
    
    module.exports = UserController;

    app->middleware->error_handler.js

    'use strict';
    
    module.exports = () => {
        return async function errorHandler(ctx, next) {
            try {
                await next();
            } catch (err) {
                // 所有的异常都会在app上出发一个error事件,框架会记录一条错误日志
                ctx.app.emit('error', err, ctx);
    
                const status = err.status || 500;
    
                // 如果时生产环境的时候 500错误的详细错误内容不返回给客户端
                const error = status === 500 && ctx.app.config.env === 'prod' ? '网络错误' : err.message;
    
                ctx.body = {
                    msg: error,
                    status: status,
                    data: {},
                };
            }
        };
    
    };
    

    app->middleware->notfound_handler.js

    module.exports = () => {
        return async function notFoundHandler(ctx, next) {
            await next();
            if (ctx.status === 404 && !ctx.body) {
                ctx.body = {
                    msg: 'Not Found',
                    status: ctx.status,
                    data: {},
                };
            }
        };
    };

    app->mode->menu.js

    'use strict';
    
    module.exports = app => {
      const { INTEGER, STRING } = app.Sequelize;
      const Menu = app.model.define('menu', {
        id: { type: INTEGER, primaryKey: true, autoIncrement: true },
        name: STRING(50),
        pid: INTEGER,
      }, {
        timestamps: false,
      });
      return Menu;
    };

    app->mode->role.js

    'use strict';
    
    module.exports = app => {
      const { INTEGER, STRING } = app.Sequelize;
      const Role = app.model.define('role', {
        id: { type: INTEGER, primaryKey: true, autoIncrement: true },
        name: STRING(50),
        pid: INTEGER,
      }, {
        timestamps: false,
      });
      return Role;
    };

    app->mode->upload.js

    'use strict';
    
    module.exports = app => {
      const { STRING, INTEGER } = app.Sequelize;
      const Upload = app.model.define('upload', {
        id: { type: INTEGER, primaryKey: true, autoIncrement: true },
        name: STRING(50),
        org_name: STRING(50),
        path: STRING(100)
      }, {
        timestamps: false,
      });
      return Upload;
    };

    app->mode->user.js

    'use strict';
    
    module.exports = app => {
      const { STRING, INTEGER } = app.Sequelize;
      const User = app.model.define('user', {
        id: { type: INTEGER, primaryKey: true, autoIncrement: true },
        username: STRING(20),
        password: STRING(50),
        roleid: INTEGER,
      }, {
        timestamps: false,
      });
      User.associate = function() {
        app.model.User.belongsTo(app.model.Role, { foreignKey: 'roleid', targetKey: 'id', as: 'role' });
      };
      return User;
    };

    app->service->menu.js

    'use strict';
    const Service = require('egg').Service;
    function toInt(str) {
      if (typeof str === 'number') return str;
      if (!str) return str;
      return parseInt(str, 10) || 0;
    }
    class MenuService extends Service {
      // 构建菜单权限树
      // 如果id为空,则构建所有的数据
      // id不为空,则构建以id为根结点的树
      buildTree(id, data, isMenu) {
        const res = [];
        if (id) {
          for (const item of data) {
            if (toInt(item.id) === toInt(id)) {
              item.children = getNode(id);
              res.push(item);
            }
          }
        } else {
          for (const item of data) {
            if (!item.pid) {
              item.children = getNode(item.id);
              res.push(item);
            }
          }
        }
        // 传入根结点id 递归查找所有子节点
        function getNode(id) {
          const node = [];
          for (const item of data) {
            if (toInt(item.pid) === toInt(id) && (isMenu === true ? item.children : true)) {
              item.children = getNode(item.id);
              node.push(item);
            }
          }
          if (node.length === 0) return;
          return node;
        }
        return res;
      }
      // 获取所有子节点集合
      getChildrenIds(treeData) {
        const res = [];
        function getIds(treeData, res) {
          for (const item of treeData) {
            res.push(item.id);
            if (item.children) { getIds(item.children, res); }
          }
        }
        getIds(treeData, res);
        return res;
      }
      // 查询角色并构建菜单树
      async getMenu(isMenu) {
        const { ctx } = this;
        const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };
        const data = await ctx.model.Menu.findAll({ query, raw: true });
        return this.buildTree(null, data, isMenu);
      }
      // 根据id查询角色
      async getMenuById(id) {
        const { ctx } = this;
        return await ctx.model.Menu.findByPk(toInt(id));
      }
      // 编辑菜单
      async editMenu(id, name, pid) {
        const { ctx } = this;
        const menu = await this.getMenuById(toInt(id));
        if (!menu) {
          ctx.status = 404;
          return;
        }
        await menu.update({ name: name || menu.name, pid: pid || menu.pid });
        ctx.status = 200;
      }
    }
    
    module.exports = MenuService;

    app->service->role.js

    'use strict';
    const Service = require('egg').Service;
    function toInt(str) {
      if (typeof str === 'number') return str;
      if (!str) return str;
      return parseInt(str, 10) || 0;
    }
    class RoleService extends Service {
      // 构建树形结构数据
      // 如果id为空,则构建所有的数据
      // id不为空,则构建以id为根结点的树
      buildTree(id, data) {
        const res = [];
        if (id) {
          for (const item of data) {
            if (toInt(item.id) === toInt(id)) {
              item.children = getNode(id);
              res.push(item);
            }
          }
        } else {
          for (const item of data) {
            if (!item.pid) {
              item.children = getNode(item.id);
              res.push(item);
            }
          }
        }
        // 传入根结点id 递归查找所有子节点
        function getNode(id) {
          const node = [];
          for (const item of data) {
            if (toInt(item.pid) === toInt(id)) {
              item.children = getNode(item.id);
              node.push(item);
            }
          }
          if (node.length === 0) return;
          return node;
        }
        return res;
      }
      // 获取所有子节点集合
      getChildrenIds(treeData) {
        const res = [];
        function getIds(treeData, res) {
          for (const item of treeData) {
            res.push(item.id);
            if (item.children) { getIds(item.children, res); }
          }
        }
        getIds(treeData, res);
        return res;
      }
      // 查询角色并构建角色树
      async getRole(id) {
        const { ctx } = this;
        const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };
        const data = await ctx.model.Role.findAll({ query, raw: true });
        return this.buildTree(id, data);
      }
      // 根据id查询角色
      async getRoleById(id) {
        const { ctx } = this;
        return await ctx.model.Role.findByPk(toInt(id));
      }
      // 插入角色
      async addRole(name, pid) {
        const { ctx } = this;
        await ctx.model.Role.create({ name, pid });
      }
      // 修改角色
      async editRole(id, name, pid) {
        const { ctx } = this;
        const role = await this.getRoleById(toInt(id));
        if (!role) {
          ctx.status = 404;
          return;
        }
        await role.update({ name: name || role.name, pid: pid || role.pid });
        ctx.status = 200;
      }
      // 删除角色
      async removeRole(id) {
        const { ctx } = this;
        const roleTree = await this.getRole(toInt(id));
        const role = await this.getRoleById(toInt(id));
        if (!role) {
          ctx.status = 404;
          return;
        }
        const ids = this.getChildrenIds(roleTree);
        for (const i of ids) {
          const r = await this.getRoleById(toInt(i));
          r.destroy();
        }
        ctx.status = 200;
      }
    }
    module.exports = RoleService;

    app->service->upload.js

    'use strict';
    const Service = require('egg').Service;
    
    function toInt(str) {
        if (typeof str === 'number') return str;
        if (!str) return str;
        return parseInt(str, 10) || 0;
    }
    
    class UploadService extends Service {
        // 插入上传文件
        async addUploadFile(name, org_name, path) {
            const { ctx } = this;
            await ctx.model.Upload.create({ name, org_name, path });
        }
    
        // 获取上传文件
        async getUpload(id) {
            const { ctx } = this;
            return await ctx.model.Upload.findByPk(toInt(id));
        }
    }
    
    module.exports = UploadService;
    

    app->service->user.js

    'use strict';
    
    const Service = require('egg').Service;
    const crypto = require('crypto');
    // 设置默认密码
    const DEFAULT_PWD = '123456';
    function toInt(str) {
      if (typeof str === 'number') return str;
      if (!str) return str;
      return parseInt(str, 10) || 0;
    }
    
    class UserService extends Service {
      // 查询user表,验证密码和花名
      async validUser(username, password) {
        const data = await this.ctx.model.User.findAll();
        const pwd = this.getMd5Data(password);
        for (const item of data) {
          if (item.username === username && item.password === pwd) return item;
        }
        return false;
      }
      // 获取用户,不传id则查询所有
      async getUser(id) {
        const { ctx } = this;
        const query = { limit: toInt(ctx.query.limit), offset: toInt(ctx.query.offset) };
        if (id) {
          return await ctx.model.User.findByPk(toInt(id));
        }
        return await ctx.model.User.findAll({ query,
          attributes: [ 'username' ],
          include: [{
            model: ctx.model.Role,
            as: 'role',
            attributes: [ 'name', 'pid' ],
          }],
        });
      }
      // 新增人员
      async addUser(username, roleid) {
        const { ctx } = this;
        const password = this.getMd5Data(DEFAULT_PWD);
        await ctx.model.User.create({ username, password, roleid });
      }
      // 修改密码
      async editPwd(id, password) {
        const { ctx } = this;
        const user = await ctx.model.User.findByPk(id);
        const newPwd = this.getMd5Data(password);
        await user.update({ password: newPwd });
      }
      // 专门对数据进行md5加密的方法,输入明文返回密文
      getMd5Data(data) {
        return crypto.createHash('md5').update(data).digest('hex');
      }
    }
    module.exports = UserService;

    app->router.js

    'use strict';
    
    /**
     * @param {Egg.Application} app - egg application
     */
    module.exports = app => {
      const { router, controller, jwt } = app;
    
      const preUrl = '/api'
    
      router.get('/', controller.home.index);
    
      router.post(`${preUrl}/user/login`, controller.user.login);
      router.get(`${preUrl}/user/captcha`, controller.user.captcha);
      // 查询
      router.get(`${preUrl}/user`, controller.user.index);
      router.get(`${preUrl}/user/:id`, jwt, controller.user.show);
      // 生成经过md5加密后的密文
      router.get(`${preUrl}/user/getMd5/:data`, controller.user.getMd5Data);
      // 新增
      router.post(`${preUrl}/user`, jwt, controller.user.create);
      // 修改密码
      router.put(`${preUrl}/user/:id`, jwt, controller.user.updatePwd);
    
      // 获取角色
      router.get(`${preUrl}/role`, controller.role.index);
      router.get(`${preUrl}/role/:id`, controller.role.show);
      // 插入角色
      router.post(`${preUrl}/role`, jwt, controller.role.create);
      // 修改角色
      router.put(`${preUrl}/role/:id`, jwt, controller.role.update);
      // 删除角色
      router.delete(`${preUrl}/role/:id`, jwt, controller.role.remove);
    
      // 获取菜单
      router.get(`${preUrl}/menu`, controller.menu.index);
      // 编辑菜单
      router.put(`${preUrl}/menu/:id`, jwt, controller.menu.update);
    
      // 上传一个文件
      router.post(`${preUrl}/upload`, controller.upload.uploadOne);
      // 上传多个文件
      router.post(`${preUrl}/upload/multi`, controller.upload.uploadMulti);
      // 获取上传文件
      router.get(`${preUrl}/upload/:id`, controller.upload.show);
    };
    

    app->config->config.default.js

    /* eslint valid-jsdoc: "off" */
    
    'use strict';
    
    /**
     * @param {Egg.EggAppInfo} appInfo app info
     */
    module.exports = appInfo => {
      /**
       * built-in config
       * @type {Egg.EggAppConfig}
       **/
      const config = exports = {};
    
      // use for cookie sign key, should change to your own and keep security
      config.keys = appInfo.name + '_1635393215721_8048';
    
      // add your middleware config here
      config.middleware = ['errorHandler', 'notfoundHandler'];
    
      // add your user config here
      const userConfig = {
        // myAppName: 'egg',
      };
    
      config.jwt = {
        secret: '123456',
      };
      // 安全配置 (https://eggjs.org/zh-cn/core/security.html)
      config.security = {
        csrf: {
          enable: false,
          ignoreJSON: true,
        },
        // 允许访问接口的白名单
        domainWhiteList: [ 'http://localhost:8080' ],
      };
      // 跨域配置
      config.cors = {
        origin: '*',
        allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH',
      };
    
      config.sequelize = {
        dialect: 'mysql',
        host: '127.0.0.1',
        port: '3306',
        user: 'root',
        password: '123456',
        database: 'test',
        define: {
          underscored: true,
          freezeTableName: true,
        },
      };
    
      config.errorHandler = {
        enable: true, // 中间件开启配置
        match: '/api', // 设置请求中间件的请求路由,只对/api开头的有效
        // ignore: '', // 设置不经过这个中间件的请求路由
      };
    
      config.notfoundHandler = {
        enable: true, // 中间件开启配置
        match: '/api', // 设置请求中间件的请求路由,只对/api开头的有效
        // ignore: '', // 设置不经过这个中间件的请求路由
      };
    
      config.captcha = {
        enable: true,
        package: 'svg-captcha'
      }
    
      config.validate = {
        // convert: false,
        // validateRoot: false,
      };
    
      config.multipart = {
        fileSize: '50mb',
        mode: 'file',
        fileExtensions: ['.txt'],
      };
    
      return {
        ...config,
        ...userConfig,
      };
    };
    

    app->config->plugin.js

    'use strict';
    
    /** @type Egg.EggPlugin */
    module.exports = {
      // had enabled by egg
      // static: {
      //   enable: true,
      // }
      jwt: {
        enable: true,
        package: 'egg-jwt',
      },
      cors: {
        enable: true,
        package: 'egg-cors',
      },
      sequelize: {
        enable: true,
        package: 'egg-sequelize',
      },
      validate: {
        enable: true,
        package: 'egg-validate',
      }
    };
    

    展开全文
  • eggjs.github.io $ npm install $ npm run dev
  • 适用于日用品、干货等商城,pc管理端 微信小程序weapp-vue-eggjs-shop-demo-master.zip
  • eggjs 创建应用知识点

    千次阅读 2022-03-13 16:30:16
    默认目录在项目下logs,其结构为:demo/logs/demo/…,默认以eggjs开头的日志后跟日志的生成者。日志的生成路径及大小可以在demo/app/config/config.${env}.js中进行配置,配置内容如下: exports.logger = { dir:'',...

    创建egg应用

    安装egg
    npm init egg --type=simple
    进入项目
    cd myegg
    安装依赖
    npm install
    启动应用
    npm run dev

    安装egg-mongoose

    npm install egg-mongoose --save

    config 文件夹修改 config.default.js

    // 配置链接mongodb路径参数
    config.mongoose = {
        client: {
          url: 'mongodb://123:123@168.1.0.1/dcw',
          options: {},
        },
      };
    
    

    需修改plugin.js

    module.exports = {
      // had enabled by egg
      // static: {
      //   enable: true,
      // }
      // 启用mongodb
      mongoose: {
        enable: true,
        package: 'egg-mongoose',
      },
      // keys: 'dcw',
    };
    

    创建controle server modle router

    controller 文件夹创建 user.js

    'use strict';
    
    // 从egg 取controller 实例
    const Controller = require('egg').Controller;
    
    class UserController extends Controller {
      async getUserList() {
        const { ctx, service } = this;
        // 获取service 业务逻辑数据
        const mockList = await service.user.find();
        ctx.body = {
          code: 1,
          msg: 'success',
          data: mockList,
        };
      }
    }
    
    module.exports = UserController;
    

    service 文件夹创建user.js

    'use strict';
    
    const Service = require('egg').Service;
    
    class UserService extends Service {
      async find() {
        // 从数据库模型拿取查询数据
        const mockList = await this.ctx.model.User.find({}, (d, s) => {
          // console.log(d, s);
        });
        return Object.assign({}, {
          pageNum: 1,
          pageSize: 10,
          list: mockList,
        });
      }
    }
    
    module.exports = UserService;
    
    

    model 文件夹创建user.js

    'use strict';
    
    module.exports = app => {
      // 创建mongoose实例 创建Ausers 表
      const mongoose = app.mongoose;
      const Schema = mongoose.Schema;
      // 创建表字段及类型
      const UserSchema = new Schema({
        userName: { type: String },
        passWord: { type: String },
      });
      const User = mongoose.model('Ausers', UserSchema);
      
      return User;
    };
    

    本地开发

    需要把 egg-bin 模块作为 devDependencies 引入

    npm i egg-bin --save-dev

    启动应用

    添加 npm scripts 到 package.json

    {
      "scripts": {
        "dev": "egg-bin dev"
      }
    }
    
    

    可以通过 npm run dev 命令启动应用

    环境配置

    本地启动的应用是以 env: local 启动的,读取的配置也是 config.default.js 和 config.local.js 合并的结果

    config 配置

    
    egg提供5种配置文件:
    - config.default.js:默认配置文件;
    - config.local.js:开发环境下的配置,与默认配置合并,同名则覆盖默认配置;
    - config.prod.js:生产环境下的配置,与默认配置合并,同名则覆盖默认配置;
    - config.test.js:测试环境下的配置,与默认配置合并,同名则覆盖默认配置;
    - config.unittest.js:单元测试环境下的配置,与默认配置合并,同名则覆盖默认配置;
    
    

    指定端口

    本地启动应用默认监听 7001 端口,可指定其他端口,例如:

    {
      "scripts": {
        "dev": "egg-bin dev --port 7002"
      }
    }
    
    

    router 创建知识点

    router.resources(‘users’, ‘/api/users’, controller.users)好处相当于定义了一组RESTful的路由: 如下面7个路由

    router.get("/api/users", controller.users.index);
    router.get("/api/users/new", controller.users.new);
    router.get("/api/users/:id", controller.users.show);
    router.get("/api/users/:id/edit", controller.users.edit);
    router.post("/api/users", controller.users.create);
    router.put("/api/users/:id", controller.users.update);
    router.delete("/api/users/:id", controller.users.destroy);
    
    

    中间件(Middleware)洋葱模型

    20180714140316832.jpeg

    编写两个测试中间件middleware1和middleware2

    // middleware/middleware1.js
    module.exports = (options, app) => {
        return async function middleware1(ctx, next) {
            console.log("==request one==");
            await next();
            console.log("==response one==");
        }
    };
    
    // middleware/middleware2.js
    module.exports = (options, app) => {
        return async function middleware2(ctx, next) {
            console.log("==request two==");
            await next();
            console.log("==response two==");
        }
    };
    

    options:中间件的参数,可在Config中配置;
    app和ctx上面也说过了,有这两个对象在中间件中可以搞好多事,比如可以拿到ctx.request对参数进行修改等;
    next为一个函数,下面会说作用。

    写完中间件之后是需要在config.default.js中进行配置:

    // config/config.default.js
    exports.middleware = ['middleware1', 'middleware2']; // 数组的顺序为中间件执行的顺序
    exports.middleware1 = { // 中间件参数(即options)
        msg: "extra message"
    }
    
    

    扩展(Extend)文件夹

    image.png

    框架的很多对象都是支持扩展的,我们可以在很多对象上扩展自定义的属性和方法,可扩展的对象有:Application、Context、helper、Request和Response。
    编写扩展的方法就是创建对应的文件,之后会与原对象进行合并对象的prototype上,以此实现了扩展的功能,在extend文件夹下创建扩展 application.js application.prod.js 为什么application会有2个扩展文件呢?看到这个命名应该就能猜到了,实际上egg也支持按照环境扩展,所以我们可以在特定的环境下扩展需要的功能。

    日志

    image.png

    在Controller、Service中使用日志时,可直接使用,this.logger,有none,warn,info,debug,error等方法。默认目录在项目下logs,其结构为:demo/logs/demo/…,默认以eggjs开头的日志后跟日志的生成者。日志的生成路径及大小可以在demo/app/config/config.${env}.js中进行配置,配置内容如下:

    exports.logger = {
        dir:'',//配置日志生成路径
        level:'DEBUG',//日志级别
        //....其它
    }
    
    

    定时发布器

    定时任务所在文件夹:/app/schedule

    const Subscription = require('egg').Subscription;
    
    class schedule1 extends Subscription{
        //设置定时任务信息
        static get schedule(){
            return {
                interval:'1m',//设置时间间隔
                type:'all'//所有worker都执行
            }
        }
    
        //要执行的内容
        async subscirbe(){
            this.logger.info(123);
        }
    }
    
    

    Cookie

    通过 ctx.cookies,我们可以在 controller 中便捷、安全的设置和读取 Cookie

    class UserController extends Controller {
      async add() {
        const ctx = this.ctx;
        let count = ctx.cookies.get('count');
        count = count ? Number(count) : 0;
        ctx.cookies.set('count', ++count);
        ctx.body = count;
      }
      async remove() {
        const ctx = this.ctx;
        ctx.cookies.set('count', null);
        ctx.status = 204;
      }
    }
    

    ctx.cookies.set(key, value, options)

    设置 Cookie 其实是通过在 HTTP 响应中设置 set-cookie 头完成的,每一个 set-cookie 都会让浏览器在 Cookie 中存一个键值对。在设置 Cookie 值的同时,协议还支持许多参数来配置这个 Cookie 的传输、存储和权限

    • {Number} maxAge: 设置这个键值对在浏览器的最长保存时间。是一个从服务器当前时刻开始的毫秒数

    • {Date} expires: 设置这个键值对的失效时间,如果设置了 maxAge,expires 将会被覆盖。如果 maxAge 和 expires 都没设置,Cookie 将会在浏览器的会话失效(一般是关闭浏览器时)的时候失效

    • {String} path: 设置键值对生效的 URL 路径,默认设置在根路径上(/),也就是当前域名下的所有 URL 都可以访问这个 Cookie

    • {String} domain: 设置键值对生效的域名,默认没有配置,可以配置成只在指定域名才能访问

    • {Boolean} httpOnly: 设置键值对是否可以被 js 访问,默认为 true,不允许被 js 访问

    • {Boolean} secure: 设置键值对只在 HTTPS 连接上传输,框架会帮我们判断当前是否在 HTTPS 连接上自动设置 secure 的值

    • {Boolean} overwrite:设置 key 相同的键值对如何处理,如果设置为 true,则后设置的值会覆盖前面设置的,否则将会发送两个 set-cookie 响应头

    • {Boolean} signed:设置是否对 Cookie 进行签名,如果设置为 true,则设置键值对的时候会同时对这个键值对的值进行签名,后面取的时候做校验,可以防止前端对这个值进行篡改。默认为 true

    • {Boolean} encrypt:设置是否对 Cookie 进行加密,如果设置为 true,则在发送 Cookie 前会对这个键值对的值进行加密,客户端无法读取到 Cookie 的明文值。默认为 false

    // 设置样例
    ctx.cookies.set(key, value, {
      httpOnly: false,
      signed: false,
    });
    
    // 获取样例
    
    ctx.cookies.get(key, options)
    
    

    Cookie 秘钥

    由于我们在 Cookie 中需要用到加解密和验签,所以需要配置一个秘钥供加密使用。在 config/config.default.js 中

    module.exports = {
      keys: 'key1,key2',
    };
    
    

    keys 配置成一个字符串,可以按照逗号分隔配置多个 key。Cookie 在使用这个配置进行加解密时:

    • 加密和加签时只会使用第一个秘钥。

    • 解密和验签时会遍历 keys 进行解密。

    如果我们想要更新 Cookie 的秘钥,但是又不希望之前设置到用户浏览器上的 Cookie 失效,可以将新的秘钥配置到 keys 最前面,等过一段时间之后再删去不需要的秘钥即可。

    Session

    框架内置了 Session 插件,给我们提供了 ctx.session 来访问或者修改当前用户 Session

    class UserController extends Controller {
      async getPosts() {
        const ctx = this.ctx;
        // 获取 Session 上的内容
        const userId = ctx.session.userId;
        const posts = await ctx.service.post.fetch(userId);
        // 修改 Session 的值
        ctx.session.visited = ctx.session.visited ? (ctx.session.visited + 1) : 1;
        ctx.body = {
          success: true,
          posts,
        };
      }
    }
    
    

    修改session, 如果要删除它,直接将它赋值为 null

    // 设置
    ctx.session.user ='xiaodoubao'
    // 删除
    ctx.session = null;
    

    设置session 注意事项

    • 不要以 _ 开头

    • 不能为 isNew

    // ❌ 错误的用法
    ctx.session._visited = 1;   //    --&gt; 该字段会在下一次请求时丢失
    ctx.session.isNew = 'HeHe'; //    --&gt; 为内部关键字, 不应该去更改
    

    更多内容请到小豆包》

    扫码关注小豆包公众号

    小豆包公众号.jpg

    展开全文
  • Eggjs的路由装饰器 用法 对于安装 npm install --save egg-blueprint 设置 在router.ts //router.ts import { Application } from 'egg' import { Blueprint } from 'egg-blueprint' export default ( app : ...
  • 为什么使用eggjs自不必言说,前端开发者想搞一下服务器接口啥的,最低学习成本的就是nodejs。加之koa是nodejs的封装库,使得nodejs更加好用。然而在eggjs的约定大于配置的前提下,对koa做了封装,使得上手成本更低,...
  • eggjs下载文件

    2022-06-02 16:23:33
    用的是eggjs框架。 记录一下。 关键代码的实现 const axios = require('axios'); const fs = require('fs'); const path = require('path'); // ..... 省略代码 /** * 下载抖音文件 */ async dyDownload() { const ...
  • sf-vue-admin dev分之为开发分之,目前正在整体重建。 注意:请使用master分之或者egg分之。master分之支持midway版,egg分之支持egg版。 构建设置 # 克隆项目 ...# 进入项目目录 cd vue-admin-template ...
  • 案例介绍 模仿实现 youTube clone项目。 在线体验地址:https://utubeclone.netlify.app 后端:... 前后端架构分离 ...先做服务接口,再做客户端应用 ...web框架:eggjs 数据库:MongoDB ORM框架:mongoo

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,769
精华内容 1,507
关键字:

eggjs