• Nodejs之egg基本使用(初始化项目、内置对象、egg路由、egg控制器)


    Node系列文章目录

    第一章:Node.js与内置模块(fs文件系统、path路径模块、http服务器模块)

    第二章:Nodejs模块化(npm与包、开发自己的包、模块加载机制)

    第三章:Nodejs之Express(基本使用、Express路由)(一)

    第四章:Nodejs之Express( Express 中间件、中间件的分类、自定义中间件)(二)

    第五章:Nodejs之解决接口跨域问题

    第六章:Nodejs操作Mysql数据库

    第七章:Node之前后端身份认证(Session认证机制、JWT 认证机制)

    第九章:Nodejs之egg基本使用(egg服务、egg-mysql、CSRF漏洞、egg-jwt、Swagger)



    Express 是 Node.js 社区⼴泛使⽤的框架,简单且扩展性强,⾮常适合做个⼈项⽬。企业级开发框架,上层框架,提供给社区框架。

    一、初始化项目

    $ mkdir egg-example && cd egg-example
    $ npm init egg --type=simple
    $ npm install
    //启动项⽬ 使⽤npm start 或者 npm run dev
    $ npm start
    ...
    $ npm stop
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使⽤simple⻣架创建出来的项⽬⽬录如下:
    在这里插入图片描述

    • app/router.js ⽤于配置 URL 路由规则,具体参⻅ Router。
    • app/controller/ ⽤于解析⽤户的输⼊,处理后返回相应的结果,具体参 ⻅ Controller。
    • app/service/ ⽤于编写业务逻辑层,可选,建议使⽤,具体参⻅ Service。
    • app/middleware/ ⽤于编写中间件,可选,具体参⻅Middleware
    • app/public/ ⽤于放置静态资源,可选,具体参⻅内置插件 egg-static。
    • app/extend/ ⽤于框架的扩展,可选,具体参⻅框架扩展
    • config/config.{env}.js⽤于编写配置⽂件,具体参⻅配置。
    • config/plugin.js ⽤于配置需要加载的插件,具体参⻅插件。
    • test/ ⽤于单元测试,具体参⻅单元测试。
    • app.jsagent.js ⽤于⾃定义启动时的初始化⼯作,可选,具体参⻅启动 ⾃定义。关于agent.js 的作⽤参⻅Agent 机制。
      由内置插件约定的⽬录:
    • app/public/ ⽤于放置静态资源,可选,具体参⻅内置插件 egg-static。
    • app/schedule/ ⽤于定时任务,可选,具体参⻅定时任务。
    • app/view/ ⽤于放置模板⽂件,可选,由模板插件约定,具体参⻅模板渲染。
    • app/model/ ⽤于放置领域模型,可选,由领域类相关插件约定,如 egg-sequelize。

    二、内置对象

    这⾥介绍⼀下框架中内置的⼀些基础对象,包括从 Koa 继承⽽来的 4 个对象(Application、 Context、 Request、Response) 以及框架扩展的⼀些对象(Controller、Service、 Helper、Config、Logger)。

    Application

    全局应⽤对象,在⼀个应⽤中,只会实例化⼀个,它继承⾃ Koa.Application,在它上⾯我们可以挂载⼀些全局的⽅法和对象。

    获取⽅式:this.app、this.ctx.app

    /*
    可以通过 ctx.app 访问到 Application 对象
    在继承于 Controller, Service 基类的实例中,可以通过 this.app 访问到Application
    */
    // app/controller/user.js
    class UserController extends Controller {
     async fetch() {
     // 往全局app中保存数据
     this.app.cache = {name:'tom'};
     // 或者 this.ctx.body = this.app.cache.name;
     this.ctx.body = this.ctx.app.cache.name;
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Context

    Context 是⼀个请求级别的对象,继承⾃ Koa.Context。在每⼀次收到⽤户请求时,框架会实例化⼀个 Context 对象,这个对象封装了这次⽤户请求的信息,并提供了许多便捷的⽅法来获取请求参数或者设置响应信息。框架会将所有的 Service 挂载到 Context 实例上,⼀些插件也会将⼀些其他的⽅法和对象挂载到它上⾯。

    // app/controller/user.js
    class UserController extends Controller {
     async fetch() {
     // this.ctx就是Context对象
     this.ctx.body = "";
     } }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Request

    Request 是⼀个请求级别的对象,继承⾃ Koa.Request。封装了 Node.js 原⽣的HTTPRequest 对象,提供了⼀系列辅助⽅法获取 HTTP 请求常⽤参数。

    获取⽅式:this.ctx.request

    Response

    Response 是⼀个响应级别的对象,继承⾃ Koa.Response。封装了 Node.js 原⽣的HTTP Response 对象,提供了⼀系列辅助⽅法设置 HTTP 响应。

    获取⽅式:this.ctx.response

    // 可以在 Context 的实例上获取到当前请求的 Request(ctx.request) 和
    Response(ctx.response) 实例。
    // app/controller/user.js
    class UserController extends Controller {
     async fetch() {
     const { app, ctx } = this;
     const id = ctx.request.query.id;
     ctx.response.body = app.cache.name;
     //ctx.body = app.cache.name;
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Controller

    框架提供了⼀个 Controller 基类,并推荐所有的 Controller 都继承于该基类实现。
    这个Controller 基类有下列属性(也就是说Controller内的this有哪些内容):

    • ctx - 当前请求的 Context 实例。
    • app - 应⽤的 Application 实例。
    • config - 应⽤的配置。
    • service - 应⽤所有的 service。
    • logger - 为当前 controller 封装的 logger 对象。
    // app/controller/user.js
    // 从 egg 上获取(推荐)
    const {Controller} = require('egg');
    class UserController extends Controller {
     // implement
    }
    module.exports = UserController;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Service

    框架提供了⼀个 Service 基类,并推荐所有的 Service 都继承于该基类实现。Service 基类的属性和 Controller 基类属性⼀致,访问⽅式也类似:

    let { Service } = require("egg");
    
    class UserService extends Service {
    }
    module.exports = UserService;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Helper

    Helper ⽤来提供⼀些实⽤的 utility 函数。 它的作⽤在于我们可以将⼀些常⽤的动作抽离在helper.js ⾥⾯成为⼀个独⽴的函数,这样可以⽤ JavaScript 来写复杂的逻辑,避免逻辑分散各 处,同时可以更好的编写测试⽤例。Helper ⾃身是⼀个类,有和 Controller 基类⼀样的属性, 它也会在每次请求时进⾏实例化,因此 Helper上的所有函数也能获取到当前请求相关的上下⽂ 信息。可以在 Context 的实例上获取到当前请求的 Helper( ctx.helper ) 实例。

    class UserService extends Service {
     // implement
    }
    module.exports = UserService;
    // 从 app 实例上获取
    module.exports = (app) => {
     return class UserService extends app.Service {
     // implement
     };
    };
    
    // app/controller/user.js
    class UserController extends Controller {
     async fetch() {
     const { app, ctx } = this;
     const id = ctx.query.id;
     const user = app.cache.get(id);
     ctx.body = ctx.helper.formatUser(user);
     }
    }
    // app/extend/helper.js
    module.exports = {
     formatUser(user) {
     return only(user, [ 'name', 'phone' ]);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    Config

    我们推荐应⽤开发遵循配置和代码分离的原则,将⼀些需要硬编码的业务配置都放到配置⽂件中,同时配置⽂件⽀持各个不同的运⾏环境使⽤不同的配置,使⽤起来也⾮常⽅便,所有框架、插件和应⽤级别的配置都可以通过 Config 对象获取到。我们可以通过 app.config 从 Application 实例上获取到 config 对象,也可以在Controller, Service, Helper 的实例上通过this.config 获取到 config 对象。

    获取⽅式:this.config、this.app.config

    Logger

    框架内置了功能强⼤的⽇志功能,可以⾮常⽅便的打印各种级别的⽇志到对应的⽇
    志⽂件中,每⼀个 logger 对象都提供了 4 个级别的⽅法:

    • logger.debug()
    • logger.info()
    • logger.warn()
    • logger.error()

    获取⽅式:this.logger(this.ctx.logger)、this.app.logger

    三、egg路由

    Router 主要⽤来描述请求URL和具体承担执⾏动作的Controller 的对应关系, 框架约定了app/router.js ⽂件⽤于统⼀所有路由规则。

    // app/router.js
    module.exports = app => {
        const { router, controller, jwt } = app;
        router.get('/', controller.home.index);
        // 提供学生查询接口
        router.get('/student/findAll', jwt, controller.student.findAll);
        // 学生删除接口
        router.delete('/student/deleteById', controller.student.deleteById);
        // 学生新增和修改的路由
        router.post("/student/saveOrUpdate", controller.student.saveOrUpdate);
        // 分页查询接口
        router.get('/student/pageQuery', controller.student.pageQuery);
        // 学生分页模糊查询
        router.get('/student/pageQueryM', controller.student.pageQueryM);
        // 提供通过id查询学生详情
        router.get('/student/findById', controller.student.findById);
        // 登录接口
        router.post('/user/login', controller.user.login);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    四、egg控制器

    Controller 负责解析⽤户的输⼊,处理后返回相应的结果。框架推荐 Controller 层主要对⽤户的请求参数进⾏处理(校验、转换),然后调⽤对应的 service ⽅法处理业务,得到业务结果后封装并返回。所有的 Controller ⽂件都必须放在 app/controller ⽬录下,可以⽀持多级⽬录,访问的时候可以通过⽬录名级联访问。

    'use strict';
    
    const { Controller } = require('egg');
    
    class StudentController extends Controller {
        // 查询接口处理函数
        async findAll() {
       
        };
    }
    
    module.exports = StudentController;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.1 获取请求参数

    • query、queries
      ⽤来获取查询字符串的参数
    const query = this.ctx.query; //如/a?name=tom&age=12,结果为{name:'tom',age:12}
    const queries = this.ctx.queries //如/a?name=tom&name=lisi&age=12,结果为{name:['tom','lisi'],age:[12]}
    
    • 1
    • 2
    • routerParams
      ⽤来获取动态路由的参数
    const params = this.ctx.params; //如路由路径为/a/:id,使⽤/a/1路由,结果{id:1}
    
    • 1
    • 2
    • body
      ⽤来接收请求体中的参数,⽆论请求体中数据的格式是什么。
    const data = this.ctx.request.body; //如请求体中传递了数据,则在这⾥可以
    接收到
    
    • 1
    • 2
    • header
      ctx.headers,ctx.header,ctx.request.headers,ctx.request.header:这⼏个⽅法是等价的,都是获取整个 header 对象。
      ctx.get(name) 和 ctx.request.get(name):获取请求 header 中的⼀个字段的值,如果这个字段不存在,会返回空字符串。
      我们建议⽤ ctx.get(name) ⽽不是 ctx.headers[‘name’],因为前者会⾃动处理⼤⼩写。
      其他内容:
    ctx.host //localhost:7001
    ctx.protocol //http
    ctx.ips //[]
    ctx.ip // ::1
    1234
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • cookie
      HTTP 请求都是⽆状态的,但是我们的 Web 应⽤通常都需要知道发起请求的⼈是谁。为了解决这个问题,HTTP 协议设计了⼀个特殊的请求头:Cookie。服务端可以通过响应头(set-cookie)将少量数据响应给客户端,浏览器会遵循协议将数据保存,并在下次请求同⼀个服务的时候带上(浏览器也会遵循协议,只在访问符合Cookie 指定规则的⽹站时带上对应的 Cookie 来保证安全性。
      通过 ctx.cookies ,我们可以在 controller 中便捷、安全的设置和读取 Cookie。
    console.log(ctx.get('cookie'));
    console.log(ctx.request.header['cookie']);
    let count = ctx.cookies.get('count', { httpOnly: false, signed:
    false });
    count = count ? Number(count) : 0;
    ctx.cookies.set('count', ++count);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    httpOnly: 设置键值对是否可以被 js 访问,默认为 true,不允许被 js 访问。
    secure: signed:设置是否对 Cookie 进⾏签名,如果设置为 true,则设置键值对的
    时候会同时对这个键值对的值进⾏签名,后⾯取的时候做校验,可以防⽌前端对这
    个值进⾏篡改。默认为 true。

    • session

    Cookie 在 Web 应⽤中经常承担标识请求⽅身份的功能,所以 Web 应⽤在 Cookie 的基础上封装了 Session 的概念,专⻔⽤做⽤户身份识别。通过 Cookie,我们可以给每⼀个⽤户设置⼀个 Session,⽤来存储⽤户身份相关的信息,这份信息会加密后存储在 Cookie 中,实现跨请求的⽤户身份保持。

    const ctx = this.ctx;
    // 获取 Session 上的内容
    const userId = ctx.session.userId;
    const posts = await ctx.service.post.fetch(userId);
    // 设置session上的内容
    ctx.session.userId = 1;
    // 删除session上的内容
    ctx.session.userId = null;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.2 调⽤Service

    我们并不想在 Controller 中实现太多业务逻辑,所以提供了⼀个 Service 层进⾏业务逻辑的封装,这不仅能提⾼代码的复⽤性,同时可以让我们的业务逻辑更好测试。在 Controller 中可以调⽤任何⼀个 Service 上的任何⽅法,同时 Service 是懒加载的,只有当访问到它的时候框架才会去实例化它。

    const res = await ctx.service.service中的⽂件名.service中的⽅法名(参 数);
    
    • 1

    4.3 发送HTTP响应

    • status状态码
    this.ctx.status=201;
    
    • 1
    • body
      绝⼤多数的数据都是通过 body 发送给请求⽅的,和请求中的 body ⼀样,在响应中发送的 body,也需要有配套的 Content-Type 告知客户端如何对数据进⾏解析。
    • 作为⼀个 RESTful 的 API 接⼝ controller,我们通常会返回 Content-Type为application/json 格式的 body,内容是⼀个 JSON 字符串。
    • 作为⼀个 html ⻚⾯的controller,我们通常会返回 Content-Type 为 text/html 格式的 body,内容是 html 代码段。
    this.ctx.body = {
     name: 'egg',
     category: 'framework',
     language: 'Node.js'
    };
    this.ctx.body = '

    Hello

    '
    ;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 重定向
    this.ctx.redirect(url)
    
    • 1
  • 相关阅读:
    FRNet代码
    外卖App点菜页-两个tableView联动【1】
    论文阅读-----使用可分离脚印的x射线3D CT向前和向后投影
    MySQL 自定义函数时:This function has none of DETERMINISTIC, NO SQL 解决方案
    Kotlin协程最佳实践
    leetcode_1339. 分裂二叉树的最大乘积
    SAP STMS请求重复传输
    Rust编程中的线程间通信
    python-xml文件读写
    当OpenHarmony遇上OpenEuler
  • 原文地址:https://blog.csdn.net/qq_48617322/article/details/127636217