Koa与常用中间件的使用

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_40629244/article/details/100786327

1.Koa是什么?

Node.js 是一个异步的世界,官方 API 支持的都是 callback 形式的异步编程模型,这会带来许多问题,例如callback 的嵌套问题 ,以及异步函数中可能同步调用 callback 返回数据,可能会带来不一致性,为了解决以上问题 Koa 出现了。

Koa 是基于 Node.js 平台的下一代 web 开发框架。

Koa是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 使用 Koa 编写 web 应用,可以免除重复繁琐的回调函数嵌套, 并极大地提升错误处理的效率。Koa不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手,开发思路和 Express 差不多,最大的特点就是可以避免异步嵌套。

2.Koa框架的安装使用

(1).安装 Node.js 7.6以上的版本

使用Koa开发之前,Node.js 是有要求的,它要求 Node.js 版本高于 V7.6,因为 Node.js 7.6 版本 开始完全支持 async/await,所以才能完全支持我们的 Koa。

(2).安装 Koa

npm install --save koa  或者用淘宝镜像的   cnpm install --save koa 

(3).Koa的基本使用

// 引入Koa
var koa = require('koa');
// 实例化Koa
var app = new koa();

// 配置路由
app.use( async(ctx)=>{
    ctx.body = "你好,koa"
});

// 监听端口
app.listen(3000);

3.Koa异步处理Async 、Await和Promise 的使用

async 是让方法变成异步。在终端里用 node 执行这段代码,你会发现输出了 Promise {‘Hello async’ },它返回的是 Promise。

async function testAsync(){
    return 'Hello async';
};
const result = testAsync();
console.log(result);

// Promise { 'Hello async' }

await 在等待async方法执行完毕。其实await等待的只是一个表达式,这个表达式在官方文档里说的是Promise对象,但是它也可以接受普通值。 注意:await必须在async方法中才可以使用,因为await访问本身就会造成程序停止堵塞,所以必须在异步方法中才可以使用。

function getData() {
    return 'This is data';
};
async function testAsync() {
    return 'Hello async';
};
async function test() {
    // await 返回普通值
    const v1 = await getData();
    console.log(v1);
    // This is data

    // await 返回promise对象
    const v2 = await testAsync();
    console.log(v2);
    // Hello async
};
test();

综上所述,async 用于申明一个 function 是异步的,它会将其后的函数的返回值封装成一个 Promise 对象,而 await 用于等待一个异步方法即 Promise 执行完成,并将其 resolve 的结果返回出来。

4.Koa路由的使用

Koa中的路由和Express有所不同,在Express中直接引入Express就可以配置路由,但是在Koa中我们需要安装对应的koa-router路由模块来实现。

// 1.安装 npm install --save koa-router

var Koa = require('koa');

// 2.引入路由并实例化
var router = require('koa-router')();
var app = new Koa();


// 3.配置路由
// 区别于Express,req和res的信息全部放到ctx里
router.get('/', async (ctx) => {
    // 返回数据
    // 相当于原生Node.js里的res.write()和res.end();
    ctx.body = "首页";

});

// 4.启动路由(来自于官方文档);
// router.allowedMethods()可以配置也可以不配置。
// 如果之前的没有设置响应头,配置此选项以后可以自动设置响应头。
app.use(router.routes()).use(router.allowedMethods());


// 监听端口
app.listen(3000);

另外,Koa的路由执行顺序与Express有所不同,Koa 选择了洋葱圈模型,即最先访问的路由里的next()后面的代码最后响应。

以下一段代码会按编号依次输出,说明路由的执行顺序。

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

app.use(async (ctx, next) => {
    console.log('1.这是一个中间件01');
    await next();
    console.log('5.匹配完路由以后又会返回来执行中间件')
});
app.use(async (ctx, next) => {
    console.log('2.这是一个中间件02');
    await next();
    console.log('4.匹配完路由以后又会返回来执行中间件')
});                   
router.get('/news', async (ctx) => {
    console.log('3.匹配到了news这个路由');
    ctx.body = '新闻页'
});


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

5.Koa中间件

中间件是配合路由匹配完成做的一系列的操作,我们就可以把它叫做中间件。Koa中运用中间件可以实现以下一些功能:

(1).添加应用。主要通过app.use()这个函数添加或是启动一些应用,如常见的一些第三方中间件的使用。

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

// Koa 应用级中间件
app.use(async (ctx, next) => {
    // 匹配路由之前打印日期
    console.log(new Date());
    // 当前配路由匹配完成以后继续向下匹配
    // 如果不写next(),这个路由被匹配到了就会终止
    await next();
});

router.get('/news', async (ctx) => {
    ctx.body = '新闻页面'
});
// 启动路由;
// router.allowedMethods()可以配置也可以不配置
// 如果之前的没有设置响应头,配置此选项以后可以自动设置响应头
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(3000);

(2).匹配路由。主要通过next()这个函数完成多级路由匹配。

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

// Koa 路由级中间件

// 配置新闻页
router.get('/news', async(ctx,next) => {
    console.log('这是新闻路由');
    // 匹配到路由以后继续向下匹配
    await next();
});

// 配置新闻页
router.get('/news', async (ctx) => {
    ctx.body = '这是新闻页'
});

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

app.listen(3000);

(3).错误处理。如果当前访问的路由一直向下匹配没有匹配到,可以通过中间件给出错误响应。

var Koa = require('koa');
var router = require('koa-router')();
var app = new Koa();
// Koa 错误处理中间件
// 无论app.use放到路由前面还是后面
// 都是先执行app.use再去执行路由
app.use(async (ctx, next) => {
    console.log('这是一个中间件');       // 执行顺序1
    await next();
    if (ctx.status == 404) {           // 执行顺序3
        ctx.body = '这是一个404页面';
    } else {
        console.log(ctx.url);
    }
});
// 配置新闻页                            // 执行顺序2
router.get('/news', async (ctx, next) => {
    console.log('这是新闻路由');
    await next();
});

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

app.listen(3000);

6.Koa中get传值及获取get传值。

Koa中的get传值主要有以下两种方式:

(1).键值对拼接传参,接收参数主要通过ctx.qruery获取,需要注意的是在ctx与ctx.request里均可以获得一样的结果。

var Koa = require('koa');
var router = require('koa-router')();
var app = new Koa();
// http://localhost:3000/news_details?id=123&author=aiguangyuan
router.get('/news_details',async(ctx)=>{

    // 从ctx中读取Get传值

    console.log(ctx.url);
    // /news_details?id=123&author=aiguangyuan

    console.log(ctx.query);
    // { id: '123', author: 'aiguangyuan' }
    // 获取的是对象,用的最多的方式

    console.log(ctx.querystring)
    // id=123&author=aiguangyuan
    // 获取的是字符串

    
    // 从ctx里的request里获取Get传值

    console.log(ctx.request.url);
    // /news_details?id=123&author=aiguangyuan

    console.log(ctx.request.query);
    // { id: '123', author: 'aiguangyuan' }

    console.log(ctx.request.querystring);
    // id=123&author=aiguangyuan

    ctx.body='新闻详情页';
  
});

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

(2).动态路由传参,接收参数主要通过ctx.params获取。

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

// http://localhost:3000/news_details/123/456
router.get('/news_details/:aid/:cid', async (ctx) => {
    // 获取动态路由的传值
    console.log(ctx.params);
    // { aid: '123' ,cid:'456'}
    ctx.body = '新闻详情页';

});

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

7.Koa中post传值的获取。

Koa中的post传值主要有以下两种方式:

(1).封装一个原生获取post传参的方法。

// 封装获取post传参的方法
exports.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);
        }
    })
};

引入封装的方法获取数据。 

var Koa = require('koa');
var router = require('koa-router')();
// 引入封装的方法
var common = require('./module/common.js')
var app = new Koa();

// 接收post提交的数据
router.post('/doAdd', async (ctx) => {
    // 用封装的方法获取数据
    var data = await common.getPostData(ctx);
    console.log(data);
    // username=aiguangyuan&password=123456
    ctx.body = '添加成功';
});

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

 (2).使用body-parser中间件获取post数据。


// 1.安装 npm install --save koa-bodyparser;

var Koa = require('koa');
var router = require('koa-router')();
// 2.引入中间件
var bodyParser = require('koa-bodyparser'); 

var app = new Koa();
// 3.配置bodyparser的中间件
app.use(bodyParser());

router.post('/doAdd', async (ctx) => {
    // 4.通过ctx.request.body获取表单提交的数据
    ctx.body=ctx.request.body;
    // {"username":"aiguangyuan","password ":"123456"}  
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);

8.Koa中 Cookie的使用

Cookie保存在浏览器客户端,可以让我们同一个浏览器访问同一个域名的时候共享数据,可以实现以下一些功能。

(1).保存用户信息

(2).浏览器历史信息

(3).猜你喜欢功能

(4).10天免登陆

(5).多个页面之间的数据传递

(6).cookie实现购物车功能

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

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

    // 注意,koa中的cookie没法直接设置成中文

    // 利用Buffer解决设置cookie中文问题
    var userinfo = new Buffer('张三').toString('base64');

    // 1.设置cookie
    ctx.cookies.set('userinfo',userinfo ,{
        // 设置过期的毫秒数
        maxAge: 60 * 1000 * 60,

        // 以下参数不常用

        // 具体的过期时间
        expires: '2019-1-1',
        // cookie可以访问的路径,默认是'/'
        path: '/',
        // cookie可以访问的域名,默认就是当前域名下所有的页面,除非有多个子域名时
        domain: '',
        // 安全cookie,默认false,设置成true表示只有https可以访问
        secure: false,
        // 是否只是服务器可访问cookie,默认是true
        httpOnly: true,
        // 是否覆盖,默认为false,不常用
        overwrite: false
    });
    ctx.body = "这是首页";
});

// 在另一个路由中访问cookie
router.get('/content', async (ctx) => {
    // 2.获取设置的cookie,并将cookie进行转换
    var data = ctx.cookies.get('userinfo');
    var userinfo = new Buffer(data,'base64').toString();

    console.log(userinfo);
    // 张三
    ctx.body = '这是内容页'
});

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

 

9.Koa中 Session的使用

session是一种记录客户状态的机制,相比于cookie,它们的区别主要有以下几点:

(1).cookie数据存放在客户的浏览器上,session数据放在服务器上。

(2).cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。

(3).session会在一定时间内保存在服务器上,当访问增多,会占用服务器,考虑到性能应当使用cookie。

(4).单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

// 1.npm install koa-session --save

var Koa = require('koa');
var router = require('koa-router')();

// 2.引入koa-session
var session = require('koa-session');
var app = new Koa();

// 3.设置cookie的签名,可以设置或默认
app.keys=['some secret hurr']; 
// 4.配置session的中间件
var CONFIG = {
    key:'koa:sess',            // cookie密钥(默认)
    maxAge:8600000,            // cookie的过期时间(修改)          
    overwrite:true,            // 是否可以覆盖重写(默认)
    httpOnly:true,             // 是否只有服务器可以获取(默认)
    signed:true,               // 签名(默认)
    rolling:false,             // 每次访问都更新session(默认)
    renew:true                 // 快要到过期时间时更新session(修改) 
};

// 5.启用中间件
app.use(session(CONFIG,app));

router.get('/login', async (ctx) => {
    // 设置session
    ctx.session.userinfo = '张三';
    ctx.body = "这是登录页";
});

router.get('/', async (ctx) => {
    // 获取session
    var userinfo = ctx.session.userinfo;
    console.log(userinfo);
    // 张三
    ctx.body = '这是首页'
});

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

10.Koa中静态资源的获取

Koa中的静态资源响应主要通过Koa-static这个中间件来实现。


// 1.npm install koa-static --save

var Koa = require('koa');
var Router = require('koa-router');
var views = require('koa-views');

// 2.引入处理静态资源的中间件
var static = require('koa-static');

var app = new Koa();
var router = new Router();
app.use(views('views', {
    extension: 'ejs'
}));
app.use(bodyParser());

// 3.配置静态web服务的中间件;
// 可以写多个,在不同的目录下去找
app.use(static(__dirname+'/static'));
app.use(static(__dirname+'/public'));

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

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

11.Koa中使用Ejs模板引擎

Ejs是一个JavaScript模板引擎,用来将数据库查询的数据渲染到模板上面,实现一个动态网站。


// 1.npm install koa-views --save
// 2.npm install ejs --save 

var Koa = require('koa');
var router = require('koa-router')();

// 3.引入koa-views
var views = require('koa-views');

var app = new Koa();


// 4.配置模板引擎中间件
// views中第一个参数为放置模板文件的位置
app.use(views('views', {
    // 运用EJS模板引擎,模板文件以ejs为结尾
    extension: 'ejs'
}));

// 按下面的方式配置模板引擎也是可以的
// app.use(views('views',{
//     模板文件以html结尾
//     map:{ html:'ejs'}
// }));

// 假如我们需要在每一个路由的render里面都要渲染一个公共的数据
// 需要将公共的数据放在中间件里面,这样的话在模板的任何地方都可以使用
app.use(async(ctx,next)=>{
    // 在任何一个路由里都可以使用userinfo
    ctx.state.userinfo ='李四';
    await next()
});


// 配置新闻页路由
router.get('/news', async (ctx) => {
    // 模拟从数据库拿到的数据
    let list = [
        '习大大发表讲话', 
        '特朗普发表讲话', 
        '李克强发表讲话'
    ];
    let content="<h2>这是一个H2</h2>";
    let num = 100;
    // 5.渲染静态页面
    await ctx.render('news', {
        list:list,
        content:content,
        num:num
    });

});

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

Ejs常用的语法如下:

<!-- 1.引入公共的文件 -->
<% include public/header.ejs%>

<!-- 2.循环渲染数据 -->
<ul>
    <%for(var i=0;i<list.length;i++){%>
        <li><%=list[i]%></li>
    <%}%>
</ul>

<!-- 3.绑定html数据,不解析 -->
<%=content%>

<!-- 4.渲染html数据,并解析 -->
<%-content%>

<!-- 5.条件判断 -->
<% if(num==100){ %>
    <p>当前数字等于100</p>
<% }else{ %>
    <p>当前数字不等于100</p>
<% } %>

<!-- 6.渲染所有路由公共的数据 -->
<p><%=userinfo %></p>

12.Koa中使用Art-template模板引擎

Art-template 是一个简约、超快的模板引擎。它采用作用域预声明的技术来优化模板渲染速度,从而获得接近 JavaScript 极限的运行性能,并且同时支持 NodeJS 和浏览器。Art-template支持Ejs的语法,也可以用类似Angular数据绑定的语法。

// 1.安装模块
// npm install --save art-template
// npm install --save koa-art-template

var Koa = require('koa');
var router = require('koa-router')();
var path = require('path');

// 2.引入模块
var render = require('koa-art-template');
var app = new Koa();


// 3.配置模块
render(app, {
    //  视图的位置
    root: path.join(__dirname, 'views'),
    //  后缀名
    extname: '.html',
    //  是否开启调试模式
    debug: process.env.NODE_ENV !== 'production'
});

router.get('/detail', async (ctx) => {
    // 模拟从数据库获取的数据
    let data = { name: '李四' };
    let text = '<span>这是一个文本</span>';
    let num = 10;
    let list = ['列表项1','列表项2','列表项3'];

    // 4.渲染数据
    await ctx.render('detail', {
        data:data,
        text:text,
        num:num,
        list:list
    })
});

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

Art-template模板引擎的Ejs写法。

<h1>Art-template模板引擎的Ejs写法</h1>

<h2>1.绑定数据</h2>
<span><%=data.name %></span>
<span><%=1+2 %></span>


<h2>2.绑定html数据</h2>
<span><%-text%></span>


<h2>3.条件渲染</h2>
<% if(num>10){ %>
    <span>大小10</span>
<% }else{ %>
    <span>小于10</span>
<%}%>


<h2>4.循环数据</h2>
<% for(var i=0;i<list.length;i++){ %>
    <p>
        <%=i%>--<%=list[i]%>
    </p>
<%}%>

<h2>5.模板引入</h2>
<% include('./public/footer.html') %>

Art-template模板引擎的Angular写法。

<h2>Art-template模板引擎的Angular写法</h2>

<h2>1.绑定数据</h2>
<span>{{data.name}}</span>
<span>{{1+2}}</span>


<h2>2.绑定html数据</h2>
<span>{{@text}}</span>


<h2>3.条件渲染</h2>
{{ if num>10 }}
    <span>大小10</span>
{{else}}
    <span>小于10</span>
{{/if}}


<h2>4.循环数据</h2>
{{each list}}
    <p>
        {{$index}}--{{$value}}
    </p>
{{/each}}


<h2>5.模板引入</h2>
{{ include 'public/footer.html' }}

13.Koa文件上传中间件Koa-multer的使用


// 1.npm install  koa-multer --save

// 2.引入koa-multer
const multer = require('koa-multer');

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


// 3.配置koa-multer; 
var storage = multer.diskStorage({
    // 文件保存路径
    destination: function (req, file, cb) {
        // 注意路径必须存在
        cb(null, 'public/uploads')
    },
    // 修改文件名称
    filename: function (req, file, cb) {
        var fileFormat = (file.originalname).split(".");
        // 图片名称为时间戳加后辍名
        cb(null,Date.now()+"."+fileFormat[fileFormat.length-1]);
    }
});

// 4.加载配置
var upload = multer({ storage: storage });

// 5.使用上传,upload.single()代表上传单个,参数为上传input的name值
router.post('/doAdd', 
    upload.single('input-name-value'), async (ctx, next) => {
        ctx.body = {
            // 返回文件名
            filename: ctx.req.file.filename,
            body: ctx.req.body
        }
    }
);

如果想使用一个路由,让多个地方共用上传图片 ,路由可以按如下配置即可。

router.post('/doAdd', upload.fields([
        {
            // 上传表单input的值
            name: 'avatar',
            // 数量
            maxCount: 1
        },
        {
            name: 'cover',
            maxCount: 2
        }
    ]),
    async (ctx, next) => {
        console.log(ctx.request.files);
        console.log(ctx.files);
        ctx.body = '上传成功!';
    }
);

注意,form表单加上enctype="multipart/form-data"

13.Koa操作MySQL数据库

MySQL模块是Node操作MySQL的引擎,可以在Node.js环境下对MySQL数据库进行建表,增、删、改、查等操作。

// 1.npm install --save mysql

// 2.引入模块
const mysql = require('mysql');

// 3.连接数据库
const connection = mysql.createConnection({
    host: '127.0.0.1',         // 数据库地址
    user: 'root',              // 用户名 
    password: '123456' ,       // 密码   
    database: 'my_data'        // 数据库名称   
})

// 4.执行sql语句操作数据库
connection.query('SELECT * FROM user',
     (error, results, fields) => {
        if (error) {
            console.log(error)
        };
        // 操作数据

        // 结束会话
        connection.release();
    }
);

注意,数据库会话操作执行完后,需要关闭掉,以免占用连接资源。

当有多个会话需要操作数据库时,为了避免每次会话都要重新连接,所以这时候就需要连接池管理会话。

// 1.npm install --save mysql

// 2.引入模块
const mysql = require('mysql');

// 3.创建数据池
const pool = mysql.createPool({
    host: '127.0.0.1',         // 数据库地址
    user: 'root',              // 用户名 
    password: '123456',        // 密码   
    database: 'my_data'        // 数据库名称 
})

// 4.在数据池中进行会话操作
pool.getConnection(function (err, connection) {
    // 5.执行sql语句操作数据库
    connection.query('SELECT * FROM user', 
        (error, results, fields) => {
        
            // 执行操作

            // 释放连接
            connection.release();
            // 如果有错误就抛出
            if (error) {
                throw error;
            }
        }
    )
})

14.Koa操作MongoDB数据库

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它的特点是高性能、易部署、易使用,存储数据非常方便。

// 1.npm install mongodb --save

// 2.引入mongodb下面的MongoClient
var MongoClient = require('mongodb').MongoClient;

// 3.定义数据库连接的地址,以及配置数据库
var dbUrl = 'mongodb://localhost:27017';
var dbName = 'koa';

// 4.连接数据库
MongoClient.connect(dbUrl,(err,client)=>{
    if(err){
        console.log(err);
        return;
    };
    var db = client.db(dbName);

    // 5.操作数据库
    var result = db.collection('user').find();
    result.toArray((err,docs)=>{
        console.log(docs);
    });

});

猜你喜欢

转载自blog.csdn.net/weixin_40629244/article/details/100786327