在Egg.js框架中,由于 Controller
基本上是业务开发中唯一和HTTP
协议打交道的地方,所以框架通过在 Controller
上绑定的 Context
实例,提供了许多便捷方法和属性获取用户通过 HTTP
请求发送过来的参数。本文就总结一下获取http请求的参数方法:
1.query
在 URL 中 ?
后面的部分是一个 Query String,这一部分经常用于 GET 类型的请求中传递参数。例如 GET /search?name=egg&age=26
中 name=egg&age=26
就是用户传递过来的参数。我们可以通过 context.query
(为一个对象)拿到解析过后的这个参数体
module.exports = app => {
class SearchController extends app.Controller {
* search() {
const queryObj = this.ctx.query;
console.log(queryObj.age);
console.log(queryObj);
//打印结果:{ name: 'egg', age: '26' }
}
}
return SearchController;
};
当 Query String 中的 key 重复时,context.query
只取 key 第一次出现时的值,后面再出现的都会被忽略。GET /posts?category=egg&category=koa
通过 context.query
拿到的值是 { category: 'egg' }
。
1.1 queries
有时候我们的系统会设计成让用户传递相同的 key,例如 GET /posts?category=egg&id=1&id=2&id=3
。针对此类情况,框架提供了 context.queries
对象,这个对象也解析了 Query String,但是它不会丢弃任何一个重复的数据,而是将他们都放到一个数组
中:
// GET /posts?category=egg&id=1&id=2&id=3
const Controller = require('egg').Controller;
module.exports = class PostController extends Controller {
* listPosts() {
console.log(this.ctx.queries);
//result:
// {
// category: [ 'egg' ],
// id: [ '1', '2', '3' ],
// }
}
};
context.queries
上所有的 key 如果有值,也一定会是数组
类型。
2. Router params
我们知道在 Router 上也可以申明参数,这些参数都可以通过 context.params
获取到。
// app.get('/projects/:projectId/app/:appId', 'app.listApp');
// GET /projects/1/app/2
const Controller = require('egg').Controller;
module.exports = class AppController extends Controller {
* listApp() {
assert.equal(this.ctx.params.projectId, '1');
assert.equal(this.ctx.params.appId, '2');
}
};
3. body
虽然我们可以通过 URL 传递参数,但是还是有诸多限制:
- 浏览器中会对 URL 的长度有所限制,如果需要传递的参数过多就会无法传递。
- 服务端经常会将访问的完整 URL 记录到日志文件中,有一些敏感数据通过 URL 传递会不安全。
我们知道在 header
之后还有一个 body
部分,我们通常会在这个部分传递 POST
、PUT
和 DELETE
等方法的参数。一般请求中有 body
的时候,客户端(浏览器)会同时发送 Content-Type
告诉服务端这次请求的 body
是什么格式的。Web 开发中数据传递最常用的两类格式分别是 JSON
和 Form
。
框架内置了 bodyParser 中间件来对这两类格式的请求 body
解析成 object
挂载到 context.request.body
上。HTTP
协议中并不建议在通过 GET、HEAD 方法访问时传递 body,所以我们无法在 GET
、HEAD
方法中按照此方法获取到内容。
// POST /api/posts HTTP/1.1
// Host: localhost:3000
// Content-Type: application/json; charset=UTF-8
//
// {"title": "controller", "content": "what is controller"}
const Controller = require('egg').Controller;
module.exports = class PostController extends Controller {
* listPosts() {
assert.equal(this.ctx.request.body.title, 'controller');
assert.equal(this.ctx.request.body.content, 'what is controller');
}
};
框架对 bodyParser 设置了一些默认参数,配置好之后拥有以下特性:
- 当请求的 Content-Type 为
application/json
,application/json-patch+json
,application/vnd.api+json
和 application/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
的异常。