• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

guo-yu/koa-guide: koa guide in Chinese

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

guo-yu/koa-guide

开源软件地址:

https://github.com/guo-yu/koa-guide

开源编程语言:


开源软件介绍:

koa 中文文档 koa@npm Build Status

NPM NPM

Koa,下一代 Node.js web 框架

koa 简介

由 Express 原班人马打造的 koa,致力于成为一个更小、更健壮、更富有表现力的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升常用错误处理效率。Koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。

安装 koa

Koa需要支持ES2015和async function的node v7.6.0或更高版本。

您可以使用喜欢的依赖管理工具快速安装支持的node版本:

$ nvm install 7
$ npm i koa
$ node my-koa-app.js
使用Babel来支持Async Functions

在node 7.6版本以下,如果你想在koa里使用async functions,我们推荐babel

require('babel-core/register');
// require the rest of the app that needs to be transpiled after the hook
const app = require('./app');

你至少要使用transform-async-to-generator or transform-async-to-module-method插件, 来解析和转译async functions。例如,你可以在你的.babelrc文件里这样写:

{
  "plugins": ["transform-async-to-generator"]
}

你也可以在env preset里使用目标选项"node": "current"


应用(Application)

一个 Koa Application(以下简称 app)由一系列 generator 中间件组成。按照编码顺序在栈内依次执行,从这个角度来看,Koa app 和其他中间件系统(比如 Ruby Rack 或者 Connect/Express )没有什么太大差别,不过,从另一个层面来看,Koa 提供了一种基于底层中间件编写「语法糖」的设计思路,这让设计中间件变得更简单有趣。

在这些中间件中,有负责内容协商(content-negotation)、缓存控制(cache freshness)、反向代理(proxy support)与重定向等等功能的常用中间件(详见 中间件 章节),但如前所述, Koa 内核并不会打包这些中间件,让我们先来看看 Koa 极其简单的 Hello World 应用程序:

var koa = require('koa');
var app = new koa();

app.use(function *(){
  this.body = 'Hello World';
});

app.listen(3000);

如果使用Koa 2的话:

var Koa = require('koa');
var app = new Koa();

app.use(ctx => {
  ctx.body = 'Hello World';
});

app.listen(3000);

译者注: 与普通的 function 不同,generator functions 以 function* 声明,以这种关键词声明的函数支持 yield。generator function是ECMAScript 6定义的新的语法,想了解其基本用法,以及Koa如何利用generator function达到在保持js代码异步特性的同时无需编写大量回调函数,可以参考这篇文章


级联代码(Cascading)

Koa 中间件以一种非常传统的方式级联起来,你可能会非常熟悉这种写法。

在以往的 Node 开发中,频繁使用回调不太便于展示复杂的代码逻辑,在 Koa 中,我们可以写出真正具有表现力的中间件。与 Connect 实现中间件的方法相对比,Koa 的做法不是简单的将控制权依次移交给一个又一个的中间件直到程序结束,Koa 执行代码的方式有点像回形针,用户请求通过中间件,遇到 yield next 关键字时,会被传递到下一个符合请求的路由(downstream),在 yield next 捕获不到下一个中间件时,逆序返回继续执行代码(upstream)。

下边这个例子展现了使用这一特殊方法书写的 Hello World 范例:一开始,用户的请求通过 x-response-time 中间件和 logging 中间件,这两个中间件记录了一些请求细节,然后「穿过」 response 中间件一次,最终结束请求,返回 「Hello World」。

当程序运行到 yield next 时,代码流会暂停执行这个中间件的剩余代码,转而切换到下一个被定义的中间件执行代码,这样切换控制权的方式,被称为 downstream,当没有下一个中间件执行 downstream 的时候,代码将会逆序执行。

var koa = require('koa');
var app = koa();

// x-response-time
app.use(function *(next){
  // (1) 进入路由
  var start = new Date;
  yield next;
  // (5) 再次进入 x-response-time 中间件,记录2次通过此中间件「穿越」的时间
  var ms = new Date - start;
  this.set('X-Response-Time', ms + 'ms');
  // (6) 返回 this.body
});

// logger
app.use(function *(next){
  // (2) 进入 logger 中间件
  var start = new Date;
  yield next;
  // (4) 再次进入 logger 中间件,记录2次通过此中间件「穿越」的时间
  var ms = new Date - start;
  console.log('%s %s - %s', this.method, this.url, ms);
});

// response
app.use(function *(){
  // (3) 进入 response 中间件,没有捕获到下一个符合条件的中间件,传递到 upstream
  this.body = 'Hello World';
});

app.listen(3000);

在上方的范例代码中,中间件依次被执行的顺序已经在注释中标记出来。你也可以自己尝试运行一下这个范例,并打印记录下各个环节的输出与耗时。

译者注: 「级联」这个词许多人也许在 CSS 中听说过,如果你不能理解为什么在这里使用这个词,可以将这种路由结构想象成 LESS 的继承嵌套书写方式:

.middleware1 {
  // (1) do some stuff
  .middleware2 {
    // (2) do some other stuff
    .middleware3 {
      // (3) NO next yield !
      // this.body = 'hello world'
    }
    // (4) do some other stuff later
  }
  // (5) do some stuff lastest and return
}

上方的伪代码中标注了中间件的执行顺序,看起来是不是有点像 ruby 执行代码块(block)时 yield 的表现了?也许这能帮助你更好的理解 koa 运作的方式。

译者注: 更加形象的图可以参考 Django Middleware

onion.png


应用配置(Settings)

应用的配置是 app 实例的属性。目前来说,Koa 的配置项如下:

  • app.name 应用名称
  • app.env 执行环境,默认是 NODE_ENV 或者 "development" 字符串
  • app.proxy 决定了哪些 proxy header 参数会被加到信任列表中
  • app.subdomainOffset 被忽略的 .subdomains 列表,详见下方 api

中间件(Middleware)


常用方法

app.listen(...)

用于启动一个服务的快捷方法,以下范例代码在 3000 端口启动了一个空服务:

var koa = require('koa');
var app = koa();

app.listen(3000);

app.listen 是 http.createServer 的简单包装,它实际上这样运行:

var http = require('http');
var koa = require('koa');
var app = koa();

http.createServer(app.callback()).listen(3000);

如果有需要,你可以在多个端口上启动一个 app,比如同时支持 HTTP 和 HTTPS:

var http = require('http');
var https = require('https');
var koa = require('koa');
var app = koa();

http.createServer(app.callback()).listen(3000);
https.createServer(app.callback()).listen(3001);

app.callback()

返回一个可被 http.createServer() 接受的程序实例,也可以将这个返回函数挂载在一个 Connect/Express 应用中。

app.use(function)

将给定的 function 当做中间件加载到应用中,详见 中间件 章节

app.keys=

设置一个签名 Cookie 的密钥。这些参数会被传递给 KeyGrip 如果你想自己生成一个实例,也可以这样:

app.keys = ['im a newer secret', 'i like turtle'];
app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

注意,签名密钥只在配置项 signed 参数为真时才会生效:

this.cookies.set('name', 'tobi', { signed: true });

错误处理(Error Handling)

除非 NODE_ENV 被配置为 "test",Koa 都将会将所有错误信息输出到 stderr,也可以自定义「错误事件」来监听 Koa app 中发生的错误,比如记录错误日志:

app.on('error', function(err){
  log.error('server error', err);
});

当任何 req 或者 res 中出现的错误无法被回应到客户端时,Koa 会在第二个参数传入这个错误的上下文:

app.on('error', function(err, ctx){
  log.error('server error', err, ctx);
});

任何错误有可能被回应到客户端,比如当没有新数据写入 socket 时,Koa 会默认返回一个 500 错误,并抛出一个 app 级别的错误到日志处理中间件中。


应用上下文(Context)

Koa 的上下文封装了 request 与 response 对象至一个对象中,并提供了一些帮助开发者编写业务逻辑的方法。为了方便,你可以在 ctx.requestctx.response 中访问到这些方法。

每一个请求都会创建一段上下文。在控制业务逻辑的中间件中,上下文被寄存在 this 对象中:

app.use(function *(){
  this; // 上下文对象
  this.request; // Request 对象
  this.response; // Response 对象
});

为了使用方便,许多上下文属性和方法都被委托代理到他们的 ctx.requestctx.response,比如访问 ctx.typectx.length 将被代理到 response 对象,ctx.pathctx.method 将被代理到 request 对象。

Request 对象

ctx.request 对象包括以下属性和别名方法,详见 Request 章节

  • ctx.header
  • ctx.method
  • ctx.method=
  • ctx.url
  • ctx.url=
  • ctx.path
  • ctx.path=
  • ctx.query
  • ctx.query=
  • ctx.querystring
  • ctx.querystring=
  • ctx.length
  • ctx.host
  • ctx.fresh
  • ctx.stale
  • ctx.socket
  • ctx.protocol
  • ctx.secure
  • ctx.ip
  • ctx.ips
  • ctx.subdomains
  • ctx.is()
  • ctx.accepts()
  • ctx.acceptsEncodings()
  • ctx.acceptsCharsets()
  • ctx.acceptsLanguages()
  • ctx.get()

Response 对象

ctx.response 对象包括以下属性和别名方法,详见 Response 章节

  • ctx.body
  • ctx.body=
  • ctx.status
  • ctx.status=
  • ctx.length=
  • ctx.type
  • ctx.type=
  • ctx.headerSent
  • ctx.redirect()
  • ctx.attachment()
  • ctx.set()
  • ctx.remove()
  • ctx.lastModified=
  • ctx.etag=

上下文对象中的其他 API

  • ctx.req: Node.js 中的 request 对象
  • ctx.res: Node.js 中的 response 对象,方法有:
    • res.statusCode
    • res.writeHead()
    • res.write()
    • res.end()
  • ctx.app: app 实例
  • ctx.state: 推荐的命名空间,用来保存那些通过中间件传递给视图的参数或数据。比如 this.state.user = yield User.find(id);
  • ctx.cookies.get(name, [options]) 对于给定的 name ,返回响应的 cookie
    • options
      • signed [boolean]
  • ctx.cookies.set(name, value, [options]) 对于给定的参数,设置一个新 cookie
    • options
      • signed [boolean]
      • expires [date]
      • path [string] 默认为 '/'
      • domain [string]
      • secure [boolean]
      • httpOnly [boolean] 默认为 true
  • ctx.throw(msg, [status]) 抛出常规错误的辅助方法,默认 status 为 500。

以下几种写法都有效:

this.throw(403)
this.throw('name required', 400)
this.throw(400, 'name required')
this.throw('something exploded')

实际上,this.throw('name required', 400) 是此代码片段的简写方法:

var err = new Error('name required');
err.status = 400;
throw err;

需要注意的是,ctx.throw 创建的错误,均为用户级别错误(标记为err.expose),会被返回到客户端。

  • ctx.assert(value, [msg], [status], [properties]) 用来断言的辅助方法,类似 Node 中的 assert() 方法。this.assert(this.user, 401, 'User not found. Please login!'); 此方法由 http-assert 模块支持。

Request

ctx.request 对象是对 Node 原生请求对象的抽象包装,提供了一些非常有用的方法。

详细的 Request 对象 API 如下:

req.header

返回请求头

req.method

返回请求方法

req.method=

设置 req.method ,用于实现输入 methodOverride() 的中间件

req.length

返回 req 对象的 Content-Length (Number)

req.url

返回请求 url

req.url=

设置请求 url,用于进行 url 重写

req.path

返回请求 pathname

req.path=

设置请求 pathname,如果原有 url 存在查询字符串,则保留这些查询。

req.querystring

返回 url 中的查询字符串,去除了头部的 '?'

req.querystring=

设置查询字符串,不包含 '?'

req.search

返回 url 中的查询字符串,包含了头部的 '?'

req.search=

设置查询字符串,包含 '?'

req.host

返回请求主机名,不包含端口;当 app.proxy 设置为 true 时,支持 X-Forwarded-Host

req.type

返回 req 对象的 Content-Type,不包括 charset 属性,范例代码:

var ct = this.type;
// => "image/png"

req.query

返回经过解析的查询字符串,类似 Express 中的 req.query,当不存在查询字符串时,返回空对象。

当 url 包含查询字符串 "color=blue&size=small" 时,返回如下:

{
  color: 'blue',
  size: 'small'
}

req.query=

设置给定的对象为查询对象。范例代码如下:

this.query = { next: '/login' };

req.fresh

检查客户端请求的缓存是否是最新。当缓存为最新时,可编写业务逻辑直接返回 304,范例代码如下:

this.set('ETag', '123');

// 当客户端缓存是最新时
if (this.fresh) {
  this.status = 304;
  return;
}

// 当客户端缓存已过期时,返回最新的数据
this.body = yield db.find('something');

req.stale

与 req.fresh 返回的结果正好相反

req.protocol

返回请求协议名,如 "https" 或者 "http";当 app.proxy 设置为 true 时,支持 X-Forwarded-Proto


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Kotlin/kotlin-koans: Kotlin workshop发布时间:2022-06-24
下一篇:
functional-koans/clojure-koans: A set of exercises for learning Clojure发布时间:2022-06-24
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap