├── .gitignore ├── LICENSE ├── README.md └── onion.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime* 2 | *.DS_* 3 | node_* 4 | thumb 5 | psd 6 | *.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 turing <o.u.turing@gmail.com> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ![koa](http://ww4.sinaimg.cn/large/61ff0de3gw1ebtqstxfuvj202g01awea.jpg) 中文文档 [![koa@npm](https://badge.fury.io/js/koa.png)](https://npmjs.org/package/koa) [![Build Status](https://travis-ci.org/koajs/koa.png)](https://travis-ci.org/koajs/koa) 2 | 3 | [![NPM](https://nodei.co/npm/koa.png)](https://nodei.co/npm/koa/) [![NPM](https://nodei.co/npm-dl/koa.png?months=6)](https://nodei.co/npm/koa/) 4 | 5 | Koa,下一代 Node.js web 框架 6 | 7 | ### koa 简介 8 | 9 | 由 Express 原班人马打造的 koa,致力于成为一个更小、更健壮、更富有表现力的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升常用错误处理效率。Koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。 10 | 11 | ### 安装 koa 12 | 13 | koa 依赖支持 generator 的 Node 环境,准确来说,是 `node >= 0.11.9` 的环境。如果你正在使用一个更早的 Node 版本,可以使用模块 `n` 来管理多版本环境,并且快速安装 `0.11.x`: 14 | 15 | ```bash 16 | $ npm install -g n 17 | $ n 0.11 18 | $ node --harmony my-koa-app.js 19 | ``` 20 | 21 | 安装完成后,应确保使用 `$ node --harmony app.js` 即,harmony 模式运行程序。 22 | 23 | 为了方便,可以将 `node` 设置为默认启动 harmony 模式的别名: 24 | 25 | ```` 26 | alias node='node --harmony' 27 | ```` 28 | 29 | --- 30 | 31 | ### 应用(Application) 32 | 33 | 一个 Koa Application(以下简称 app)由一系列 generator 中间件组成。按照编码顺序在栈内依次执行,从这个角度来看,Koa app 和其他中间件系统(比如 Ruby Rack 或者 Connect/Express )没有什么太大差别,不过,从另一个层面来看,Koa 提供了一种基于底层中间件编写「语法糖」的设计思路,这让设计中间件变得更简单有趣。 34 | 35 | 在这些中间件中,有负责内容协商(content-negotation)、缓存控制(cache freshness)、反向代理(proxy support)与重定向等等功能的常用中间件(详见 [中间件](#%E4%B8%AD%E9%97%B4%E4%BB%B6middleware) 章节),但如前所述, Koa 内核并不会打包这些中间件,让我们先来看看 Koa 极其简单的 Hello World 应用程序: 36 | 37 | ````javascript 38 | var koa = require('koa'); 39 | var app = koa(); 40 | 41 | app.use(function *(){ 42 | this.body = 'Hello World'; 43 | }); 44 | 45 | app.listen(3000); 46 | ```` 47 | 48 | 如果使用Koa 2的话: 49 | 50 | ````javascript 51 | var Koa = require('koa'); 52 | var app = new Koa(); 53 | 54 | app.use(ctx => { 55 | ctx.body = 'Hello World'; 56 | }); 57 | 58 | app.listen(3000); 59 | ```` 60 | 61 | **译者注:** 与普通的 function 不同,generator functions 以 `function*` 声明,以这种关键词声明的函数支持 `yield`。generator function是ECMAScript 6定义的新的语法,想了解其基本用法,以及Koa如何利用generator function达到在保持js代码异步特性的同时无需编写大量回调函数,可以参考[这篇文章](http://blog.stevensanderson.com/2013/12/21/experiments-with-koa-and-javascript-generators/)。 62 | 63 | --- 64 | 65 | ### 级联代码(Cascading) 66 | 67 | Koa 中间件以一种非常传统的方式级联起来,你可能会非常熟悉这种写法。 68 | 69 | 在以往的 Node 开发中,频繁使用回调不太便于展示复杂的代码逻辑,在 Koa 中,我们可以写出真正具有表现力的中间件。与 Connect 实现中间件的方法相对比,Koa 的做法不是简单的将控制权依次移交给一个又一个的中间件直到程序结束,Koa 执行代码的方式有点像回形针,用户请求通过中间件,遇到 `yield next` 关键字时,会被传递到下一个符合请求的路由(downstream),在 `yield next` 捕获不到下一个中间件时,逆序返回继续执行代码(upstream)。 70 | 71 | 下边这个例子展现了使用这一特殊方法书写的 Hello World 范例:一开始,用户的请求通过 x-response-time 中间件和 logging 中间件,这两个中间件记录了一些请求细节,然后「穿过」 response 中间件一次,最终结束请求,返回 「Hello World」。 72 | 73 | 当程序运行到 `yield next` 时,代码流会暂停执行这个中间件的剩余代码,转而切换到下一个被定义的中间件执行代码,这样切换控制权的方式,被称为 74 | downstream,当没有下一个中间件执行 downstream 的时候,代码将会逆序执行。 75 | 76 | ````javascript 77 | var koa = require('koa'); 78 | var app = koa(); 79 | 80 | // x-response-time 81 | app.use(function *(next){ 82 | // (1) 进入路由 83 | var start = new Date; 84 | yield next; 85 | // (5) 再次进入 x-response-time 中间件,记录2次通过此中间件「穿越」的时间 86 | var ms = new Date - start; 87 | this.set('X-Response-Time', ms + 'ms'); 88 | // (6) 返回 this.body 89 | }); 90 | 91 | // logger 92 | app.use(function *(next){ 93 | // (2) 进入 logger 中间件 94 | var start = new Date; 95 | yield next; 96 | // (4) 再次进入 logger 中间件,记录2次通过此中间件「穿越」的时间 97 | var ms = new Date - start; 98 | console.log('%s %s - %s', this.method, this.url, ms); 99 | }); 100 | 101 | // response 102 | app.use(function *(){ 103 | // (3) 进入 response 中间件,没有捕获到下一个符合条件的中间件,传递到 upstream 104 | this.body = 'Hello World'; 105 | }); 106 | 107 | app.listen(3000); 108 | ```` 109 | 在上方的范例代码中,中间件依次被执行的顺序已经在注释中标记出来。你也可以自己尝试运行一下这个范例,并打印记录下各个环节的输出与耗时。 110 | 111 | **译者注:** 「级联」这个词许多人也许在 CSS 中听说过,如果你不能理解为什么在这里使用这个词,可以将这种路由结构想象成 LESS 的继承嵌套书写方式: 112 | 113 | ```` 114 | .middleware1 { 115 | // (1) do some stuff 116 | .middleware2 { 117 | // (2) do some other stuff 118 | .middleware3 { 119 | // (3) NO next yield ! 120 | // this.body = 'hello world' 121 | } 122 | // (4) do some other stuff later 123 | } 124 | // (5) do some stuff lastest and return 125 | } 126 | ```` 127 | 上方的伪代码中标注了中间件的执行顺序,看起来是不是有点像 ruby 执行代码块(block)时 yield 的表现了?也许这能帮助你更好的理解 koa 运作的方式。 128 | 129 | **译者注:** 更加形象的图可以参考 [Django Middleware](https://docs.djangoproject.com/en/1.6/topics/http/middleware/) 130 | 131 | ![onion.png](https://raw.github.com/fengmk2/koa-guide/master/onion.png) 132 | 133 | --- 134 | 135 | ### 应用配置(Settings) 136 | 137 | 应用的配置是 app 实例的属性。目前来说,Koa 的配置项如下: 138 | 139 | - app.name 应用名称 140 | - app.env 执行环境,默认是 `NODE_ENV` 或者 `"development"` 字符串 141 | - app.proxy 决定了哪些 `proxy header` 参数会被加到信任列表中 142 | - app.subdomainOffset 被忽略的 `.subdomains` 列表,详见下方 api 143 | 144 | --- 145 | 146 | ### 中间件(Middleware) 147 | * [koa-router](https://github.com/alexmingoia/koa-router) 148 | * [trie-router](https://github.com/koajs/trie-router) 149 | * [route](https://github.com/koajs/route) 150 | * [basic-auth](https://github.com/koajs/basic-auth) 151 | * [etag](https://github.com/koajs/etag) 152 | * [compose](https://github.com/koajs/compose) 153 | * [static](https://github.com/koajs/static) 154 | * [static-cache](https://github.com/koajs/static-cache) 155 | * [session](https://github.com/koajs/session) 156 | * [compress](https://github.com/koajs/compress) 157 | * [csrf](https://github.com/koajs/csrf) 158 | * [logger](https://github.com/koajs/logger) 159 | * [mount](https://github.com/koajs/mount) 160 | * [send](https://github.com/koajs/send) 161 | * [error](https://github.com/koajs/error) 162 | 163 | --- 164 | 165 | ### 常用方法 166 | 167 | #### app.listen(...) 168 | 169 | 用于启动一个服务的快捷方法,以下范例代码在 3000 端口启动了一个空服务: 170 | 171 | ````javascript 172 | var koa = require('koa'); 173 | var app = koa(); 174 | 175 | app.listen(3000); 176 | ```` 177 | app.listen 是 http.createServer 的简单包装,它实际上这样运行: 178 | 179 | ````javascript 180 | var http = require('http'); 181 | var koa = require('koa'); 182 | var app = koa(); 183 | 184 | http.createServer(app.callback()).listen(3000); 185 | ```` 186 | 187 | 如果有需要,你可以在多个端口上启动一个 app,比如同时支持 HTTP 和 HTTPS: 188 | 189 | ````javascript 190 | var http = require('http'); 191 | var koa = require('koa'); 192 | var app = koa(); 193 | 194 | http.createServer(app.callback()).listen(3000); 195 | http.createServer(app.callback()).listen(3001); 196 | ```` 197 | 198 | #### app.callback() 199 | 200 | 返回一个可被 `http.createServer()` 接受的程序实例,也可以将这个返回函数挂载在一个 Connect/Express 应用中。 201 | 202 | #### app.use(function) 203 | 204 | 将给定的 function 当做中间件加载到应用中,详见 [中间件](#middleware) 章节 205 | 206 | #### app.keys= 207 | 208 | 设置一个签名 Cookie 的密钥。这些参数会被传递给 [KeyGrip](https://github.com/jed/keygrip) 如果你想自己生成一个实例,也可以这样: 209 | 210 | ````javascript 211 | app.keys = ['im a newer secret', 'i like turtle']; 212 | app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256'); 213 | ```` 214 | 注意,签名密钥只在配置项 `signed` 参数为真时才会生效: 215 | 216 | ````javascript 217 | this.cookies.set('name', 'tobi', { signed: true }); 218 | ```` 219 | 220 | ### 错误处理(Error Handling) 221 | 222 | 除非 `NODE_ENV` 被配置为 `"test"`,Koa 都将会将所有错误信息输出到 `stderr`,也可以自定义「错误事件」来监听 Koa app 中发生的错误,比如记录错误日志: 223 | 224 | ````javascript 225 | app.on('error', function(err){ 226 | log.error('server error', err); 227 | }); 228 | ```` 229 | 230 | 当任何 `req` 或者 `res` 中出现的错误无法被回应到客户端时,Koa 会在第二个参数传入这个错误的上下文: 231 | 232 | ````javascript 233 | app.on('error', function(err, ctx){ 234 | log.error('server error', err, ctx); 235 | }); 236 | ```` 237 | 238 | 任何错误有可能被回应到客户端,比如当没有新数据写入 socket 时,Koa 会默认返回一个 500 错误,并抛出一个 app 级别的错误到日志处理中间件中。 239 | 240 | --- 241 | 242 | ### 应用上下文(Context) 243 | 244 | Koa 的上下文封装了 request 与 response 对象至一个对象中,并提供了一些帮助开发者编写业务逻辑的方法。为了方便,你可以在 `ctx.request` 和 `ctx.response` 中访问到这些方法。 245 | 246 | 每一个请求都会创建一段上下文。在控制业务逻辑的中间件中,上下文被寄存在 `this` 对象中: 247 | 248 | ````javascript 249 | app.use(function *(){ 250 | this; // 上下文对象 251 | this.request; // Request 对象 252 | this.response; // Response 对象 253 | }); 254 | ```` 255 | 为了使用方便,许多上下文属性和方法都被委托代理到他们的 `ctx.request` 或 `ctx.response`,比如访问 `ctx.type` 和 `ctx.length` 将被代理到 `response` 对象,`ctx.path` 和 `ctx.method` 将被代理到 `request` 对象。 256 | 257 | #### Request 对象 258 | 259 | ctx.request 对象包括以下属性和别名方法,详见 [Request](#request) 章节 260 | 261 | - ctx.header 262 | - ctx.method 263 | - ctx.method= 264 | - ctx.url 265 | - ctx.url= 266 | - ctx.path 267 | - ctx.path= 268 | - ctx.query 269 | - ctx.query= 270 | - ctx.querystring 271 | - ctx.querystring= 272 | - ctx.length 273 | - ctx.host 274 | - ctx.fresh 275 | - ctx.stale 276 | - ctx.socket 277 | - ctx.protocol 278 | - ctx.secure 279 | - ctx.ip 280 | - ctx.ips 281 | - ctx.subdomains 282 | - ctx.is() 283 | - ctx.accepts() 284 | - ctx.acceptsEncodings() 285 | - ctx.acceptsCharsets() 286 | - ctx.acceptsLanguages() 287 | - ctx.get() 288 | 289 | #### Response 对象 290 | 291 | ctx.response 对象包括以下属性和别名方法,详见 [Response](#response) 章节 292 | 293 | - ctx.body 294 | - ctx.body= 295 | - ctx.status 296 | - ctx.status= 297 | - ctx.length= 298 | - ctx.type 299 | - ctx.type= 300 | - ctx.headerSent 301 | - ctx.redirect() 302 | - ctx.attachment() 303 | - ctx.set() 304 | - ctx.remove() 305 | - ctx.lastModified= 306 | - ctx.etag= 307 | 308 | #### 上下文对象中的其他 API 309 | 310 | - ctx.req: Node.js 中的 request 对象 311 | - ctx.res: Node.js 中的 response 对象,方法有: 312 | - res.statusCode 313 | - res.writeHead() 314 | - res.write() 315 | - res.end() 316 | - ctx.app: app 实例 317 | - ctx.state: 推荐的命名空间,用来保存那些通过中间件传递给试图的参数或数据。比如 `this.state.user = yield User.find(id);` 318 | - ctx.cookies.get(name, [options]) 对于给定的 name ,返回响应的 cookie 319 | - options 320 | * `signed` [boolean] 321 | - ctx.cookies.set(name, value, [options]) 对于给定的参数,设置一个新 cookie 322 | - options 323 | * `signed` [boolean] 324 | * `expires` [date] 325 | * `path` [string] 默认为 `'/'` 326 | * `domain` [string] 327 | * `secure` [boolean] 328 | * `httpOnly` [boolean] 默认为 `true` 329 | - ctx.throw(msg, [status]) 抛出常规错误的辅助方法,默认 status 为 500。 330 | 331 | 以下几种写法都有效: 332 | 333 | ````javascript 334 | this.throw(403) 335 | this.throw('name required', 400) 336 | this.throw(400, 'name required') 337 | this.throw('something exploded') 338 | ```` 339 | 340 | 实际上,`this.throw('name required', 400)` 是此代码片段的简写方法: 341 | 342 | ````javascript 343 | var err = new Error('name required'); 344 | err.status = 400; 345 | throw err; 346 | ```` 347 | 348 | 需要注意的是,`ctx.throw` 创建的错误,均为用户级别错误(标记为err.expose),会被返回到客户端。 349 | 350 | - ctx.assert(value, [msg], [status], [properties]) 用来断言的辅助方法,类似 Node 中的 `assert()` 方法。`this.assert(this.user, 401, 'User not found. Please login!');` 此方法由 `http-assert` 模块支持。 351 | 352 | --- 353 | 354 | ### Request 355 | 356 | ctx.request 对象是对 Node 原生请求对象的抽象包装,提供了一些非常有用的方法。 357 | 358 | 详细的 Request 对象 API 如下: 359 | 360 | #### req.header 361 | 362 | 返回请求头 363 | 364 | #### req.method 365 | 366 | 返回请求方法 367 | 368 | #### req.method= 369 | 370 | 设置 req.method ,用于实现输入 `methodOverride()` 的中间件 371 | 372 | #### req.length 373 | 374 | 返回 req 对象的 `Content-Length` (Number) 375 | 376 | #### req.url 377 | 378 | 返回请求 url 379 | 380 | #### req.url= 381 | 382 | 设置请求 url,用于进行 url 重写 383 | 384 | #### req.path 385 | 386 | 返回请求 pathname 387 | 388 | #### req.path= 389 | 390 | 设置请求 pathname,如果原有 url 存在查询字符串,则保留这些查询。 391 | 392 | #### req.querystring 393 | 394 | 返回 url 中的查询字符串,去除了头部的 `'?'` 395 | 396 | #### req.querystring= 397 | 398 | 设置查询字符串,不包含 `'?'` 399 | 400 | #### req.search 401 | 402 | 返回 url 中的查询字符串,包含了头部的 `'?'` 403 | 404 | #### req.search= 405 | 406 | 设置查询字符串,包含 `'?'` 407 | 408 | #### req.host 409 | 410 | 返回请求主机名,不包含端口;当 `app.proxy` 设置为 `true` 时,支持 `X-Forwarded-Host`。 411 | 412 | #### req.type 413 | 414 | 返回 req 对象的 `Content-Type`,不包括 `charset` 属性,范例代码: 415 | 416 | ````javascript 417 | var ct = this.type; 418 | // => "image/png" 419 | ```` 420 | 421 | #### req.query 422 | 423 | 返回经过解析的查询字符串,类似 Express 中的 req.query,当不存在查询字符串时,返回空对象。 424 | 425 | 当 url 包含查询字符串 `"color=blue&size=small"` 时,返回如下: 426 | 427 | ````javascript 428 | { 429 | color: 'blue', 430 | size: 'small' 431 | } 432 | ```` 433 | 434 | #### req.query= 435 | 436 | 设置给定的对象为查询对象。范例代码如下: 437 | 438 | ````javascript 439 | this.query = { next: '/login' }; 440 | ```` 441 | 442 | #### req.fresh 443 | 444 | 检查客户端请求的缓存是否是最新。当缓存为最新时,可编写业务逻辑直接返回 `304`,范例代码如下: 445 | 446 | ````javascript 447 | this.set('ETag', '123'); 448 | 449 | // 当客户端缓存是最新时 450 | if (this.fresh) { 451 | this.status = 304; 452 | return; 453 | } 454 | 455 | // 当客户端缓存已过期时,返回最新的数据 456 | this.body = yield db.find('something'); 457 | ```` 458 | 459 | #### req.stale 460 | 461 | 与 req.fresh 返回的结果正好相反 462 | 463 | #### req.protocol 464 | 465 | 返回请求协议名,如 `"https"` 或者 `"http"`;当 `app.proxy` 设置为 `true` 时,支持 `X-Forwarded-Proto`。 466 | 467 | #### req.secure 468 | 469 | 判断请求协议是否为 HTTPS 的快捷方法,等同于 `this.protocol == "https"` 470 | 471 | #### req.ip 472 | 473 | 返回请求IP;当 `app.proxy` 设置为 `true` 时,支持 `X-Forwarded-For`。 474 | 475 | #### req.ips 476 | 477 | 返回请求IP列表,仅当 `app.proxy` 设置为 `true` ,并存在 `X-Forwarded-For` 列表时,否则返回空数组。 478 | 479 | #### req.subdomains 480 | 481 | 返回请求对象中的子域名数组。子域名数组会自动由请求域名字符串中的 `.` 分割开,在没有设置自定义的 `app.subdomainOffset` 参数时,默认返回根域名之前的所有子域名数组。 482 | 483 | 例如,当请求域名为 `"tobi.ferrets.example.com"` 时候,返回 `["ferrets", "tobi"]`,数组顺序是子代域名在前,孙代域名在后。 484 | 485 | 此例中,如果设置了自定义的 `app.subdomainOffset` 为 `3`,将忽略三级域名,返回 `["tobi"]`。 486 | 487 | #### req.is(type) 488 | 489 | 判断请求对象中 `Content-Type` 是否为给定 type 的快捷方法,如果不存在 `request.body`,将返回 `undefined`,如果没有符合的类型,返回 `false`,除此之外,返回匹配的类型字符串。 490 | 491 | ````javascript 492 | // 客户端 Content-Type: text/html; charset=utf-8 493 | this.is('html'); // => 'html' 494 | this.is('text/html'); // => 'text/html' 495 | this.is('text/*', 'text/html'); // => 'text/html' 496 | 497 | // 客户端 Content-Type 为 application/json 时: 498 | this.is('json', 'urlencoded'); // => 'json' 499 | this.is('application/json'); // => 'application/json' 500 | this.is('html', 'application/*'); // => 'application/json' 501 | 502 | this.is('html'); // => false 503 | ```` 504 | 505 | 又如,下方的代码使用 `req.is(type)`,仅当请求类型为图片时才进行操作: 506 | 507 | ````javascript 508 | if (this.is('image/*')) { 509 | // process 510 | } else { 511 | this.throw(415, 'images only!'); 512 | } 513 | ```` 514 | 515 | #### req.accepts(type) 516 | 517 | 判断请求对象中 `Accept` 是否为给定 type 的快捷方法,当匹配到符合的类型时,返回最匹配的类型,否则返回 `false`(此时服务器端应当返回 406 "Not Acceptable" ),传入参数可以是字符串或者数组。 518 | 519 | ````javascript 520 | // Accept: text/html 521 | this.accepts('html'); 522 | // => "html" 523 | 524 | // Accept: text/*, application/json 525 | this.accepts('html'); 526 | // => "html" 527 | this.accepts('text/html'); 528 | // => "text/html" 529 | this.accepts('json', 'text'); 530 | // => "json" 531 | this.accepts('application/json'); 532 | // => "application/json" 533 | 534 | // Accept: text/*, application/json 535 | this.accepts('image/png'); 536 | this.accepts('png'); 537 | // => undefined 538 | 539 | // Accept: text/*;q=.5, application/json 540 | this.accepts(['html', 'json']); 541 | this.accepts('html', 'json'); 542 | // => "json" 543 | ```` 544 | 545 | 注意,当请求头中不包含 Accept 属性时,给定的第一个 type 将会被返回。 546 | 547 | #### req.acceptsEncodings(encodings) 548 | 549 | 判断客户端是否接受给定的编码方式的快捷方法,当有传入参数时,返回最应当返回的一种编码方式。 550 | 551 | ````javascript 552 | // Accept-Encoding: gzip 553 | this.acceptsEncodings('gzip', 'deflate'); 554 | // => "gzip" 555 | 556 | this.acceptsEncodings(['gzip', 'deflate']); 557 | // => "gzip" 558 | ```` 559 | 560 | 当没有传入参数时,返回客户端的请求数组: 561 | 562 | ````javascript 563 | // Accept-Encoding: gzip, deflate 564 | this.acceptsEncodings(); 565 | // => ["gzip", "deflate"] 566 | ```` 567 | 568 | #### req.acceptsCharsets(charsets) 569 | 570 | 使用方法同 req.acceptsEncodings(encodings) 571 | 572 | #### req.acceptsLanguages(langs) 573 | 574 | 使用方法同 req.acceptsEncodings(encodings) 575 | 576 | --- 577 | 578 | ### Response 579 | 580 | 详细的 Response 对象 API 如下: 581 | 582 | #### res.header 583 | 584 | 获取返回头 585 | 586 | #### res.status 587 | 588 | 获取返回状态 589 | 590 | #### res.status= 591 | 592 | 设置返回状态,可用状态如下: 593 | 594 | - 100 "continue" 595 | - 101 "switching protocols" 596 | - 102 "processing" 597 | - 200 "ok" 598 | - 201 "created" 599 | - 202 "accepted" 600 | - 203 "non-authoritative information" 601 | - 204 "no content" 602 | - 205 "reset content" 603 | - 206 "partial content" 604 | - 207 "multi-status" 605 | - 300 "multiple choices" 606 | - 301 "moved permanently" 607 | - 302 "moved temporarily" 608 | - 303 "see other" 609 | - 304 "not modified" 610 | - 305 "use proxy" 611 | - 307 "temporary redirect" 612 | - 400 "bad request" 613 | - 401 "unauthorized" 614 | - 402 "payment required" 615 | - 403 "forbidden" 616 | - 404 "not found" 617 | - 405 "method not allowed" 618 | - 406 "not acceptable" 619 | - 407 "proxy authentication required" 620 | - 408 "request time-out" 621 | - 409 "conflict" 622 | - 410 "gone" 623 | - 411 "length required" 624 | - 412 "precondition failed" 625 | - 413 "request entity too large" 626 | - 414 "request-uri too large" 627 | - 415 "unsupported media type" 628 | - 416 "requested range not satisfiable" 629 | - 417 "expectation failed" 630 | - 418 "i'm a teapot" 631 | - 422 "unprocessable entity" 632 | - 423 "locked" 633 | - 424 "failed dependency" 634 | - 425 "unordered collection" 635 | - 426 "upgrade required" 636 | - 428 "precondition required" 637 | - 429 "too many requests" 638 | - 431 "request header fields too large" 639 | - 500 "internal server error" 640 | - 501 "not implemented" 641 | - 502 "bad gateway" 642 | - 503 "service unavailable" 643 | - 504 "gateway time-out" 644 | - 505 "http version not supported" 645 | - 506 "variant also negotiates" 646 | - 507 "insufficient storage" 647 | - 509 "bandwidth limit exceeded" 648 | - 510 "not extended" 649 | - 511 "network authentication required" 650 | 651 | #### res.length= 652 | 653 | 设置返回头的 `Content-Length` 属性 654 | 655 | #### res.length 656 | 657 | 返回返回头的 `Content-Length` 属性,当不存在 `Content-Length` 属性时,根据 `res.body` 推断 658 | 659 | #### res.body 660 | 661 | 获取 res.body,当 res.body 为 null ,但返回状态仍为 200 时,koa 将会返回 404 页面。 662 | 663 | #### res.body= 664 | 665 | 设置请求返回的主要内容,可以是以下几种类型: 666 | 667 | - string 668 | 669 | Content-Type 将默认设置为 text/html 或者 text/plain,默认字符集是 utf-8,Content-Length 也将一并设置 670 | 671 | - Buffer 672 | 673 | Content-Type 将默认设置为 application/octet-stream,Content-Length 也将一并设置 674 | 675 | - Stream 676 | 677 | Content-Type 将默认设置为 application/octet-stream 678 | 679 | - Object 680 | 681 | Content-Type 将默认设置为 application/json 682 | 注意:默认的json返回会添加空格,如果你希望压缩json返回中的空格,可以这样配置:`app.jsonSpaces = 0` 683 | 684 | - null 685 | 686 | #### res.get(field) 687 | 688 | 获取指定的返回头属性,属性名称区分大小写。 689 | 690 | ````javascript 691 | var etag = this.get('ETag'); 692 | ```` 693 | 694 | #### res.set(field, value) 695 | 696 | 使用给定的参数设置一个返回头属性: 697 | 698 | ````javascript 699 | this.set('Cache-Control', 'no-cache'); 700 | ```` 701 | 702 | #### res.set(fields) 703 | 704 | 使用给定的对象一次设置多个返回头属性: 705 | 706 | ````javascript 707 | this.set({ 708 | 'Etag': '1234', 709 | 'Last-Modified': date 710 | }); 711 | ```` 712 | 713 | #### res.remove(fields) 714 | 715 | 删除指定的返回头属性 716 | 717 | #### res.type 718 | 719 | 获取返回头中的 Content-Type,不包括 `"charset"` 等属性。 720 | 721 | ````javascript 722 | var ct = this.type; 723 | // => "image/png" 724 | ```` 725 | 726 | #### res.type= 727 | 728 | 使用字符串或者文件后缀设定返回的 Content-Type 729 | 730 | ````javascript 731 | this.type = 'text/plain; charset=utf-8'; 732 | this.type = 'image/png'; 733 | this.type = '.png'; 734 | this.type = 'png'; 735 | ```` 736 | 737 | 注意:当使用文件后缀指定时,koa 会默认设置好最匹配的编码字符集,比如当设定 `res.type = 'html'` 时,koa 会默认使用 `"utf-8"` 字符集。但当明确使用 `res.type = 'text/html'` 指定时,koa 不会自动设定字符集。 738 | 739 | #### res.redirect(url, [alt]) 740 | 741 | 返回一个 `302` 跳转到给定的 url,您也可以使用关键词 `back` 来跳转到该 url 的上一个页面(refer),当没有上一个页面时,默认会跳转到 '/' 742 | 743 | ````javascript 744 | this.redirect('back'); 745 | this.redirect('back', '/index.html'); 746 | this.redirect('/login'); 747 | this.redirect('http://google.com'); 748 | ```` 749 | 如果你需要覆盖 `302` 状态码,并在跳转时返回一些文案,可以这样做: 750 | 751 | ````javascript 752 | this.status = 301; 753 | this.redirect('/cart'); 754 | this.body = 'Redirecting to shopping cart'; 755 | ```` 756 | 757 | #### res.attachment([filename]) 758 | 759 | 设置返回熟悉 Content-Disposition 为 `"attachment"`,并告知客户端进行下载。 760 | 761 | #### res.headerSent 762 | 763 | 判断一个响应头是否已经发送到客户端,通常用来检测客户端是否收到了错误信息。 764 | 765 | #### res.lastModified 766 | 767 | 如果返回头中存在 Last-Modified 属性,则返回它。 768 | 769 | #### res.lastModified= 770 | 771 | 设置返回头中的 Last-Modified 属性,可以使用时间对象或者时间字符串。 772 | 773 | ````javascript 774 | this.response.lastModified = new Date(); 775 | ```` 776 | 777 | #### res.etag= 778 | 779 | 设置返回头的 Etag 字段。koa 不提供关于 Etag 的获取方法。 780 | 781 | ````javascript 782 | this.response.etag = crypto.createHash('md5').update(this.body).digest('hex'); 783 | ```` 784 | 785 | --- 786 | 787 | ### 性能(Benchmarks) 788 | 789 | 挂载不同数量的中间件,wrk 得出 benchmarks 如下: 790 | 791 | ```` 792 | 1 middleware 793 | 8367.03 794 | 795 | 5 middleware 796 | 8074.10 797 | 798 | 10 middleware 799 | 7526.55 800 | 801 | 15 middleware 802 | 7399.92 803 | 804 | 20 middleware 805 | 7055.33 806 | 807 | 30 middleware 808 | 6460.17 809 | 810 | 50 middleware 811 | 5671.98 812 | 813 | 100 middleware 814 | 4349.37 815 | ```` 816 | 一般来说,我们通常要使用约50个中间件,按这个标准计算,单应用可支持 340,260 请求/分钟,即 20,415,600 请求/小时,也就是约 4.4 亿 请求/天。 817 | 818 | --- 819 | 820 | ### 学习资料 821 | 822 | 发现更多第三方的 koa 中间件,或者一起来参与社区的讨论和建设吧: 823 | 824 | - [GitHub repository](https://github.com/koajs/koa) 825 | - [Examples](https://github.com/koajs/examples) 826 | - [Middleware](https://github.com/koajs/koa/wiki) 827 | - [Wiki](https://github.com/koajs/koa/wiki) 828 | - [G+ Community](https://plus.google.com/communities/101845768320796750641) 829 | - [Mailing list](https://groups.google.com/forum/#!forum/koajs) 830 | - [Guide](https://github.com/koajs/koa/blob/master/docs/guide.md) 831 | - [FAQ](https://github.com/koajs/koa/blob/master/docs/faq.md) 832 | 833 | --- 834 | 835 | ### Contributing 836 | - Fork this repo 837 | - Clone your repo 838 | - Install dependencies 839 | - Checkout a feature branch 840 | - Feel free to add your features 841 | - Make sure your features are fully tested 842 | - Open a pull request, and enjoy <3 843 | 844 | ### MIT license 845 | Copyright (c) 2013 turing <o.u.turing@gmail.com> 846 | 847 | Permission is hereby granted, free of charge, to any person obtaining a copy 848 | of this software and associated documentation files (the "Software"), to deal 849 | in the Software without restriction, including without limitation the rights 850 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 851 | copies of the Software, and to permit persons to whom the Software is 852 | furnished to do so, subject to the following conditions: 853 | 854 | The above copyright notice and this permission notice shall be included in 855 | all copies or substantial portions of the Software. 856 | 857 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 858 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 859 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 860 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 861 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 862 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 863 | THE SOFTWARE. 864 | 865 | --- 866 | ![docor](https://cdn1.iconfinder.com/data/icons/windows8_icons_iconpharm/26/doctor.png) 867 | 868 | Generated using [docor](https://github.com/guo-yu/docor.git) @ 0.1.0. brought to you by [Guo Yu](https://github.com/guo-yu) 869 | -------------------------------------------------------------------------------- /onion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/17koa/koa-guide/99601f3bdc75ed345e60a3a98284afeaa54a1c4d/onion.png --------------------------------------------------------------------------------