Egg之路由与控制器

从上一章,我们了解了egg的一些基础知识,以及尝试启动了egg的项目之后呢,本章节给大家讲解一下路由和控制器,这是两个非常重要的内容。还不是很清楚的小伙伴,我会在文章的末尾,放上,传送门,方便大家去理解学习。

一、MVC概述

什么是mvc,这个概念,我相信绝大部分有一定基础的童鞋么肯定是了解的。MVC是模型(model)- 视图(view)- 控制器(controller)的缩写。MVC是一种软件设计规范,主要作用就是逻辑拆分:
1、视图为用户展示数据
2、控制器用来处理用户输入
3、模型用户数据处理

ps:这一整套流程实际上是一个闭环,你可以这么去理解,就是用户输入给控制器,然后控制器将用户输入的指令和数据传递给业务组件,业务组件进行业务逻辑判断,数据库存取,将要展示的数据返回到视图,用户得到了反馈,在进行下一步的操作。

二、Egg中的控制器(controller)

1、直接响应数据或渲染模板
2、接受用户输入
3、与路由器建立对应关系

this.ctx可以获取到当前请求的上下文对象,通过此对象可以便捷的获取到请求与响应的属性与方法。

我们可以拿最初的模板案例来解析,controller文件夹下面的home.js文件:

'use strict'; //严格模式

const Controller = require('egg').Controller; //Controller类

class HomeController extends Controller { //定义一个新的类去继承Controller类
  async index() {
    const {
      ctx
    } = this;
    ctx.body = '你好egg';
  }
}

module.exports = HomeController; //暴露HomeController

解析:上面我也给出了注释,提醒一下的就是,这个严格模式,你写和不写都可以,没必要说一定要写,这个也不是egg特有的,因为egg已经给我们封装好了控制器的方法,所以我们只需要调用就好了,然后继承暴露出来,最简单的一个控制器我们就写好了。

接下来,尝试写一个水果控制器,首先要在controller文件夹下创建fruits.js,然后我们模仿home.js进行编写。

'use strict'; //严格模式

const Controller = require('egg').Controller; //Controller类

class FruitsController extends Controller {
  async index() {
    this.ctx.body = '我是一个水果列表'
  }
}
module.exports = FruitsController

此时,我们写完,想要去访问这个页面,肯定是需要路由去访问的,所以,我们需要去编写router.js这个文件,来访问我们刚刚写的水果页面。

'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
  router.get('/fruits', controller.fruits.index);
};

解析:通过这个代码,我们可以看出来,这个是通过暴露一个函数,这个app是一个形参,是egg应用里的一些实例,然后通过结构赋值的方法,我们拿到了,router和controller这两个对象,然后通过路由的get方法去规定跳转的路由地址,以及展示的是controller目录下面的home文件里的index方法,所以我们访问地址是'/'看到的就是'hi,egg',所以我们模仿一下,加上router.get('/fruits', controller.fruits.index); 就能通过访问'/fruits'实现访问上面写的水果页面了。

水果页面

所以举一反三,如果以后还想访问什么都能在路由里面进行配置。

三、通过路由传递参数

1、获取query参数
2、获取params参数

首先讲一下这个获取query参数的get请求方法。这个我们在控制器里靠一个属性去获取get请求的query,this.ctx.request.query,然后我们只要在url后面拼上传递的数据,就能获取到了,http://127.0.0.1:7001/fruits?index=100
完整的代码:

'use strict'; //严格模式

const Controller = require('egg').Controller; //Controller类

class FruitsController extends Controller {
  async index() {
    let query = this.ctx.request.query
    this.ctx.body = `传递的index的值是${query.index}`
  }
}
module.exports = FruitsController
query,get方式

然后我们params参数的get请求方法,该如何获取呢,首先我们要在router.js里面加上一行router.get('/fruits/:id', controller.fruits.getId),我们重新写一个getId的方法。

  async getId() {
    let id = this.ctx.params.id
    this.ctx.body = `传递的id的值是${id}`
  }

然后我们只要在url后面加上'/100'就能获取到了,http://127.0.0.1:7001/fruits/100

params,get方式

拓展:那么如果你想传多个参数,就继续往后面拼就可以了router.get('/fruits/:id/:title', controller.fruits.getId),拿的话,也和上面一样let title = this.ctx.params.title

说完了query以及params的get请求呢,接下来就是query和params的post请求了,这个稍微难一点。获取post请求参数:this.ctx.request.body
举个例子实现,表单提交是post请求,我们可以通过实现表单提交来获取post的参数,那么,我们可以通过提交表单,插入数据到列表里面。
1、第一步:要创建数组,和类同级

let fruitList = ["香蕉", "苹果", "西瓜"]

2、第二步:有了数组,我们就需要一个页面去展示这个数组

 async getList() {  //获取水果列表
    this.ctx.body = fruitList
  }
router.get('/fruitList', controller.fruits.getList);
水果列表

3、第三步:构建表单页面

 async createPage() {   //提交表单
    this.ctx.body = `
    <form>
    <input name="fruitName">
    <button>添加</button>
    </form>
    `
  }
router.get('/createPage', controller.fruits.createPage);
表单页面

4、第四步:我们需要输入,然后将输入的内容插入到水果列表里面,首先我们要修改一下createPage方法,加入请求方法为post,提交的地址也要加上。

async createPage() {
    this.ctx.body = `
    <form method='post' action='/creatFruit'>
    <input name="fruitName">
    <button>添加</button>
    </form>
    `
  }
 async creatFruit() {  //获取提交的水果数据
    let fruit = this.ctx.request.body  
    this.ctx.body = fruit
  }
router.post('/creatFruit', controller.fruits.creatFruit);

5、第五步:输入水果,提交表单,发现,页面显示403


页面403

之所以有这个情况,是因为CSRF指跨站请求伪造,Egg中对post请求做了一些安全验证,可以在config.default.js文件中,通过下面的设置验证。

config.security = {
      csrf:{
          enable:false
    }
}

配置好这个,我们再次输入水果,再次点击提交,此时,我们成功跳转到了'/createFruit'页面


展示提交的数据

6、第六步:也就是最后一步,我们需要提交跳转的页面,将数据插入到原先的列表里面去

async creatFruit() {
    let fruit = this.ctx.request.body
    fruitList.push(fruit.fruitName)
    this.ctx.body = '添加成功'
  }

此时,我们输入提交会出现 '添加成功'的字样,然后我们在跳转会'/fruitList'就会看到输入的内容已经被插入了


输入的橘子已经被成功添加进来

先给出上面所说的全部代码,在进行下一环节的内容

//fruits.js
'use strict'; //严格模式

const Controller = require('egg').Controller; //Controller类
let fruitList = ["香蕉", "苹果", "西瓜"]
class FruitsController extends Controller {

  //index方法  用于获取query的get请求参数
  async index() {
    let query = this.ctx.request.query
    this.ctx.body = `传递的index的值是${query.index}`
  }

  //getId方法  用于获取params的get请求参数
  async getId() {
    let id = this.ctx.params.id
    let title = this.ctx.params.title
    this.ctx.body = `传递的id的值是${id},传递的title是${title}`
  }

  //getList方法  用于展示水果列表
  async getList() {
    this.ctx.body = fruitList
  }

  //createPage方法  用于表单提交页面
  async createPage() {
    this.ctx.body = `
    <form method='post' action='/creatFruit'>
    <input name="fruitName">
    <button>添加</button>
    </form>
    `
  }

  //插入提交的水果
  async creatFruit() {
    let fruit = this.ctx.request.body
    fruitList.push(fruit.fruitName)
    this.ctx.body = '添加成功'
  }

}
module.exports = FruitsController
//router.js
'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const {
    router,
    controller
  } = app;
  router.get('/', controller.home.index); //默认页
  router.get('/fruits', controller.fruits.index);
  router.get('/fruits/:id/:title', controller.fruits.getId)
  router.get('/fruitList', controller.fruits.getList);
  router.get('/createPage', controller.fruits.createPage);
  router.post('/creatFruit', controller.fruits.creatFruit);
};

到这里,post的请求方法拿参数我们也学习了,不过,大家往回看路由,会发现,我们写的而很多,get写一个,post写一个,如果后面说,一个地址里面,引入了好多的方法,那会越写越多,所以这里egg为了简单写法,给出了新的形式。

四、RESTful风格的URL定义

restful风格的url可以简化路由文件
格式:app.router.resources('routerName', 'pathMatch', controller)

router.resources('posts','/api/posts',controller.posts) //一个方法同时定义增删改查
风格写法

我们来简化一下之前的路由,全部隐藏,你可以删除,然后写上

router.resources('fruits', '/fruits', controller.fruits)

根据表格的对对照关系,我们将fruits.js修改一下

async index() {
    this.ctx.body = fruitList
  }
async new() {
    this.ctx.body = `
    <form method='post' action='/fruits'>
    <input name="fruitName">
    <button>添加</button>
    </form>
    `
  }
 async create() {
    let fruit = this.ctx.request.body
    fruitList.push(fruit.fruitName)
    this.ctx.body = '添加成功'
  }

解析:考虑到有些同学不太懂,我简单的介绍一下,首先这个index方法,对应表格里面的get请求,那我们就把之前的getList的方法内容移到index方法里面,就是index方法专门去获取水果列表,表里对应的new方法,我们放表单的提交内容,有一点要注意,就是我们提交时候的action地址要变成router.js里面写的地址,creat方法对应的是post请求,所以我们放拿到参数的一系列操作。然后我们回到页面,在地址栏上输入'/fruits',就能看到列表,输入'/fruits/new',就会来到提交表单页面。

最后我们完善一下这个demo,添加成功,返回到水果列表的页面,我们只需要用this.ctx.redirect('/fruits')就能重定向到水果列表页面
完整代码如下:

//router.js
'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const {
    router,
    controller
  } = app;
  router.get('/', controller.home.index); //默认页
  router.resources('fruits', '/fruits', controller.fruits)
};
//fruits.js
'use strict'; //严格模式

const Controller = require('egg').Controller; //Controller类
let fruitList = ["香蕉", "苹果", "西瓜"]
class FruitsController extends Controller {

  //index方法  用于获取query的get请求参数
  async index() {
    this.ctx.body = fruitList
  }

  //createPage方法  用于表单提交页面
  async new() {
    this.ctx.body = `
    <form method='post' action='/fruits'>
    <input name="fruitName">
    <button>添加</button>
    </form>
    `
  }

  //插入提交的水果
  async create() {
    let fruit = this.ctx.request.body
    fruitList.push(fruit.fruitName)
    //跳转到/fruits get请求
    this.ctx.redirect('/fruits')
  }
}
module.exports = FruitsController

传送门:
第一章 Egg框架概述 :https://www.jianshu.com/p/bfdaecb5a18c

那么文章到这里也就结束了,那么按照惯例,给大家推荐一首《命运》,这首歌,大家应该都听过,我不相信命运,但是当我想做的事情做不了,想要在一起的人也只能说再见的时候,我不得不去相信,不过,鲸鱼死在了被蝴蝶救赎的海里,蝴蝶消散在夏日,我没有难过 我只是觉得在这个时刻有你在 会更好,所以,如果遇到了喜欢的人,就珍惜在一起的时光吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容