Koa
次世代nodejs 的 web框架
简介
koa是由Express幕后团队打造的,目标是更小,更快,更稳固的web运用和apis。经由过程杠杆生成器(leveraging generators)Koa可以让你指导(ditch)回调函数,极大的提拔毛病处置惩罚。Koa中心不集成任何的中间件,其自身供应的文雅的功用套件就可以写出既快又nice的效劳器。
装置
Koa须要node7.6.0或更高的版本,由于须要async function
支撑。
你可以运用本身的版本管理器很快的装置一个支撑的版本。
nvm install 7
npm i koa
node my-koa-app.js
Async Function 连系 Babel
想要在较低版本的node中运用async
函数,我们发起运用babel。
require('babel-core/register')
//然后在加载运用的主代码,这个必需在babel背面
const app = require('./app')
为了编译和转化async functions
你须要在末了的紧缩版本中运用’transform-async-to-generator’或许transform-async-to-module-method
插件。比方,在你的.babelrc
文件中,举行以下设置。
{
"plugins":["transform-async-to-generator"]
}
你也可以运用stage-3 persent
来替换。
运用 Application
一个Koa运用是一个对象,其包含一个数组,数组有很多函数构成的中间件,这些函数鸠合起来守候请求,而且实行时是根据类栈的体式格局。koa和很多其他中间件体系相似,你或许是用过Ruby
的Rack
,Connect
等。然则一个设想的决议行要素是供应高品级”sugar”,与此同时低品级中间件层。因而提拔了交互性,鲁棒性(软件设想术语,即稳固性)而且使得编写中间件越发的带劲!
这包含一些经常使用使命的要领——比方链接谐和,缓存,代办支撑,别用等。只管供应了大批的有效的要领,然则koa依然坚持了一个较小的体积,由于没有绑定中间件。
怎么能偶少得了一个hello world
运用。
const Koa = require('koa');
const app = new Koa();
app.use(ctx => {
ctx.body = 'hello world';
})
app.listen(3000)
串连 Cascading
koa 的串连中间件运用了一个比较传统的体式格局,跟你通经常使用的东西很像。这是由于原本很难让用户有好的运用node的回调函数。然则运用异步函数我们能偶“真正得”是有中间件。相较于衔接的完成,这个更轻易供应一些列函数/功用来掌握,在末了返回便可。koa挪用”downstream”,掌握流返回”upstream”.
下面的例子返回“hello world”,然则最最先请求先经由过程x-response-time
和logging
中间件来纪录请求的最先。然后经由过程返回的中间件产出掌握。当一个中间件实行next()
函数,来耽误和通报掌握给下一个声明的中间件。然后直到没有中间件须要实行downstream
了,栈将会松开然后每一个中间件回复去展示本身的“upstream”行动。
设置 Settings
运用设置即在实例app上的属性。当前支撑以下:
app.env 默许是NODE_ENV或许“development”。
app.proxy 当设置为true时,porxy头部将被信托。
app.subdomainOffset 设置
.subdomains
的偏移量。替换[2]。app.listen(…)
一个Koa运用不是一对一的显现一个htpp效劳器。一个或很多个运用或许被增加到一块形成大的运用对应一个http效劳器。
建立返回一个http效劳器,通报给定的参数到Server#listen()
。这些参数在nodejs.org都有申明。下面是一个无意义的Koa运用,绑定了端口3000
app.listen(…)要领是以下的一个语法糖。
const http = require('http')
const Koa = require('koa')
const app = new Koa()
http.createServer(app.callback()).listen(3000)
这申明你可以定义同一个运用为https和http或很多个地点。
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
http.createServer(app.callback()).listen(3001);
app.callback()
返回一个回调函数,相当于http.createServer()
要领,来出了请求。
你也可以运用这个要领在你的Connect/Express运用中增加koa运用。
app.use(function)
增加一个给定的中间件要领来完成它的功用。检察Middleware相识更多。
app.keys=
设置cookie的键。
这些键被通报到KeyGrip,或许你想运用本身的KeyGrip
,可以以下做。
app.keys = ['im a newer secret', 'i like turtle'];
app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
这些键或许是轮回的,而且可以设置{signed:true}
来运用。
ctx.cookies.set('name','tobi',{signed:true})
app.context
app.context是ctx的泉源。你可以运用app.context
增加分外的属性到ctx。这关于建立逾越悉数app运用的属性或许要领来讲是有效的,而且机能更好,在依靠上也跟简朴,可以斟酌做一个anti-pattern
。
例子,从ctx增加一个数据库援用。
add.context.db = db()
app.use(async (ctx)=>{
console.log(ctx.db)
})
注重:
经由过程getter和setter以及Object.difineProperty()设置的属性,你只能在app.context运用Object.defineProperty()来编辑他们。(不引荐)
运用父级的
ctx
和设置来增加当前的运用。如许增加的app就可以运用到那些中间件。
毛病处置惩罚 Error Handling
除非设置app.silent是true,不然一切的出无输出都是规范输出。默许的毛病输出不会处置惩罚像是err.sttus是404或许err.expose是true。为了自定义毛病输出比方日记,你可以增加毛病事宜监听。
app.on('error', err =>
log.error('server error', err)
);
当 req/res 周期中涌现任何毛病且没法响应客户端时,Koa 会把 Context(上下文) 实例作为第二个参数通报给 error 事宜:
app.on('error', (err, ctx) =>
log.error('server error', err, ctx)
);
假如有毛病发作, 而且还能响应客户端(即没有数据被写入到 socket), Koa 会返回 500 “Internal Server Error”. 这两种状况都邑触发 app-level 的 error 事宜, 用于 logging.
环境(Context)
一个Koa环境(实例)封装了node原生的要乞降返回对象到一个零丁的对象中,这个零丁的对象供应了很多运用的要领,可以编写web运用和API。这些HTTP效劳器开辟中经经常使用到的操纵被增加到当前品级,而不是高品级。他将强迫中间件从新完成这些经常使用的功用。
一个环境Context
在每次请求时被建立,而且被援用至中间件作为接收器,或许定义成this
。以下所示。
app.use(function *(){
this;//这里是koa环境context
this.request;//是一个koa请求
this.response;//是一个koa返回
})
很多context
环境接见器和要领只是ctx.request
koa请求或许ctx.response
koa返回的代办,主要是为了轻易。比方ctx.type
和ctx.length
代表response
返回对象,ctx.paht
和ctx.methos
代表请求。
API 接口。
环境(Context)定义的要领和接见器。
ctx.req Node的
request
对象。ctx.res Node的
response
对象。
绕开运用koa的response处置惩罚是不支撑的。防止运用下面的node属性。res.statusCode
res.writeHead()
res.write()
res.end()
ctx.request 一个Koa的
request
对象。ctx.response 一个Koa的
response
对象。ctx.state
引荐的经由过程中间件通报信息给前端view(显现)的定名空间。ctx.app 运用实例的援用。
ctx.cookies.get(name,[options])
经由过程options
取得cookie
名字。signed 请求cookie已署名。
koa运用cookie模块,这里只是传入选项即可。
ctx.coolies.set(name,value,[options])
运用options
设置name
的值value
。signed 署名cookie的值。
expires 使cookie的有效期到期。
path cookie途径,默许
/
。domain cookie域
secure 庇护coolie
httpOnly 效劳器端cookie,默许值
true
。
经由过程设置options
来运用cookie模块。
ctx.throw([msg],[status],[properties])
处置惩罚抛出毛病的辅佐要领,默许.status
的值为500时抛出,koa在返回的信息中恰当处置惩罚。限免的组合也是可疑的。this.throw(403);
this.throw(‘name require’, 400);
this.throw(400,’name require’);
this.throw(‘something exploded’);
比方:this.throw('name require', 400)
即是
var err = new Error('name require');
err.status = 400;
throw err;
注重这些是用户自定义的毛病,运用err.expose
发出。因而只适宜某些客户端的反应。这些毛病不同于内置的毛病信息提示,由于毛病的详细信息不会泄漏。
你或许通报一个properties
选项对象,他和原本的毛病信息举行了整合,关于人性化体验很有协助,它报告个给请求者一个回溯流(upsteam
)。
this.throw(401,'access_denied',{user:user});
this.throw('access_denied',{user:user});
koa运用http-errors
来建立毛病。
ctx.assert(value,[msg],[status],[properties])
跑出毛病辅佐要领,相似`.throw()`,当`!value`是相似node的`assert()`要领。
this.assert(this.sate.user,401,'User not found, Please login!');
koa运用http-assert
完成断言(assertions
)
ctx.response
经由过程绕开koa内置的返回处置惩罚(response handling),你可以明白的设置this.response = false;
假如你想运用原生的res对象处置惩罚而不是koa的response处置惩罚,那末就运用它。
注重那种用法koa不支撑。这或许会打断koa中间件原本的功用,或许koa也被打断。运用这个属性最好斟酌一下hack,这是运用传统的fn(req,res)
要领和koa中间件的唯一轻易的要领。
请求别号Request aliases
下面的接见起和Request别号相称。
ctx.header
ctx.headers
ctx.method
ctx.method=
ctx.url
ctx.url=
ctx.originalUrl
ctx.origin
ctx.href
ctx.path
ctx.query
ctx.query=
ctx.querystring
ctx.querystring=
ctx.host
ctx.hostname
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 aliases
下面的接见起和返回别号相称
ctx.body
ctx.body=
ctx.status
ctx.status=
ctx.message
ctx.message=
ctx.length=
ctx.length
ctx.type
ctx.type=
ctx.handerSent
ctx.redirect()
ctx.attachment()
ctx.set()
ctx.append()
ctx.remove()
ctx.lastModified=
ctx.etag=
请求 Request
一个koa请求Request对象是个建立在node请求request之上的笼统。供应了一些分外的功用,这对每一个http效劳器开辟者来讲异常有效。
API
request.header
Request header 对象
request.headers
Requests header 对象,别号`request.header`。
request.method
request.method
request.method=
设置request method,完成中间件很有效,比方`methodoverride()`。
request.length
返回request Content-lenght值是数字或许undefined。
request.url
返回rquest URL
request.url=
设置rquest URL,重写url时有效。
request.originalUrl
返回request 原始 URL
request.orgin
获得URL的域,包含协媾和host(主机号)。
this.request.origin
//=>http://example.com
request.href
返回悉数request URL,包含协定,主机号,和url。
this.request.href
//=>http://example.com/foo/bar?q=1
request.path
返回途径名(pathname)。
request.path=
设置请求途径名字,保留查询参数
rquest.querystring
获得原始的查询参数,不包含`?`。
request.querystring=
设置原始的查询参数。
request.search
获得原始的查询字符,带`?`。
request.search=
设置原始的查询字符。
rquest.host
获得主机号(hostname:port)当显现时。支撑`X-Forwarded-Host`当`app.proxy`是true,不然是经常使用的`host`。
request.hostname
当偶然返回hostname,支撑`X-Frowarded-Host`当`app.proxy`是true,不然是经常使用的。
request.type
返回request的`Content-type`,无效的一些参数,如`charset`。
var ct = this.request.type.
//=>'image/png'
request.charset
当偶然返回request的charset,或许`undefined`。
this.request.charset
//=>'utf-8'
request.query
返回剖析过的查询字符query-string,假如没有则返回一个空对象。注重,这个getter不支撑嵌套的剖析nested parsing。比方:`color=blue&size=small`。
{
color:'blue',
size:'small'
}
request.query=
设置查询字符query-string到给定的对象。注重给设置setter不支撑嵌套对象。
this.query = {next:'/login'};
request.fresh
搜检请求缓存是不是“革新fresh”,或许内容是不是发作转变。这个要领是为了`if-None-Match`和`if-Modified-Since`以及`last-modified`之间的缓存沟通。他必需可以援用到变动以后的返转头部response headers
//freshness check requeire stats 20x or 304
this.status = 200;
this.set('ETag','123');
//cache is ok
if(this.fresh) {
this.status = 304;
return;
}
//cache is stale
//fetch new data
shis.body = yield db.find('something');
request.stale
与`request.fresh`相反
request.protocol
返回请求协定,`https`或许`http`。支撑`X-Forwarded-Proto`当`app.proxy`是true。
request.secure
`this.protocol == "https"`的速记,用以搜检一个讨情可否经由过程平安传输层。
request.ip
请求的长途地点。支撑`X-Forwarded-For`当`app.proxy`为true。
request.ips
当有`X-Forwarded-For`而且`app.proxy`可用,那末返回这些的ip的一个数组。 从回溯upstream——>downstream预定,当上述不可用则返回一个空数组。
request.subdomains
返回子域数组。 子域是在主域之前的部份,由点`.`离开。默许状况下,运用的主域都被假设成倒数的那两个。可以经由过程`app.subdomainOffset`来转变。 比方,假如域是`tobi.ferrest.example.com`,而且`app.subdomainOffset`没有设置,那末这个子域是['ferrets','tobi']。假如设置`app.subdomainOffset`为3,那末子域是['tobi']。
request.is(type…)
搜检接下来的请求是不是包含`Content-Type`头部内容,它包含任何的mime范例。假如这里没有请求体,返回undefined。假如没有内容范例,或许婚配失利,返回false。其他的直接返回内容范例(mime)。
//Contetn-type:text/html;charset=utf-8
this.is('html');//=>'html'
this.is('text/html');//=>'text/html'
this.is('text/*', 'test/html');//=>'test/html'
//when Content-type is application/json
this.is('json','urlencoded');//=>'json'
this.is('application/json',);//=>'application/json'
this.is('html','application/*',);//=>'application/json'
this.is('html');//=>false
例子:你只想只要图片可以发送到路由
if(this.is('image/*')) {
//process
}else{
this.throw(415,'image only!');
}
内容协商 Content Negotiation
koa请求request包含有效的内容写上东西,由accepts
和negotaitor
支撑完成,这些东西是:request accepts(types)
rquest acceptsEncoding(types)
rquest acceptsCharsets(charsets)
rquest acceptsLanguages(langs)
假如没有供应范例,那末一切可接收的范例将被返回。
假如供应了多个范例,最优婚配奖杯返回。假如没有婚配到,返回false,而且你应当发送406 "Not Acceptable"
返回response给客户端。
在可以接收任何范例的处所丧失了accept头部。第一个婚配到的将被返回。因而供应科技收的范例是很主要的。
request.accepts(types)
搜检给定的范例是不是是可接收的。当为true则返回最好婚配,不然false。范例`type`的值或许是一个或很多个mime范例字符,比方'application/json',扩大名是'josn',或许一个数组`['josn','html','text/plain']`。
//Accept:text/html
this.accepts('html')
//=>'html'
//Accept:text/*, application/json
this.accepts('html')
//=>'html'
this.accepts('json', 'text')
//=>'json'
this.accepts('application/json')
//=>'application/json'
//Accept.text/*, application/json
this.accepts('image/png')
this.accepts('png')
//=>false
//Accept:text/*,q=.5, application/json
this.accepts(['html', 'json'])
this.accepts('html', 'json')
//=>json
//No Accepts header
this.accpts('html', 'json')
//=>html
this.accepts('json','html')
//=> json
你或许挪用this.accepts()
很屡次,或许运用switch语句。
switch(this.accepts('json', 'html', 'text')) {
case 'json': bareak;
case 'html': bareak;
case 'text': bareak;
default: this.throw(406, 'json , html or text only');
}
request.acceptsEncodings(encodings)
搜检编码`encodings`是不是可接收,true时返回最优婚配,不然返回false。 注重,你应当包含一个`indentity`作为编码`encodings`之一。
//Accept-Encoding:gzip
this.acceptsEncodings('gzip', 'deflate', 'identify');
//=>gzip
this.acceptsEncodings(['gzip', 'deflate', 'identify'])
//=>gzip
当没有参数时,一切可接收的编码作为数组元素返回
//Accept-Encoding:gzip, deflate
this.acceptsEncodings();
//=>['gzip','deflate','identify']
注重假如用户明白发送identify为identify,q=0
。虽然这是个特别例子,你依然须要处置惩罚这个状况,当要领返回false时。
request.acceptsCharsets(charsets)
搜检charset是不是可接收,为true时返回最优婚配,不然返回false。
//Accept-Charset:utf-8, iso-8859-1;q=0.2,utf-7;q=0.5
this.acceptsCharsets(‘utf-8′,’utf-7’)
//=>utf-8
this.acceptsCharsets([‘utf-7′,’utf-8’]);
//=>utf-8
假如没有参数是则返回一切可接收的编码到一个数组。
//Accept-Charset:utf-8,iso-8859-1;q=0.2,utf-7;q=0.5
this.acceptsCharsets();
//=>['utf-8','utf-7','iso-8859-7']
request.acceptLanguages(langs)
搜检langs是不是可接收,假如为true则返回最有婚配,不然返回false。
//Accept-Language:en;q=0.8,es,pt
this.acceptsLanguages('es','en');
//=>'es'
this.acceptsLanguages(['en','es']);
//=>'es'
当没有传入参数则返回一切的言语。
//Accept-Language:en;q=0.8, es,pt
this.acceptsLanguages();
//=>['es', 'pt', 'en']
request.idempotent
价差请求是不是idempotent(幂等)
request.socket
返回请求的socket
request.get(field)
返回请求头header
返回 Response
一个koa返回Response对象是个建立在node请求request之上的笼统。供应了一些分外的功用,这对每一个http效劳器开辟者来讲异常有效。
API
response.header
返回header对象response。headers
返回header对象。response.header的别号response.status
返回response的状况,默许状况下response.status
没有默许值,而res.statusCode
的默许值是200。response.status =
经由过程数字设置状况值100 ‘continue’继承
101 ‘switch protocols’换协定
102 ‘processing’处置惩罚中
200 ‘ok’ ok
201 ‘created’已建立
202 ‘accepted’ 已接收
203 ‘non-authoritative information’无作者信息
204 ‘no content’ 无内容
205 ‘reset content’ 重置内容
206 “partial content” 部份内容
207 “multi-status” 多状况
300 “multiple choices” 多挑选
301 “moved permanently” 移动到永远
302 “moved temporarily” 移动到临时
303 “see other” 看其他
304 “not modified” 没有修正
305 “use proxy” 运用代办
307 “temporary redirect” 临时改向
400 “bad request” 坏请求
401 “unauthorized” 未经受权
402 “payment required” 请求付款
403 “forbidden” 制止
404 “not found” 没有发明
405 “method not allowed” 要领不允许
406 “not acceptable” 不接收
407 “proxy authentication required” 请求代办受权
408 “request time-out” 请求超时
409 “conflict” 争执
410 “gone” 消逝
411 “length required” 请求长度
412 “precondition failed” 预处置惩罚失利
413 “request entity too large” 请求量太大
414 “request-uri too large” 请求赞同资本太大
415 “unsupported media type” 不支撑的媒体范例
416 “requested range not satisfiable” 不满足请求局限
417 “expectation failed” 不是期望值
418 “i’m a teapot” 我是个茶壶???
422 “unprocessable entity” 毛病实体
423 “locked” 已锁定
424 “failed dependency” 依靠毛病
425 “unordered collection” 未预定鸠合
426 “upgrade required” 请求更新
428 “precondition required” 请求条件
429 “too many requests” 过量请求
431 “request header fields too large” 请求头的域太大
500 “internal server error” 效劳器内部毛病
501 “not implemented” 没有完成
502 “bad gateway” 网关毛病
503 “service unavailable” 不可效劳
504 “gateway time-out” 网关超时
505 “http version not supported” http版本不支撑
506 “variant also negotiates” 多样协商
507 “insufficient storage” 存储不足
509 “bandwidth limit exceeded” 凌驾带宽
510 “not extended” 扩大毛病
511 “network authentication required” 请求网路受权证实
注重:不要担心要记太多东西,你可以随时检察。
response.message
获得返回状况的信息。默许状况下,response.message
是和response.status
婚配的。response.message=
设置返回状况信息。response.length=
设置内容的长度response.length
返回内容的长度,或许计算出的this.body
的大小。值为数字。或许undifinedresponse.body
获得response的body。response.body=
设置返回体(response.body)为以下之一:String written
Buffer written
Stream piped
Object json-stringified
null no content response
String
Content-type是text/html或许text/plain,charset是utf-8.Content-length也须要设置。
Buffer
Content-type是application/octet-stream,Content-length也要设置。
Stream
Content-type是application/octet-stream.
Object
Content-type是application/json.response.get(field)
获得response头部的field的值,不辨别大小写。
var etag = this.get('ETag');
response.set(field, value)
设置response头部field的值。
this.set('Cache-control', 'no-cache');
response.append(field, value)
给头部增加额为的域和值。
this.append('Link', '<http://127.0.0.1/>');
response.set(fields)
运用对象设置头部的fields
this.set({
'Etag':'1234',
'Last-modified':date
});
response.remove(field)
移除头部的某个域。
resposne.type
返回Content-type的范例,没有其他参数——如‘charset’。
var ct = this.type;
//=>image/png
response.type
经由过程名字或许扩大名设置Content-type
this.type = 'text/plain;charset=utf-8';
this.type = 'image/png';
this.type='.png';
this.type='png';
注重,每一个字符编码charset都是为你选的最适宜的,比方response.type='html'
,那末默许的字符编码是utf-8
,然则明白定义一个完全的范例,如response.type='text/html'
,将不会有指定的字符编码。
response.is(type…)
很相似于`this.request.is()`。搜检response的范例是不是是被支撑的范例。这在建立那些对返回举行操纵的中间件是异常有效。 示例:这是一个紧缩html返回response的中间件,除了stream不被紧缩。
var minify = require('html-minifier');
app.use(function *minifyHtml(next){
yield next;
if(!this.response.is('html')) return;
var body = this.body;
if(!body||body.pipe) return;
if(Buffer.isBuffer(body)) body = body.toString();
this.body = minify(body);
})
response.redirect(url, [alt])
把[302]状况码重导向至`url`。 字符串`back`是一个特别的例子,供应了援用这支撑,当援用者不存在或许`/`没有运用。
this.redirect('back');
this.redirect('back','/index.html');
this.redirect('login');
this.redirect('http://google.com');
为了转变默许的状况302,只需在这个状况吗涌现之前或许涌现以后举行重导向即可。为了转变body,在其挪用以后举行重定向。
this.status = 301;
this.redirect('/cart');
this.body = 'Redirecting to shopping cart';
response.attachment([filename])
设置`Content-disposition`为"attachment"为客户端发出下载的信号。 文件的名字是可以指定的。
response.headerSent
搜检返转头response header是不是早已发送。检察客户端是不是关照毛病信号异常有效。
response.lastModified
返回`Last-Modified`末了修正头部的数据(假如存在)。
response.LastModified=
设置`Last-Modified`头部为一个适宜的UTC(国际规范时间)字符串。你也可以设置其为一个日期或许日期字符串。
this.response.lastModified = new Date();
response.etag=
设置ETag到一个返回中,包含表面的双引号。注重,这里没有响应的response.etag的getter。
this.response.etag = crypto.createHash('md5'),update(this.body).digest('hex');
response.vary(field)
激活field。