此篇文章纯属个人笔记。
Koa2框架更加轻量, 优雅, 支持async/await + Promise来处理异步操作, 十分方便作为底层框架进行定制开发。
- 实现Koa2的hello world版本
const Koa = require('koa')
const app = new Koa()
const port = 3000
app.use(async (ctx, next) => {
await next()
ctx.response.type = 'text/html'
ctx.response.body = 'hello world'
})
app.listen(port, () => {
console.log(`server is running on the port: ${port}`)
})- Context对象
上下文(Context)对象: Koa把node的request对象, response对象封装在Context对象, 所以你也可以把Context对象称为一次对话的上下文, 我们通过加工Context对象就可以控制返回给用户的内容。
下面这段Koa源码可以看见创建Context对象做了什么:
/**
* Initialize a new context.
*
* @api private
*/
createContext(req, res) {
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.response);
context.app = request.app = response.app = this;
// node的request对象
context.req = request.req = response.req = req;
// node的response对象
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
// Koa的response对象
request.response = response;
// Koa的request对象
response.request = request;
// 请求原始URL。
context.originalUrl = request.originalUrl = req.url;
// 命名空间
context.state = {};
return context;
}Koa中每个请求都会创建一个Context对象。
callback() {
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}Context对象内置了一些常用属性,比如ctx.req, ctx,res, ctx.request, ctx.response等, 我们也可以在Context对象上自定义一些属性,配置等以供全局使用。
下面我们可以打印一下ctx内容。
app.use(async (ctx, next) => {
console.log(ctx)
await next()
ctx.response.type = 'text/html'
ctx.response.body = 'hello world'
})
{
request:
{ method: 'GET',
url: '/',
header:
{ host: '192.168.2.103:3000',
'user-agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
accept:
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'accept-encoding': 'gzip, deflate',
'accept-language': 'zh-CN,zh;q=0.9',
'cache-control': 'max-age=0',
'proxy-connection': 'keep-alive',
'upgrade-insecure-requests': '1',
'x-lantern-version': '5.2.2' } },
response: { status: 404, message: 'Not Found', header: {} },
app: { subdomainOffset: 2, proxy: false, env: 'development' },
originalUrl: '/',
req: '<original node req>',
res: '<original node res>',
socket: '<original node socket>'
}写一个小demo感受下ctx.request对象的使用。
// 请求url => /search?keywords=content&pageSize=10&pageIndex=1
app.use(async (ctx, next) => {
await next()
ctx.response.type = 'text/html'
ctx.response.body = {
url: ctx.request.url, // 获取请求url
query: ctx.request.query, // 获取解析的查询字符串
querystring: ctx.request.querystring, // 获取原始的的查询字符串
search: ctx.request.search // 获取带?的查询字符串
}
})
{
"url":"/search?keywords=content&pageSize=10&pageIndex=1",
"query":{
"keywords":"content",
"pageSize":"10",
"pageIndex":"1"
},
"querystring":"keywords=content&pageSize=10&pageIndex=1",
"search":"?keywords=content&pageSize=10&pageIndex=1"
}上述的例子你可以用来处理大多数情况下的GET请求, 现在我们再学习一个demo来处理POST请求。
想要通过Koa来获取POST请求的参数上可以借助node原生的req对象。
app.use(async (ctx, next) => {
let postData = ''
await next()
ctx.response.type = 'text/html'
ctx.req.on('data', (data) => {
postData += data
})
ctx.req.on('end', () => {
console.log('over', postData)
})
})通过终端发送一个POST请求或者Postman也可以。
curl -d "name=kobe&age=10" http://ip:3000/
// 结果可以获取到
name=kobe&age=10当然开发中我们使用一些中间件来处理POST请求如koa-bodyparser。
讲完了Koa的request对象, 接下来学习下Koa的response对象。
ctx.response.body = '' // 设置响应的主体
ctx.response.status = '' // 设置请求的响应状态, 如200, 204, 500
ctx.response.type = '' // 设置响应的Content-Type, 如 html, image/png, text/plain.app.use(async (ctx, next) => {
await next()
ctx.response.status = 200
// request.accepts(types)
// 内容协商:判断客户端能接受的数据类型
if (ctx.request.accepts('json')) {
ctx.response.type = 'json'
ctx.response.body = {
"name": "kobe"
}
} else if (ctx.request.accepts('html')) {
ctx.response.type = 'html'
ctx.response.body = '<h3>h3</h3>'
} else {
ctx.response.type = 'text'
ctx.response.body = 'hello world'
}
})ctx.state是推荐的命名空间, 用于通过中间件来传递消息和前端视图。
// 把user属性存在state对象中,用以另外一个中间件读取
ctx.state.user = yield User.find(id)ctx.cookies用于设置和获取cookie。
ctx.cookies.get(name, [options]) // 获取cookie
ctx.cookies.set(name, value, [options]) // 设置cookiectx.throw用于抛出错误,把错误返回给用户。
ctx.throw([status], [msg], [properties]) // .status 属性默认为 500 的错误
ctx.throw(400);
ctx.throw(400, 'name required');
ctx.throw(400, 'name required', { user: user });note: 未完明天继续更新。