Koa 以及中间件学习笔记


2019-3-30 Koa Koa-router

简单开始

级联

next() 暂停当前函数的执行,进行下面函数的执行,然后会到上游next函数执行处,继续向下执行。

const Koa =require('koa')
const app = new Koa()
app.use(async  (ctx, next) =>{
    console.log(1)
    await next()
    const rt = ctx.response.get('X-Response-Time')
    console.log(`${ctx.method} ${ctx.url} - ${rt}`);
    console.log(2)
})

app.use(async  (ctx, next) =>{
    console.log(3)
    const start =Date.now()
    await next()
    const ms= Date.now() - start
    ctx.set('X-Response-Time',`${ms}ms`)
    console.log(4);
})

app.use(async  ctx =>{
    ctx.body='Hello world'
    console.log(5)
    
})
app.listen(3000)


// 1 3 5 4 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

属性

app的属性和方法

app.env

环境变量 默认使NODE_ENV development

app.proxy

//设置代理

app.subdomainOffset

可以监听多个端口

app.callback()

将方法用于 http.createServer https.createServer 或者connect 和express程序

app.use(function)

将给定的中间件方法添加到此应用程序

app.keys

设置Cookie 密匙

app.context

从外部直接向ctx 内部添加属性

app.context.db = db();

app.use(async ctx => {
  console.log(ctx.db);
});
1
2
3
4
5

错误处理

通过监听 error 事件进行事件处理 app.on('error', err , ctx=> { //ctx 可以继续向下传递 log.error('server error', err) //错误日志 });

ctx 属性

基本属性

  • ctx.req

  • ctx.res node 的请求与相应对象

  • ctx.responce

  • ctx.request koa 的对象

  • ctx.state 推荐的进行变量绑定的地方

  • ctx.app app对象

  • ctx.app.emit 触发on的绑定事件

  • ctx.cookies.set 设置cookie

  • ctx.cookies.get 获取cookis

  • ctx.throw([status], [msg], [properties]) 通常使用户的错误 而不是服务器的错误 http-errors

  • ctx.assert(value, [status], [msg], [properties]) 用于断言 当第一个值为false的时候 第二值为错误码 第三个值使message http-assert

Request 别名

以下访问器和 Request 别名等效:

  • ctx.header 请求的标识头 可设置

  • ctx.headers ==上面

  • ctx.method 请求方法 可设置

  • ctx.url 请求的url 可设置

  • ctx.originalUrl 获取请求原始URL

  • ctx.origin 获取URL的来源,包括 protocol 和 host。

  • ctx.href

  • ctx.path

  • ctx.path=

  • ctx.query

  • ctx.query=

  • ctx.querystring

  • ctx.querystring=

  • ctx.host

  • ctx.hostname

http://localhost:3000 -------origin   常用
http://localhost:3000/apple?abc=11111 -------href
/apple?abc=11111 -------originalUrl
/apple -------path  常用
/apple?abc=11111 -------url
{ abc: '11111' } -------query 常用
abc=11111 -------querystring
localhost:3000 -------host
localhost -------hostname
1
2
3
4
5
6
7
8
9
  • ctx.fresh 检查缓存是否新鲜 也就是内容没有改变。 在设置一个或多个这些响应头后应该引用它。
  • ctx.stale 以上面相反
  • ctx.socket request.socket 返回请求套接字。
  • ctx.protocol 返回请求协议,“https” 或 “http”。当 app.proxy 是 true 时支持 X-Forwarded-Proto。
  • ctx.secure 通过 ctx.protocol == "https" 来检查请求是否通过 TLS 发出。
  • ctx.ip 返回请求的IP
  • ctx.ips 返回转发过后的多个IP
  • ctx.subdomains 返回子域名 根据 subdomainsoffset偏移量试着
  • ctx.is() 检查传入请求是否包含 Content-Type 头字段, 并且包含任意的 mime type。 如果没有请求主体,返回 null。 如果没有内容类型,或者匹配失败,则返回 false。 反之则返回匹配的 content-type。
  • ctx.accepts() request.accepts(types) 通过类型检查 可以适用switch设置
  • ctx.acceptsEncodings() request.acceptsEncodings(encodings) encodings检查
  • ctx.acceptsCharsets() request.acceptsCharsets(charsets) 编码类型检查
  • ctx.acceptsLanguages() request.acceptsLanguages(langs) 语言类型检查
  • ctx.get() request.get(field) 返回请求标头。

Response 别名

以下访问器和 Response 别名等效:

  • ctx.body
  • ctx.body=
string 写入
Buffer 写入
Stream 管道
Object || Array JSON-字符串化
null 无内容响应
1
2
3
4
5
  • ctx.status 状态码
  • ctx.status=
  • ctx.message 信息
  • ctx.message=
  • ctx.length= 长度
  • ctx.length
  • ctx.type= 获取响应 Content-Type 不含参数 "charset"。
  • ctx.type
  • ctx.headerSent 检查是否已经发送了一个响应头。 用于查看客户端是否可能会收到错误通知。
  • ctx.redirect() response.redirect(url, [alt])
  • ctx.attachment()将 Content-Disposition 设置为 “附件” 以指示客户端提示下载。(可选)指定下载的 filename 和部分 参数。
  • ctx.set() 设置响应标头 field 到 value: 用一个对象设置多个响应标头fields:
  • ctx.append() 用值 val 附加额外的标头 field。
  • ctx.remove() 移除表头
  • ctx.lastModified= 将 Last-Modified 标头返回为 Date, 如果存在。 检查最后的更改事件
  • ctx.etag= 设置etag 头部
  • response.is(types...) 。这对于创建操纵响应的中间件特别有用。 html-minifier,可以削减除流之外的所有HTML响应。
  • response.vary(field) 查看在 field 上变化。
  • response.flushHeaders() 刷新任何设置的标头,并开始主体。

缓存

使用 HTTP 缓存:Etag, Last-Modified 与 Cache-Control Cache-Control 响应头表示了资源是否可以被缓存,以及缓存的有效期。

  1. no-cache 为本次响应不可直接用于后续请求(在没有向服务器进行校验的情况下)
  2. no-store 为禁止缓存(不得存储到非易失性介质,如果有的话尽量移除,用于敏感信息)
  3. private为仅 UA 可缓存
  4. public为大家都可以缓存。
  5. 可以添加过期时间 max-age Etag 响应头标识了资源的版本,此后浏览器可据此进行缓存以及询问服务器。
  6. Etag 响应头字段表示资源的版本
  7. 浏览器在发送请求时会带 If-None-Match 头字段, Last-Modified 响应头标识了资源的修改时间,此后浏览器可据此进行缓存以及询问服务器。
  8. if-modified-since 在请求头中 时间戳
  9. 如果没有过过期时间则可以直接适用缓存 返回304 而不返回任何资源
  10. 否则返回一个Last-Modified', new Date().toString() 设置新时间

koa 中间件

  • Keygrip是一个node.js模块,用于通过轮换凭证系统对数据(如cookie或URL)进行签名和验证,其中可以添加新服务器密钥并定期删除旧服务器密钥,而不会使客户端凭据无效。
  • html-minifier 缩小html 大小
  • koa-router 路由
  • koa-bodyparser 解析post
  • koa-session session 中间件
  • koa-logger koa 使用日志打印的中间件

koa-router

路由使用

/*
 * @Author: yu-lei 
 * @Date: 2019-08-22 20:02:15 
 * @Last Modified by: yu-lei
 * @Last Modified time: 2019-08-26 11:22:21
 */

 /**
  * 包区域
  */
const koa =require('koa')
const koaRouter = require('koa-router')
const koaBodyparser = require('koa-bodyparser')


/**
 * 定义区域
 */
const app =new koa()
const router = new koaRouter()

/**
 * 文件区域
 */
//路由

const userRoutes = require('./routers/users')()
const homeRoutes = require('./routers/homes')()



  
/**
 * 中间件
 */
app.use(koaBodyparser())
app.use(router.routes())
app.use(homeRoutes.routes())
app.use(userRoutes.routes())
app.use(router.allowedMethods()) //开启所有的请求方法

/**
 * 服务器
 */
app.listen(3000, () =>{
    console.log("服务器已经开启!")
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

路由匹配方式

router.all('/*' , async (ctx,next) =>{
    console.log(ctx.url)
    // 决绝特殊访问服务
    // 搜索引擎优化 
    //roubat
    // 过滤服务 
    await next()
})
router.get()// get|put|post|patch|delete|del|all
router.regiest('path','GET',(ctx.next)=>{}) // 注册方法
1
2
3
4
5
6
7
8
9
10

路由的三种传递参数方式

1 通过动态router 传递

通过ctx.params.id 获取


 router.get('/home/:id',async (ctx,next) =>{
        ctx.body="<h1>home route</h1>"
        console.log(ctx.params.id) // 
       
    })
1
2
3
4
5
6

2 通过query 字符串传递

通过ctx.query.参数名称获取

 router.get('/home',async (ctx,next) =>{
        ctx.body="<h1>home route</h1>"
        console.log(ctx.query.id) // 
       
    })
1
2
3
4
5

3 通过post 传递

通过 koa-bodyparser 内的 ctx.request.body 获取

router.post('/home',async (ctx) =>{
        console.log(ctx.request.body)
        ctx.body=ctx.request.body
        // console.log(ctx.request.body)
    })
1
2
3
4
5

redirect

router.redirect('从那个路由',’跳转到那个路由‘,状态码)
// redirect 可能不需要匹配prefex 和父组件也会 匹配到路由跳转

1
2
3

use 嵌套路由

const userRouter = new koaRouter({
    prefix:'/user'
})
userRouter.get('/admin',async ctx=>{
    //todo
})
//中间件使用方法
router.use('/home',userRouter.routes(),userRouter.allowedMethods({配置}))

//请求地址为
// /home/user/admin     显示嵌套的父级路由  然后是子集路由的prefix   然后是接口
1
2
3
4
5
6
7
8
9
10
11

router.param(param, middleware)

所有带有id 参数的路由全部需要先经过这个路由

const router = new koaRouter({
        prefix:'/home'
    })  //子路由
   
    router
    // 所有带有id 参数的路由全部需要先经过这个路由
    .param('id',async (id, ctx, next) =>{
        console.log(ctx.path,'param');
        await next()
    })
    .get('/user/:id' , async ctx =>{
        console.log(1,ctx.params.id)

    })
    .get('/haha/:id' , async ctx =>{
        console.log(2,ctx.params.id)

    })
    .redirect('/abc/:id','/haha/1',303)
    
    
    return router
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22