koa框架的使用技巧

Koa

npm install koa

请求

写的好的博客:https://www.jianshu.com/p/e6aec8bcdcf4
Context

Koa Context 将节点的请求和响应对象封装到单个对象中,该对象为编写Web应用程序和API提供了许多有用的方法

每个请求都会创建一个Context,并在中间件中作为接收者或ctx标识符引用

app.use(async ctx => {
  ctx; 				// is the Context
  ctx.request		// is a Koa Request
  ctx.response 		// is a Koa Response
})

为方便起见,许多上下文的访问器和方法与委托给它们的ctx.requestctx.response是相同的

如:ctx.status 等同于 ctx.response.status

// ctx意思为context(上下文),拥有请求和响应的信息,大概内容如下
{
  request: {
    method: 'GET',
    url: '/',
    header: {
      host: 'localhost:3000',
      connection: 'keep-alive',
      'cache-control': 'max-age=0',
      'upgrade-insecure-requests': '1',
      'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36',
      'sec-fetch-dest': 'document',
      accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
      'sec-fetch-site': 'none',
      'sec-fetch-mode': 'navigate',
      'sec-fetch-user': '?1',
      'accept-encoding': 'gzip, deflate, br',
      'accept-language': 'zh-CN,zh;q=0.9'
    }
  },
  response: {
    status: 200,
    message: 'OK',
    header: [Object: null prototype] {
      'content-type': 'text/plain; charset=utf-8',
      'content-length': '6'
    }
  },
  app: { subdomainOffset: 2, proxy: false, env: 'development' },
  originalUrl: '/',
  req: '<original node req>',
  res: '<original node res>',
  socket: '<original node socket>'
}

ctx.requestctx.req 的区别(res同理):

  • ctx.request是koa2中context经过封装的请求对象,它用起来更直观和简单

  • ctx.req是context提供的node.js原生HTTP请求对象,虽然不那么直观,但是可以得到更多的内容,更适合深度编程


API
  • ctx.req

Node的请求对象

  • ctx.res

    Node的响应对象

  • ctx.request

    Koa的请求对象

  • ctx.response

    Koa的响应对象

  • ctx.method

    获取请求的类型

  • ctx.url

    获取请求的地址


路由

koa和express不同的是,koa要使用路由时,需要额外引入包

npm install koa-router
var Koa = require('koa')
var app = new Koa()
// 引入时直接创建实例 等同于var Router = require('koa-router') ; var router = new Router()
var router = require('koa-router')()	

// 配置路由
router.get('/',async (ctx) => {
    // 返回数据,相当于res.writeHead() 和 res.end()
    ctx.body = '首页'
    console.log(ctx)
}).get('/news', async (ctx) => {
    ctx.body = '新闻页'
})
// 链式调用写法,也可以再 router.get(),下面的app.use同理


// 启动路由(让路由生效)
app.use(router.routes())    // 启动路由 必须加上才能正常使用路由
    // 可以配置也可以不配置,建议配置,作用:用在router.routes()之后,所以在当所有路由中间件最后调用,此时根据ctx.status设置response响应头
    .use(router.allowedMethods())      

app.listen(3000)

其它使用技巧

import { Context } from 'koa'
import Router from 'koa-router'

const router = new Router({
  prefix: '/api',		// prefix 表示默认在所有路由前加的前缀
})

// 如声明一个router.get('/login') 实际声明的是'/api/login',用prefix可以省略

获取请求内容
get

在ctx里获取

  • query:返回将字符串格式化好的json对象
  • querystring:返回字符串
// 请求url: /admin?ad=123&ac=321
app.use(async(ctx)=>{
    let url =ctx.url
    
    //从request中获取GET请求
    let request =ctx.request
    let req_query = request.query
    let req_querystring = request.querystring
    
    //从上下文中直接获取
    let ctx_query = ctx.query				// { ad: '123', ac: '321' }
    let ctx_querystring = ctx.querystring	// ad=123&ac=321

})
// 使用 ctx.xxx 和 ctx.request.xxx 功能完全一样

上面的方法实际使用发现问题,使用下面这个

ctx.params		// { ad: '123', ac: '321' }
post

对于POST请求的处理,koa2没有封装方便的获取参数的方法,需要通过解析上下文context中的原生node.js请求对象req来获取

获取POST请求的步骤:
 1、解析上下文ctx中的原生nodex.js对象req。
 2、将POST表单数据解析成query string-字符串(如:name=zyb&age23)。
 3、将字符串转换成JSON格式

一般用中间件来实现获取post请求数据(ctrl点击跳转)


动态路由

在声明匹配路由路径时用:声明跟在这之后的url为指定变量,并且可以被匹配到

通过ctx.params获取到动态路由的传值

router.get('/admin/:aid',async (ctx) => {
	console.log(ctx.params)
}
           
// 请求/admin/test1,打印{ aid: 'test1' }
// 请求/admin/test2,打印{ aid: 'test2' }

中间件

koa的中间件和express不同,express的中间件执行的顺序是只看声明的顺序

koa中间件的执行顺序:匹配到没有next()返回去重新执行一遍之前匹配过的中间件,同路径的路由或中间件看声明顺序执行

匹配中间件→匹配路由→重新回去匹配中间件或路由执行next()后的代码

声明路径为 /(省略路径默认 /)时,表示匹配所有中间件

const Koa = require("koa");
const app = new Koa;

app.use(async (ctx, next) => {
    console.log('1'); 
    await next(); // 调用下一个middleware
    console.log('5')
});
app.use(async (ctx, next) => {
    console.log('2');
    await next(); 
    console.log('4');
});
app.use(async (ctx, next) => {
    console.log('3');
});

app.listen(3000)
console.log("listening on port 3000")

// 执行顺序:1 2 3 4 5
const Koa = require("koa");
const app = new Koa; 
var router = require('koa-router')()

router.get('/app', async (ctx,next) => {
    ctx.body = 'app'
    console.log('3')
    await next()
    console.log('5')
})

app.use(async (ctx, next) => {
    console.log('1'); 
    await next(); 
    console.log('7')
});

app.use(async (ctx, next) => {
    console.log('2'); 
    await next(); 
    console.log('6')
});

router.get('/app', async (ctx) => {
    console.log('4');  
});

router.get('/app', async (ctx) => {
    console.log('因为没有next因此无法匹配到这'); 
});


app.use(router.routes()) 
app.listen(3000)

// 访问 /app时,执行顺序 1 2 3 4 5 6 7
// 可以看到,是先匹配完了能匹配的中间件才开始去匹配路由的

错误处理中间件:

app.use(async (ctx,next) => {
    next()	// 也可以写在if判断后
    if(ctx.status == 404) {
        ctx.body('404页面')
    }
    else {
        ..... 
    }
})

ejs模板
npm istall koa-views
npm install esj

在koa中使用ejs并不需要安装了还要引用,只需下载就行,配置koa使用的是koa-views

同时配置该中间件时,必须保证在所有路由前配置,即app.use

配置方法一:网页文件的后缀名为.ejs

const views = require('koa-views');
//views 代表引入的koa-views模块;template代表我们保存模板文件的目录
app.use(views('views', {    
    extension: 'ejs'                   //指定我们使用的模板为ejs
  })
);

配置方法二:网页文件的后缀为.html

app.use(views('views', {
  map: {
    html: 'ejs'
  }
}))
const Koa = require("koa");
const app = new Koa; 
var router = require('koa-router')()
const views = require('koa-views')

app.use(views('views', {
    extension: 'ejs'
}))


router.get('/',async (ctx) => {  
    // index.ejs 需要放在项目/views文件夹下才能找到, awit必须加上
    await ctx.render('index', {
        title: "标题"
    })	
})

app.use(router.routes()) 
app.listen(3000)
// index.ejs 
<body>
    <%= title %>
</body>

使用TS编写时,ctx的返回类型为Context,但是此时如果使用import方式来导入koa-router,将ctx声明为Context时会因为有点问题报错,使用require的方式来导入就不会出现此问题,待找原因

koa-viewskoa-router同时用时,使用require方式引入路由


获取请求数据

原生NodeJs获取提交数据方式

【连接上面ejs代码】由于这里是异步渲染ejs,因此获取页面数据时也应该异步获取(用promise)

这种方式很麻烦

const Koa = require("koa");
const app = new Koa; 
var router = require('koa-router')()
const views = require('koa-views')

const getPostData = function(ctx) {
    return new Promise(function(resolve,reject){
        try { 
            let str = ''
            ctx.req.on('data', function(chunk) {
                str += chunk
            })
            ctx.req.on('end', function(chunk) {
                resolve(str)
            })
        }
        catch(err) {
            reject(err)
        }
    })       
}

// 在/doAdd路由里获取传送过来的post数据{name:...}
router.post('/doAdd', async(ctx) => {
    // 原生nodejs在koa中获取提交的数据
    var data = await getPostData(ctx)
    ctx.body = data
})

app.use(views('views', {
    extension: 'ejs'
}))

router.get('/',async (ctx) => {  
    await ctx.render('index')
})

app.use(router.routes()) 
app.listen(3000)

使用koa中间件获取提交数据方式

npm install koa-bodyparser

var bodyParser = require('koa-bodyparser')

app.use(bodyParser())	// 配置中间件

ctx.request.body	// 获取表单提交数据
const Koa = require("koa");
const app = new Koa; 
var router = require('koa-router')()
const views = require('koa-views')
var bodyParser = require('koa-bodyparser')

router.post('/doAdd', async(ctx) => {
    ctx.body = ctx.request.body
})

app.use(views('views', {
    extension: 'ejs'
}))

router.get('/',async (ctx) => {  

    await ctx.render('index')
})

app.use(bodyParser())		// 注意要放在路由中间件前,否则提交表单不会自动跳转到提交页面
app.use(router.routes()) 
app.listen(3000)


静态资源

去指定目录寻找静态资源文件,如果能找到返回对应文件,否则next()

npm install koa-static
const static = require('koa-static')

//app.use(static(dir_name))
app.use(static('static'))	// 匹配/static目录下的静态资源
app.use(static('public'))	// 可以配置多个,去多个目录下寻找

// 这里是绝对路径,且省略第一个/
// 即如果要匹配/src/style下的内容时,应该是下面这个,写成/src/style不行
app.use(static('src/style'))
// 这里有个坑,如:html需要成功导入一个/static/css/index.css的静态资源文件,由于中间件已经默认去/static文件夹下寻找了,因此路径需要省略static,应该用第二种写法
<link rel="stylesheet" href="static/css/index.css">	
<link rel="stylesheet" href="css/index.css">	

认证机制

Cookie

cookie介绍

  • 保存在浏览器客户端

  • 用同一个浏览器访问同一个域名的时候共享数据

浏览器与WEB服务器之间是使用HTTP协议进行通信的,当某个用户发出页面请求时,WEB服务器只是简单的进行响应,然后就关闭与该用户的连接,因此当一个请求发送到WEB服务器时,无论其是否是第一次来访,服务器都会把它当作第一次来对待,每一次的访问是没有建立联系的(http的无状态)

再再一次请求某个网站的链接时,将本地的cookie一并发送过去,这样浏览器就能识别到还是同一个客户在操作(实现多个页面之间数据的传递)

ctx.cookies.set(name, value, [options])

options属性:

  • maxAge:一个数组表示从Date.now()得到的毫秒数,表示相对时间
  • expires:cookie过期的Date,表示绝对时间
  • path:cookie路径,默认'/'都可访问,也可设置具体路由表示只在访问特定路由中传递cookie
  • domain:cookie域名,默认当前域下面的所有页面都可以访问
  • secure:安全cookie,默认false,为true时表示仅https可访问
  • httpOnly:是否只是服务器可访问cookie,默认true
  • overwrite:是否覆盖以前设置的同名cookie,默认flase
// 正常的基本配置
const Koa = require("koa");
const app = new Koa; 
const router = require('koa-router')()

router.get('/',async (ctx) => {  
    ctx.cookies.set('userinfo','mary',{
        maxAge:60*1000*60  // 多少毫秒后过期
    })
})

router.get('/news',async (ctx) => {  
    var userinfo = ctx.cookies.get('userinfo')
    console.log(userinfo)   // 访问首页再访问/news路由,打印mary
})

app.use(router.routes()) 
app.listen(3000)

koa中没法直接设置中文的cookie值

console.log(new Buffer('汉字').toSting('base64'))	 //拿到打印的中文base64字符
console.log(new Buffer('拿到的base64字符', 'base64').toString())	// 还原中文字符

因此在设置的时候使用base64字符,想要取cookie并正常显示的时候转换为汉字即可


Session

session也用来记录客户状态,不同的是cookie保存在客户端浏览器中,而session保存在服务器上

工作流程

session需要依赖cookie:

当浏览器访问服务器并发送第一次请求时,服务器端会创建一个session对象,生成一个类似于key,value的键值对

然后将key(cookie)返回到浏览器(客户)端,浏览器下次再访问时,携带key(cookie)找到对应的session(value),客户的信息都保存在session中

npm install koa-session
// 配置
const session = require('koa-session')

app.keys = ['some secret hurr']     // cookie的签名

const CONFIG = {
    // 主要设置maxAge和renew
    key: 'koa:sess',    // 默认 
    maxAge: 600000,     // 最大过期时间(相对毫秒)
    overwrite:true,     // 默认 true
    httpOnly:true,      // 默认true
    signed:true,        // 签名,默认true
    rolling:false,      // 每次访问的时候重新注册session,默认false
    renew:true         	// 当用户一直停留在一个页面时,有操作情况下session快过期的时候重新设置,默认flase(以防长时间操作过程中session失效)
}

app.use(session(CONFIG, app))

当设置了signed:true时,则必须要配置app.key属性,里面的字符串属性可以随意填,但是一般默认'some secret hurr',作用是将cookie的内容通过秘钥进行加密处理

const Koa = require("koa");
const app = new Koa; 
const router = require('koa-router')()
const session = require('koa-session')

router.get('/',async (ctx) => {  
    ctx.session.userinfo = '张三'  // 可以直接设置中文
})

router.get('/news',async (ctx) => {  
    var userinfo = ctx.session.userinfo
    console.log(userinfo)   
})


app.keys = ['some secret hurr']    
const CONFIG = {
    key: 'koa:sess',    
    maxAge: 600000,    
    overwrite:true,    
    httpOnly:true,      
    signed:true,       
    rolling:false,    
    renew:true        
}

app.use(session(CONFIG, app))
app.use(router.routes()) 
app.listen(3000)

跨域处理

JSONP

html中,有不受同源策略限制的标签

<script src="...">`		//加载js脚本
<img src="..."> 		//图片
<link href="...">		//css
<iframe src="...">		//任意资源

JSONP就是利用了<script/>标签的这个性质,利用脚本读取到跨域的数据并加载到本服务器,缺点是只能在GET请求中使用且不安全


CROS

浏览器发出CORS请求,需要对请求头增加一些信息,服务器会根据这些信息来是否决定同意这次请求

CROS需要请求方和服务器同时支持(IE不能低于10版本),几乎所有浏览器都支持CROS,因此只需要在服务器上实现CROS即可

服务器控制CROS的主要头信息字段:

  • Access-Control-Allow-Origin

    必须字段,浏览器通过设置该字段表示开启CROS,它指定允许进入来源的域名、ip+端口号 。 如果值是 *,表示接受任意的域名请求,这个方式不推荐,主要是因为其不安全

  • Access-Control-Allow-Credentials

可选字段,它设置是否可以允许发送cookie,true表示cookie包含在请求中,false则相反,默认为false。如果项目需要cookie就得设置该字段了,CORS请求默认不发送Cookie和HTTP认证信息的,所以在此基础上同时也需要在前端设置(以axios为例):axios.defaults.withCredentials = true

  • Access-Control-Max-Age

    可选字段,用于配置CORS缓存时间,即本次请求的有效期,单位为秒

  • Access-Control-Allow-Methods

    可选字段,设置允许请求的方法

  • Access-Control-Allow-Headers

    可选字段,设置允许的请求头信息


koa处理跨域

koa解决跨域的方式有很多种,最好的方案应该是在服务器端设置支持跨域

  • 原生设置方式:在koa2中设置具体的请求头信息

    设置允许请求的域名

app.use(async (ctx, next) => {
    ctx.set("Access-Control-Allow-Origin", "*")		// 允许所有域名请求
    await next()
})

// 只允许来自 http://localhost:8080 域名的请求
// ctx.set("Access-Control-Allow-Origin", "http://localhost:8080")

​ 设置允许的跨域请求方式

ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE")

​ 设置服务器所支持的所有头信息字段(不同值用逗号分隔)

ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type")
/* 
	服务器收到请求,检查
		Origin
		Access-Control-Request-Method
		Access-Control-Request-Headers
	字段,确认允许跨源请求,即可做出响应。
*/

​ 设置具体请求中的媒体类型信息

ctx.set("Content-Type", "application/json;charset=utf-8")

​ 设置是否允许发送cookie【可选设置】

ctx.set("Access-Control-Allow-Credentials", true);
// 默认情况下,Cookie不包括在CORS请求之中,当设置成允许请求携带凭证cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*"

​ 设置次验证的有效时间,即在该时间段内服务端可以不用再进行验证【可选设置】

ctx.set("Access-Control-Max-Age", 300);		// 单位为s
// 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证

​ 返回自定义所需字段

CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
    - Cache-Control
    - Content-Language
    - Content-Type
    - Expires
    - Last-Modified
    - Pragma
需要获取其他字段时,使用Access-Control-Expose-Headers
ctx.set("Access-Control-Expose-Headers", "myData")

完整的一次请求:

app.use(async (ctx, next) => {
    ctx.set("Access-Control-Allow-Origin", "*")
    ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type")
    ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type")
    ctx.set("Content-Type", "application/json;charset=utf-8")
    ctx.set("Access-Control-Allow-Credentials", true)
    ctx.set("Access-Control-Max-Age", 300)
    ctx.set("Access-Control-Expose-Headers", "myData")
    await next()
})
  • 使用中间件方式:koa2-cors
npm i koa2-cors -s

注册中间件

const cors = require('koa2-cors')
app.use(cors(...))
const Koa = require('koa');
const bodyParser = require('koa-bodyparser'); //post数据处理
const router = require('koa-router')(); //路由模块
const cors = require('koa2-cors'); //跨域处理

const app = new Koa();
app.use(
    cors({
        origin: function(ctx) { //设置允许来自指定域名请求
            if (ctx.url === '/test') {
                return '*'; // 允许来自所有域名请求
            }
            return 'http://localhost:8080'; //只允许http://localhost:8080这个域名的请求
        },
        maxAge: 5, //指定本次预检请求的有效期,单位为秒。
        credentials: true, //是否允许发送Cookie
        allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], //设置所允许的HTTP请求方法
        allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //设置服务器支持的所有头信息字段
        exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'] //设置获取其他自定义字段
    })
);
router.post('/', async function (ctx) {
    ctx.body = '请求成功了'
});


app.use(router.routes())
    .use(router.allowedMethods());

app.listen(3000);

一般无特殊用法的话直接全部配置默认就可(即什么都不需要填)

app
  .use(cors())
  .use(bodyParser())
  .use(router.routes())
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值