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