赞
踩
Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。
Express 是 Node.js 社区广泛使用的框架,简单且扩展性强,非常适合做个人项目。但框架本身缺少约定,标准的 MVC 模型会有各种千奇百怪的写法。Egg 按照约定进行开发,奉行『约定优于配置』,团队协作成本低。
Sails 是和 Egg 一样奉行『约定优于配置』的框架,扩展性也非常好。但是相比 Egg,Sails 支持 Blueprint REST API、WaterLine 这样可扩展的 ORM、前端集成、WebSocket 等,但这些功能都是由 Sails 提供的。而 Egg 不直接提供功能,只是集成各种功能插件,比如实现 egg-blueprint,egg-waterline 等这样的插件,再使用 sails-egg 框架整合这些插件就可以替代 Sails 了。
我们推荐直接使用脚手架,只需几条简单指令,即可快速生成项目(npm >=6.1.0
):
- 创建项目文件夹(egg-example),并且进入该文件夹
- $ mkdir egg-example && cd egg-example
- 初始化项目
- $ npm init egg --type=simple
- 安装项目依赖
- $ npm i
- 启动
- $ npm run dev
-
- 浏览器直接打开这个端口: http://localhost:7001
项目完整目录结构:
egg-project
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── app
| ├── router.js
│ ├── controller
│ | └── home.js
│ ├── service (可选)
│ | └── user.js
│ ├── middleware (可选)
│ | └── response_time.js
│ ├── schedule (可选)
│ | └── my_task.js
│ ├── public (可选)
│ | └── reset.css
│ ├── view (可选)
│ | └── home.tpl
│ └── extend (可选)
│ ├── helper.js (可选)
│ ├── request.js (可选)
│ ├── response.js (可选)
│ ├── context.js (可选)
│ ├── application.js (可选)
│ └── agent.js (可选)
├── config
| ├── plugin.js
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| └── config.unittest.js (可选)
└── test
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
虽然开始我们有些目录没有,当我们需要用到哪些目录在创建
第一步需要编写的是 Controller 和 Router。
Router 主要用来描述请求 URL 和具体承担执行动作的 Controller 的对应关系, 框架约定了 app/router.js
文件用于统一所有路由规则。
通过统一的配置,我们可以避免路由规则逻辑散落在多个地方,从而出现未知的冲突,集中在一起我们可以更方便的来查看全局的路由规则。
app/router.js
里面定义 URL 路由规则- // app/router.js
- module.exports = (app) => {
- const { router, controller } = app;
- router.get('/user/:id', controller.user.info);
- };
app/controller
目录下面实现 Controller- // app/controller/user.js
- class UserController extends Controller {
- async info() {
- const { ctx } = this;
- ctx.body = {
- name: `hello ${ctx.params.id}`,
- };
- }
- }
我们通过 Router 将用户的请求基于 method 和 URL 分发到了对应的 Controller 上,那 Controller 负责做什么?
简单的说 Controller 负责解析用户的输入,处理后返回相应的结果,例如
框架推荐 Controller 层主要对用户的请求参数进行处理(校验、转换),然后调用对应的 service 方法处理业务,得到业务结果后封装并返回:
所有的 Controller 文件都必须放在 app/controller
目录下,可以支持多级目录,访问的时候可以通过目录名级联访问。Controller 支持多种形式进行编写,可以根据不同的项目场景和开发习惯来选择。
初步介绍一下框架中内置的一些基础对象,包括从 Koa 继承而来的 4 个对象(Application, Context, Request, Response)
Application 是全局应用对象,在一个应用中,只会实例化一个,它继承自 Koa.Application,在它上面我们可以挂载一些全局的方法和对象。我们可以轻松的在插件或者应用中扩展 Application 对象。
Application 对象几乎可以在编写应用时的任何一个地方获取到,下面介绍几个经常用到的获取方式:
几乎所有被框架 Loader 加载的文件(Controller,Service,Schedule 等),都可以 export 一个函数,这个函数会被 Loader 调用,并使用 app 作为参数:
- // app/controller/user.js
- class UserController extends Controller {
- async fetch() {
- this.ctx.body = this.app.cache.get(this.ctx.query.id);
- }
- }
Context 是一个请求级别的对象,继承自 Koa.Context。在每一次收到用户请求时,框架会实例化一个 Context 对象,这个对象封装了这次用户请求的信息,并提供了许多便捷的方法来获取请求参数或者设置响应信息。框架会将所有的 Service 挂载到 Context 实例上,一些插件也会将一些其他的方法和对象挂载到它上面(egg-sequelize 会将所有的 model 挂载在 Context 上)。
最常见的 Context 实例获取方式是在 Middleware, Controller 以及 Service 中。Controller 中的获取方式在上面的例子中已经展示过了,在 Service 中获取和 Controller 中获取的方式一样,在 Middleware 中获取 Context 实例则和 Koa 框架在中间件中获取 Context 对象的方式一致。
框架的 Middleware 同时支持 Koa v1 和 Koa v2 两种不同的中间件写法,根据不同的写法,获取 Context 实例的方式也稍有不同(例子):
- // app.js
- module.exports = (app) => {
- app.beforeStart(async () => {
- const ctx = app.createAnonymousContext();
- // preload before app start
- await ctx.service.posts.load();
- });
- };
Request 是一个请求级别的对象,继承自 Koa.Request。封装了 Node.js 原生的 HTTP Request 对象,提供了一系列辅助方法获取 HTTP 请求常用参数。
Response 是一个请求级别的对象,继承自 Koa.Response。封装了 Node.js 原生的 HTTP Response 对象,提供了一系列辅助方法设置 HTTP 响应。
可以在 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.get(id);
- }
- }
ctx.request.query.id
和 ctx.query.id
是等价的,ctx.response.body=
和 ctx.body=
是等价的。ctx.request.body
,而不是 ctx.body
。学生信息接口API。
本地数据库最好部署在自己服务器,没有待会再给大家讲一下连接本地数据库
框架提供了 egg-mysql 插件来访问 MySQL 数据库。这个插件既可以访问普通的 MySQL 数据库,也可以访问基于 MySQL 协议的在线数据库服务。
安装对应的插件 egg-mysql :
$ npm i --save egg-mysql
开启插件:
- // config/plugin.js
- exports.mysql = {
- enable: true,
- package: 'egg-mysql',
- };
我的:
在 config/config.${env}.js
配置各个环境的数据库连接信息。
如果我们的应用只需要访问一个 MySQL 数据库实例,可以如下配置:
- // config/config.${env}.js
- exports.mysql = {
- // 单数据库信息配置
- client: {
- // 端口号,没有服务器就些本地127.0.0.1
- host: 'mysql.com',
- // 端口号
- port: '3306',
- // 用户名
- user: 'test_user',
- // 密码
- password: 'test_password',
- // 数据库名
- database: 'test',
- },
- // 是否加载到 app 上,默认开启
- app: true,
- // 是否加载到 agent 上,默认关闭
- agent: false,
- };
我的:
使用方式:
await app.mysql.query(sql, values); // 单实例可以直接通过 app.mysql 访问
建表:
- // service/student.js
- const { Service } = require("egg");
-
- class studentService extends Service {
- // 查询所有学生信息
- async findAll() {
- let { mysql } = this.app;
- const students = await mysql.select("student_information");
- console.log(students);
- return students;
- }
-
- // 根据id查询学生数据
- async findById(id) {
- let { mysql } = this.app;
- const student = await mysql.get("student_information", { student_id: id });
- console.log(student);
- return student;
- }
-
- // 更新插入学生数据
- async saveOrUpdate(student) {
- let result = null;
- if (student.id) {
- result = await this.app.mysql.update("student_information", student);
- } else {
- result = await this.app.mysql.insert("student_information", student);
- }
- return result;
- }
-
- // 根据id删除学生数据
- async deleteById(id) {
- const result = await this.app.mysql.delete("student_information", { id });
- }
- }
-
- module.exports = studentService;
- // 封装响应体
- // utils/Response.js
-
- class Response {
- constructor(message, data) {
- this.message = message;
- this.data = data;
- this.status = 200;
- this.timestamp = new Date().getTime();
- }
- }
-
- module.exports = Response;
- // controller/students.js
- "use strict";
-
- const Controller = require("egg").Controller;
-
- // 引入响应体
- let Response = require("../utils/Response");
-
- class StudentController extends Controller {
- // vscode 有默认的用户片段 输入controller即可生成
- async findAll() {
- // egg规定方法都是异步的
- // 应该是底层把egg相关的东西都挂上去了
- const { ctx, service } = this;
- // 调用service层查询方法
- let students = await service.student.findAll();
- // 接口返回内容
- ctx.body = new Response("查询成功", students);
- }
-
- // 根据id查询学生信息
- async findById() {
- let { ctx, service } = this;
- // 1、获取参数
- let id = ctx.query.id;
- console.log(id);
- // 调用service层方法
- // await service.findById(id);
- let student = await service.student.findById(id);
- // 给出响应
- ctx.body = new Response("查询成功", student);
- }
-
- async lists() {
- const { ctx } = this;
-
- await new Promise((resolve) => {
- // 模拟接口延迟
- setTimeout(() => {
- resolve();
- }, 1500);
- });
-
- ctx.body = [{ id: 123 }];
- }
- }
-
- module.exports = StudentController;
- // app/router.js
- "use strict";
-
- /**
- * @param {Egg.Application} app - egg application
- */
- module.exports = (app) => {
- const { router, controller } = app;
-
- router.get("/", controller.home.index);
- router.post("/logon", controller.logon.logon);
- router.get("/student", controller.student.findAll); // 新增
- // 根据id查询学生信息;
- router.get("/student/findById", controller.student.findById);
- router.get("/student/lists", controller.student.lists); // 新增
- };
当我们用axios访问是会出现跨域问题,我们用中间件解决一下:
cors中间件就是解决跨域的,express和egg框架都是可以用的,安装:
$ npm install cors --save
开启中间件:
- // plugin.js
- cors: {
- enable: true,
- package: "egg-cors",
- },
配置白名单,将所有的请求方式都添加进来
- // config.default.js
- config.cors = {
- origin: "*",
- allowMethods: "GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS",
- };
这样应该就没有什么问题了,就可以通过Ajax或者axios请求刚写的接口!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。