koa2中一些常用中间件

本文介绍Node.js的关键特点,包括其基于Chrome V8引擎的JavaScript运行环境、事件驱动的非阻塞IO机制等,并深入探讨koa2框架的应用实践,如静态资源服务、模板引擎配置、跨域资源共享及文件上传等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Node.js的特点和优势

    1、基于chrome V8引擎进行代码解析的JavaScript运行环境。

    2、事件驱动,

    3、非阻塞IO,IO即输入输出操作,阻塞IO可以理解为被阻塞了的输入输出操作,在服务器端有很多会涉及阻塞IO的操作,例如在读取文件过程中,需要等待文件读取完毕后才能继续执行后面的操作,Node.js中使用事件回调的方式来解决这种阻塞IO情况,避免了阻塞IO所需的等待,所以说Node.js具有非阻塞IO的特点。

    4、单进程、单线程,进程可以理解为一个引用程序运行,它是一个动态的概念;而线程是进程中的一部分,进程包含多个线程在运行。单线程就是进程中只有一个线程,阻塞IO模式下一个线程只能处理一个任务,而非阻塞IO模式下,一个线程永远在处理任务,这样cpu的利用率是100%。Node.js采用单线程,利用事件驱动的异步编程模式实现了非阻塞IO。

    5、轻量、可伸缩,适用于实时数据交互应用,在Node.js中,Socket可以实现双向通信,例如聊天室就是实时的数据交互应用。

 

koa2

静态资源服务

使用koa-static中间件

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

//静态文件目录是根目录下的public文件件
app.use(static(__dirname+'/public'));

 

ejs模板引擎

使用koa-views中间件

npm install koa-views ejs --save
const views = require('koa-views');

//使用模板引擎,ejs文件地址在根目录下的views文件夹
app.use(views(__dirname+'/views',{extension: 'ejs'}));

ejs基本语法

<% for(var i=0; i<articles.length; i++){ %>
    <li class="article">
        <a href="/article/<%= articles[i].id %>">
            <div class="article-cover" style="background-image: url(<%=articles[i].cover%>)"></div>
            <div class="article-text">
                <h2 class="article-title"><%= articles[i].title %></h2>
                <p class="article-intro"><%= articles[i].intro %>...</p>
            </div>
        </a>
    </li>
<% } %>

<%- include('side-nav')%>:内嵌公共ejs文件。

<%= %>输出变量,会发生转义

var ejs = require('ejs');
var result = ejs.render('<%=a%>',{a:'<div>123</div>'});
console.log(result);//&lt;div&gt;123&lt;/div&gt;

如果不希望内容被转义,可以使用<%- %>

var ejs = require('ejs');
var result = ejs.render('<%-a%>',{a:'<div>123</div>'});
console.log(result);//<div>123</div>

 

CORS跨域资源共享

使用koa2-cors中间件

npm install koa2-cors --save

我在路由目录下新建了一个cors.js文件

const cors = require('koa2-cors');

let allowUrl = ['/upload'];    //允许跨域的地址

//将cors()暴露出去给app.js使用
module.exports = function(){
    return cors({
        origin: function (ctx) {
            let url = ctx.url.indexOf('?') >= 0 ? ctx.url.slice(0, ctx.url.indexOf('?')) : ctx.url;
            if (allowUrl.indexOf(url) >= 0) {
                return 'http://localhost:8080'; // 允许请求的域
            }
            return false;
        },
        exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
        maxAge: 5,
        credentials: true,
        allowMethods: ['GET', 'POST'],
        allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
    })
}

程序入口文件app.js中

const cors = require('./router/cors');

app.use(cors());

参考文章

 

koa-body文件上传

npm install koa-body --save

app.js

const koaBody = require('koa-body');

app.use(koaBody({
    multipart: true,
    formidable: {
        maxFileSize: 200*1024*1024    // 设置上传文件大小最大限制,默认2M
    }
}));

新版本的koa-body通过ctx.request.files获取上传的文件,而旧版本的koa-body通过ctx.request.body.files获取上传的文件。

参考文章    参考文章

 

koa2路由

使用koa-router中间件

npm install koa-router --save

假设router/home.js路由文件

const Router = require('koa-router');
const router = new Router();

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

app.js

const Router = require('koa-router');
const router = new Router();
//引入路由文件
const home = require('./router/home');

//装载子路由
router.use('', home.routes(), home.allowedMethods());
//启用路由中间件
app.use(router.routes()).use(router.allowedMethods());

这里要注意的是,app.js中的router.use()第一个参数与home.js中的路由地址是字符串连接的,如果app.js中写'/',那么最终的路由地址是'//xxx'

 

Cookie

跟原生node.js一样cookie不需要中间件,直接使用。

设置cookie

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

options一些配置说明

maxAge              一个数字表示从 Date.now() 得到的毫秒数
expires cookie      过期的 Date
path cookie         路径, 默认是'/'
domain cookie       域名
secure             安全 cookie   默认false,设置成true表示只有 https可以访问
httpOnly           是否只是服务器可访问 cookie, 默认是 true
overwrite          一个布尔值,表示是否覆盖以前设置的同名的 cookie (默认是 false). 如果是 true, 在同一个请求中设置相同名称的所有 Cookie(不管路径或域)是否在设置此Cookie 时从 Set-Cookie 标头中过滤掉。

基本用法

ctx.cookies.set('user', username, {
    // domain: 'localhost',  // 写cookie所在的域名
    // path: '/index',       // 写cookie所在的路径
    maxAge: 60 * 1000, // cookie有效时长
    // expires: new Date('2017-02-15'),  // cookie失效时间
    httpOnly: true,  // 是否只用于服务器端
    overwrite: false  // 是否允许重写
})

取值

ctx.cookies.get('name')

koa中设置中文cookie可能会报错,可以用Buffer将中文转成base64编码存到cookie中

new Buffer('你好世界').toString('base64')// 转换成base64字符串:5L2g5aW95LiW55WM
new Buffer('5L2g5aW95LiW55WM', 'base64').toString()// 还原base64字符串:你好世界

 

Session

使用koa-session中间件

npm install koa-session --save

官方文档使用

const session = require('koa-session');

app.keys = ['some secret hurr'];
const CONFIG = {
   key: 'koa:sess',   //cookie key (default is koa:sess)
   maxAge: 86400000,  // cookie的过期时间 maxAge in ms (default is 1 days)
   overwrite: true,  //是否可以overwrite    (默认default true)
   httpOnly: true, //cookie是否只有服务器端可以访问 httpOnly or not (default true)
   signed: true,   //签名默认true
   rolling: false,  //在每次请求时强行设置cookie,这将重置cookie过期时间(默认:false)
   renew: false,  //(boolean) renew session when session is nearly expired,
};
app.use(session(CONFIG, app));

使用

设置 ctx.session.username = "xxx";
获取 ctx.session.username

 

文件下载

其实文件下载的思路并不难,就是设置个响应头,主要设置两个

res.setHeader('Content-Disposition', `attachment; filename=${filename}`);
res.setHeader('Content-Type', 'application/x-download; charset=utf-8');

看看简单的原生代码

let {pathname} = url.parse(req.url);

if(pathname.startsWith('/download/')){
    let filename = pathname.slice(10);
    let filepath = `logs/${filename}`;
    let reader = fs.createReadStream(filepath);
    res.setHeader('Content-Disposition', `attachment; filename=${filename}`);
    reader.pipe(res);
}

使用koa-send中间件

npm install koa-send --save

基本使用

const send = require('koa-send');

router.get('/download/:filename', async ctx => {
    let filepath = `logs/${ctx.params.filename}`;
    ctx.attachment(filepath);
    await send(ctx, filepath);
})

前端

<button class="btn">点我下载文件</button>
<script>
    document.querySelector('.btn').onclick = function(){
        window.open('/download/2018-11-07.log', '_blank');
    }
</script>

参考文章    参考文章

axios实现文件下载

首先后台响应文件流,这里我还是不用koa-send中间件

router.get('/download', async ctx => {
    //文件路径
    let filepath = path.join(__dirname, `../logs/${ctx.query.filename}`);
    //创建文件读取流
    let reader = fs.createReadStream(filepath);
    //设置响应头
    ctx.set('Content-Disposition', `attachment; filename=${ctx.query.filename}`);
    ctx.set('Content-Type', 'application/x-download; charset=utf-8');
    //响应文件流
    ctx.body = reader;
})

前端用blob对象接收流,然后创建a标签实现下载

axios.get('xxx').then(res => {
    let blob = new Blob([res.data],{
        type: 'text/plain'      //文件MIME类型 
    });
    let url = window.URL.createObjectURL(blob);
    // window.location.href = url;
    let a = document.createElement('a');    //用a标签指定下载文件名
    a.href = url;
    a.download = filename;
    a.click();
})

参考文章

 

koa-jwt

主要用来进行token验证的,一般还要配合jsonwebtoken使用。

安装

npm install jsonwebtoken koa-jwt --save

登录成功服务端给客户端下发token

const jwt = require('jsonwebtoken');
const jwtSecret = 'xxx';

router.post('/login', async ctx => {
    let user = ctx.request.body;
    let count = await User.count(user);
    // 如果用户存在
    if(count > 0){
        // 生成token
        let token = jwt.sign({
            name: 'xxx'
        }, jwtSecret, {     //秘钥,自定义
            expiresIn: '1h'     //过期时间
        })
        return ctx.body = {code: 1, token}
    }else{
        return ctx.body = {code: 0}
    }
})

前端接收到token后将它储存起来,可以用localStorage或sessionStorage,然后在每次请求时将token放入请求头中,这里举例在vue中用法

// 给每个请求头加token
axios.interceptors.request.use(config => {
    const token = sessionStorage.getItem('token');    //我将token储存在sessionStorage
    config.headers.common['Authorization'] = 'Bearer ' + token;
    return config;
})

最后后台用koa-jwt中间件验证token

const koaJwt = require('koa-jwt');
const jwtSecret = 'xxx';

// token验证
router.use(koaJwt({
    secret: jwtSecret
}));

jsonwebtoken验证token

let token = rq.body.token || rq.query.token || rq.headers["x-access-token"]; // 从body或query或者header中获取token

jwt.verify(token, secretOrPrivateKey, function (err, decode) {
    if (err) {  //  时间失效的时候/ 伪造的token          
         rs.json({err:err})
    } else {
         rq.decode = decode; 
         console.log(decode.msg);   // token文本内容
         next();
    }
})

参考文章    参考文章

 

日志管理

我写了一个基于log4js的日志中间件koa-sam-log,我的文章

 

错误处理

利用koa2的洋葱模型在第一个中间件中try catch就可以。

app.use(async (ctx, next) => {
    try{
        await next();
        if(ctx.status == 404) ctx.throw(404);//如果后台没响应,就报404
        console.log(ctx.status, '没报错')
    }catch(err){
        console.log(ctx.status, err.statusCode, err.statusm, '报错了');
        ctx.status = err.statusCode || err.status || ctx.status;
        ctx.log.error(JSON.stringify({errorCode: ctx.status, errorMsg: err.message}));
        ctx.body = err.message;
    }
})

参考文章

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值