node.js—基于Egg框架的简单后端搭建(包含传参数据库操作)

1、Egg.js 是什么?

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 了。

特性

2、搭建配置项目 

 快速初始化

我们推荐直接使用脚手架,只需几条简单指令,即可快速生成项目(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 

第一步需要编写的是 Controller 和 Router。 

路由(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}`,
    };
  }
}

什么是 Controller

我们通过 Router 将用户的请求基于 method 和 URL 分发到了对应的 Controller 上,那 Controller 负责做什么?

简单的说 Controller 负责解析用户的输入,处理后返回相应的结果,例如

  • 在 RESTful 接口中,Controller 接受用户的参数,从数据库中查找内容返回给用户或者将用户的请求更新到数据库中。
  • 在 HTML 页面请求中,Controller 根据用户访问不同的 URL,渲染不同的模板得到 HTML 返回给用户。
  • 在代理服务器中,Controller 将用户的请求转发到其他服务器上,并将其他服务器的处理结果返回给用户。

框架推荐 Controller 层主要对用户的请求参数进行处理(校验、转换),然后调用对应的 service 方法处理业务,得到业务结果后封装并返回:

  1. 获取用户通过 HTTP 传递过来的请求参数。
  2. 校验、组装参数。
  3. 调用 Service 进行业务处理,必要时处理转换 Service 的返回结果,让它适应用户的需求。
  4. 通过 HTTP 将结果响应给用户。

所有的 Controller 文件都必须放在 app/controller 目录下,可以支持多级目录,访问的时候可以通过目录名级联访问。Controller 支持多种形式进行编写,可以根据不同的项目场景和开发习惯来选择。 

3、框架内置基础对象(重点理解)

初步介绍一下框架中内置的一些基础对象,包括从 Koa 继承而来的 4 个对象(Application, Context, Request, Response)

Application

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 

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

 获取方式

最常见的 Context 实例获取方式是在 MiddlewareController 以及 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 & Response

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= 是等价的。
  • 需要注意的是,获取 POST 的 body 应该使用 ctx.request.body,而不是 ctx.body

 4、带大家写后台接口(router、Controller、service)

学生信息接口API。

安装egg-MySql和配置本地数据库

本地数据库最好部署在自己服务器,没有待会再给大家讲一下连接本地数据库

egg-mysql

框架提供了 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 访问

 多数据源:MySQL - Egg

建表:

编写service层(操作数据库)

// 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;

封装Response

// 封装响应体
// 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层

// 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;

路由router

// 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请求刚写的接口!

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北木南-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值