控制器

创建控制器

方式一: 继承Controller类

const { Controller } = require('egg');
class UserController extends Controller {
  create () {
    this.ctx.body = { msg: 'ok' };
    this.ctx.status = 200;
  }
}
module.exports = BaseController;

方式二: 通过定义方法直接导出

exports.create = async ctx => {
  ctx.body = { msg: 'ok' };
  ctx.status = 200;
};

方式三: 通过app.Controller创建

// 从 app 实例上获取
module.exports = app => {
  return class UserController extends app.Controller {
    // implement
  };
};

自定义 Controller 基类

按照类的方式编写 Controller,不仅可以让我们更好的对 Controller 层代码进行抽象(例如将一些统一的处理抽象成一些私有方法),还可以通过自定义 Controller 基类的方式封装应用中常用的方法。

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

此时在编写应用的 Controller 时,可以继承 BaseController,直接使用基类上的方法:

// app/controller/post.js
const Controller = require('../core/base_controller');
class PostController extends Controller {
  async list() {
    const posts = await this.service.listByUser(this.user);
    this.success(posts);
  }
}

发送HTTP相应

class PostController extends Controller {
  async create() {
    this.ctx.status = 200; // 设置HTTP状态码
    this.ctx.body = { // 设置响应体
      msg: 'ok',
      data: '',
      code: 1
    }
  }
};

ctx.bodyctx.response.body 的简写

响应体解析为:

  • 作为一个 RESTful 的 API 接口 controller,我们通常会返回 Content-Type 为 application/json 格式的 body,内容是一个 JSON 字符串。
  • 作为一个 html 页面的 controller,我们通常会返回 Content-Type 为 text/html 格式的 body,内容是 html 代码段。

由于 Node.js 的流式特性,我们还有很多场景需要通过 Stream 返回响应,例如返回一个大文件,代理服务器直接返回上游的内容,框架也支持直接将 body 设置成一个 Stream,并会同时处理好这个 Stream 上的错误事件。

class ProxyController extends Controller {
  async proxy() {
    const ctx = this.ctx;
    const result = await ctx.curl(url, {
      streaming: true,
    });
    ctx.set(result.header);
    // result.res 是一个 stream
    ctx.body = result.res;
  }
};

渲染模板

通过 ctx.render(template) 来渲染模板生成 html

class HomeController extends Controller {
  async index() {
    const ctx = this.ctx;
    await ctx.render('home.tpl', { name: 'egg' });
    // ctx.body = await ctx.renderString('hi, {{ name }}', { name: 'egg' });
  }
};

参数获取

通过查询字符串

可以通过 ctx.query 获取查询字符串

// GET http://127.0.0.1:7001/search?name=egg
exports.index = async ctx => {
  ctx.body = `search: ${ctx.query.name}`;
};

如果传递的查询字符串包括多个相同的key, 可以通过 ctx.queries 获取:

// GET /posts?category=egg&id=1&id=2&id=3
class PostController extends Controller {
  async listPosts() {
    console.log(this.ctx.queries);
    // {
    //   category: [ 'egg' ],
    //   id: [ '1', '2', '3' ],
    // }
  }
}

通过路由参数

// app/router.js
module.exports = app => {
  app.router.get('/user/:id/:name', app.controller.user.info);
};

可以通过 ctx.params 获取路由参数

// GET http://127.0.0.1:7001/user/123/xiaoqiao
exports.info = async ctx => {
  ctx.body = `user: ${ctx.params.id}, ${ctx.params.name}`;
};

表单内容的获取

通过 request.body 获取 POST、PUT、DELETE 等传过来的请求体

框架内置了 bodyParser 中间件来对这两类格式的请求 body 解析成 object 挂载到 ctx.request.body 上。

// app/controller/form.js
exports.post = async ctx => {
  ctx.body = {
    status: 1,
    data: {
      ...ctx.request.body,
    },
    msg: 'ok',
  };
};
// curl -X POST http://127.0.0.1:7001/form --data '{"name":"controller"}' --header 'Content-Type:application/json'

框架对 bodyParser 设置了一些默认参数,配置好之后拥有以下特性:

  • 当请求的 Content-Type 为 application/jsonapplication/json-patch+jsonapplication/vnd.api+jsonapplication/csp-report 时,会按照 json 格式对请求 body 进行解析,并限制 body 最大长度为 100kb。
  • 当请求的 Content-Type 为 application/x-www-form-urlencoded 时,会按照 form 格式对请求 body 进行解析,并限制 body 最大长度为 100kb。
  • 如果解析成功,body 一定会是一个 Object(可能是一个数组)。

我们最经常调整的配置项就是变更解析时允许的最大长度,可以在 config/config.default.js 中覆盖框架的默认值。

module.exports = {
  bodyParser: {
    jsonLimit: '1mb',
    formLimit: '1mb',
  },
};

如果用户的请求 body 超过了我们配置的解析最大长度,会抛出一个状态码为 413 的异常,如果用户请求的 body 解析失败(错误的 JSON),会抛出一个状态码为 400 的异常。

从请求头获取参数

  • ctx.headersctx.headerctx.request.headersctx.request.header:这几个方法是等价的,都是获取整个 header 对象。
  • ctx.get(name)ctx.request.get(name):获取请求 header 中的一个字段的值,如果这个字段不存在,会返回空字符串。
  • 建议用 ctx.get(name) 而不是 ctx.headers['name'],因为前者会自动处理大小写。

有些头部信息可以通过更加简洁的方式获取:

  • ctx.host 默认为 x-forwarded-host
  • ctx.protocol 默认为 x-forwarded-proto
  • ctx.ips 默认为 x-forwarded-for
  • ctx.ip 当 config.proxy = false 时会返回当前连接发起者的 ip 地址,ips 此时会为空数组
  • ctx.cookies.get(key) 获取指定 Cookie
  • ctx.cookies.set(key, value) 设置 Cookie
  • 直接通过 ctx.session 设置或获取session值, 比如 ctx.session.id=0, const id=ctx.session.id

MIT Licensed | Copyright © 2018-present 滇ICP备16006294号

Design by Quanzaiyu | Power by VuePress