• Koa 框架常用知识点整理


    img

    简介

    Koa 就是一种简单好用的 Web 框架。它的特点是优雅、简洁、表达力强、自由度高。本身代码只有1000多行,所有功能都通过插件实现。

    学前准备

    检查Nodejs版本

    打开cmd命令行窗口node -v
    注意:Koa 必须使用 7.6 以上的版本。如果你的版本低于这个要求,就要先升级 Node。
    配套案例

    一、基本用法

    1.1 三行代码架设HTTP服务

    npm install koa

      const Koa = require('koa');
      const app = new Koa();
      app.listen(3000);
    

    1.2 Context 对象

    Koa 提供一个 Context 对象,表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。通过加工这个对象,就可以控制返回给用户的内容。

    Context 对象所包含的:

    context

    const Koa = require('koa')
    const app = new Koa()
    
    app.use((ctx, next) => {
        //ctx是整个应用的上下文,包含常用的request、response
        //ctx.response代表 HTTP Response。同样地,ctx.request代表 HTTP Request。
        //ctx.response.body可以简写成ctx.body
        ctx.response.body = 'hello world'
    })
    
    app.listen(3000)
    

    1.3 HTTP Response 的类型

    Koa 默认的返回类型是text/plain (纯文本的形式),如果想返回其他类型的内容,可以先用ctx.request.accepts判断一下,客户端希望接受什么数据(根据 HTTP Request 的Accept字段),然后使用ctx.response.type指定返回类型。

    const Koa = require('koa')
    const app = new Koa()
    //声明一个main中间件,如果你急于了解中间件可以跳转到(三)
    const main = (ctx,next) =>{
    if (ctx.request.accepts('json')) {
        ctx.response.type = 'json';
        ctx.response.body = { data: 'Hello World' };
      } else if (ctx.request.accepts('html')) {
        ctx.response.type = 'html';
        ctx.response.body = '<p>Hello World</p>';
      } else if (ctx.request.accepts('xml')) {
        ctx.response.type = 'xml';
        ctx.response.body = '<data>Hello World</data>';
      } else{
        ctx.response.type = 'text';
        ctx.response.body = 'Hello World';
      };
    }; //直接运行页面中会显示json格式,因为我们没有设置请求头,所以每一种格式都是ok的。   
    
    app.use(main)//app.use()用来加载中间件。
    app.listen(3000)
    

    1.4 网页模板

    实际开发中,返回给用户的网页往往都写成模板文件。我们可以让 Koa 先读取模板文件,然后将这个模板返回给用户。

    关于fs.createReadStream

    const fs = require('fs');
    const Koa = require('koa');
    const app = new Koa();
    
    const main = ctx => {
        ctx.response.type = 'html';
        ctx.response.body = fs.createReadStream('./data/index.html');
    };
    
    app.use(main);
    app.listen(3000);
    

    二、路由

    2.1 原生路由

    const Koa = require('koa')
    const app = new Koa()
    app.use((ctx, next) => {
        if (ctx.request.url == '/') {//通过ctx.request.url获取用户请求路径
            ctx.body = '<h1>首页</h1>'
        } else if (ctx.request.url == '/my') {
            ctx.body = '<h1>联系我们</h1>'
        } else {
            ctx.body = '<h1>404 not found</h1>'
        }
    })
    app.listen(3000)
    

    2.2 koa-router 模块路由

    npm中的koa-route

    npm install koa-router

    const Koa = require('koa')
    const Router = require('koa-router')
    
    const app = new Koa()
    const router = new Router()
    
    app.use(router.routes()).use(router.allowedMethods());
    //routes()返回路由器中间件,它调度与请求匹配的路由。
    //allowedMethods()处理的业务是当所有路由中间件执行完成之后,若ctx.status为空或者404的时候,丰富response对象的header头.
    
    router.get('/',(ctx,next)=>{//.get就是发送的get请求
      ctx.response.body = '<h1>首页</h1>'
    })
    router.get('/my',(ctx,next)=>{
      ctx.response.body = '<h1>联系我们</h1>'
    })
    
    app.listen(3000)
    

    2.3 静态资源

    如果网站提供静态资源(图片、字体、样式表、脚本......),为它们一个个写路由就很麻烦,也没必要koa-static模块封装了这部分的请求。请看下面的例子

    npm中的koa-static

    npm install koa-staic

    const Koa = require('koa');
    const app = new Koa();
    const path = require('path');
    const serve = require('koa-static');
    
    const main = serve(path.join(__dirname));
    
    app.use(main);
    app.listen(3000);
    

    访问 http://localhost:3000/data/index.html,在浏览器里就可以看到这个文件的内容。

    2.4 重定向跳转

    有些场合,服务器需要重定向访问请求。比如,用户登陆以后,将他重定向到登陆前的页面。ctx.response.redirect()方法可以发出一个跳转,将用户导向另一个路由。

    const Koa = require('koa');
    const Router = require('koa-router');
    const app = new Koa();
    const router = new Router()
    
    app.use(router.routes()).use(router.allowedMethods());
    
    router.get('/cdx',(ctx,next)=>{
      ctx.response.redirect('/');//发出一个跳转,将用户导向另一个路由。
    })
    router.get('/',(ctx,next)=>{
       ctx.body = 'Hello World';
    })
    
    app.listen(3000);
    

    访问 http://localhost:3000/cdx,浏览器会将用户导向根路由。

    三、中间件

    3.1 Logger功能

    Koa 的最大特色,也是最重要的一个设计,就是中间件。为了理解中间件,我们先看一下 Logger (打印日志)功能的实现。

    ./logger/koa-logger.js

    module.exports = (ctx, next) => {
        console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);//自定义
    }   
    

    ./logger.js

    const Koa = require('koa')
    const koaLogger = require('./logger/koa-logger')
    const app = new Koa();
    
    app.use(koaLogger)
    app.listen(3000)
    

    打印结果logger

    3.2中间件的概念

    处在 HTTP Request 和 HTTP Response 中间,用来实现某种中间功能的函数,就叫做"中间件"。

    基本上,Koa 所有的功能都是通过中间件实现的,前面例子里面的main也是中间件。每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是next函数。只要调用next函数,就可以把执行权转交给下一个中间件。

    多个中间件会形成一个栈结构,以"先进后出"的顺序执行。

    1. 最外层的中间件首先执行。
    2. 调用next函数,把执行权交给下一个中间件。
    3. ...
    4. 最内层的中间件最后执行。
    5. 执行结束后,把执行权交回上一层的中间件。
    6. ...
    7. 最外层的中间件收回执行权之后,执行next函数后面的代码。

    例子:

    const Koa = require('koa');
    const app = new Koa();
    
    app.use((ctx, next)=>{
      console.log('>> one');
      next();
      console.log('<< one');
    })
    
    app.use((ctx, next)=>{
      console.log('>> two');
      next(); 
      console.log('<< two');
    })
    app.use((ctx, next)=>{
      console.log('>> three');
      next();
      console.log('<< three');
    })
    app.listen(3000);
    

    输出结果:

    中间件

    如果中间件内部没有调用next函数,那么执行权就不会传递下去。

    3.4 异步中间件

    迄今为止,所有例子的中间件都是同步的,不包含异步操作。如果有异步操作(比如读取数据库),中间件就必须写成async 函数。

    npm install fs.promised

    npm中的fs.promised

    const fs = require('fs.promised');
    const Koa = require('koa');
    const app = new Koa();
    
    const main = async function (ctx, next) {
        ctx.response.type = 'html';
        ctx.response.body = await fs.readFile('./data/index.html', 'utf8');
    };
    
    app.use(main);
    app.listen(3000);
    

    上面代码中,fs.readFile是一个异步操作,必须写成await fs.readFile(),然后中间件必须写成 async 函数。

    app.use(async(ctx, next)=>{
      ctx.body = '1'
      //延时2秒执行下一个中间件,这样是没用的,因为是异步函数
      setTimeout(()=>{
          next()
      },2000)
      ctx.body += '2'
    })
    
    app.use(async(ctx, next)=>{
      ctx.body += '3'
      next()
      ctx.body += '4'
    })
    
    
    server.js正确做法
    function delay(){
        return new Promise((reslove,reject)=>{
          setTimeout(()=>{
            reslove()
          },1000)
        })
    }
    
    app.use(async(ctx, next)=>{
      ctx.body = '1'
      await next()
      ctx.body += '2'
    })
    
    app.use(async(ctx, next)=>{
      ctx.body += '3'
      await delay()
    await next()
      ctx.body += '4'
    })
    
    

    3.5 中间件的合成

    koa-compose 模块可以将多个中间件合成一个。

    npm中的koa-compose

    npm install koa-compose

    const Koa = require('koa');
    const compose = require('koa-compose');
    const app = new Koa();
    
    const logger = (ctx, next) => {
      console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
      next();
    }
    
    const main = ctx => {
      ctx.response.body = 'Hello World';
    };
    
    const middlewares = compose([logger, main]);//合成中间件
    
    app.use(middlewares);//加载中间件
    app.listen(3000);
    

    输出结果:先打印日志,再在页面中显示Hello World

    四、处理错误

    4.1 500错误

    如果代码运行过程中发生错误,我们需要把错误信息返回给用户。HTTP 协定约定这时要返回500状态码。Koa 提供了ctx.throw()方法,用来抛出错误,ctx.throw(500)就是抛出500错误。

    const Koa = require('koa');
    const app = new Koa();
    
    const main = ctx => {
      ctx.throw(500);//这个时候你访问首页会报一个500的错误(内部服务器错误)服务器会报错
    };
    
    app.use(main);
    app.listen(3000);
    

    500

    4.2 404错误

    如果将ctx.response.status设置成404,就相当于ctx.throw(404),返回404错误。

    const Koa = require('koa');
    const app = new Koa();
    
    const main = ctx => {
      ctx.response.status = 404;//response返回的状态码就是404
      ctx.response.body = 'Page Not Found';//让页面中显示该内容,服务器不不报错
    };
    
    app.use(main);
    app.listen(3000);
    

    404

    4.3 处理错误的中间件

    为了方便处理错误,最好使用try...catch将其捕获。但是,为每个中间件都写try...catch太麻烦,我们可以让最外层的中间件,负责所有中间件的错误处理。

    const Koa = require('koa');
    const app = new Koa();
    
    const handler = async (ctx, next) => {
      try {
        await next();//执行下个中间件
      } catch (err) {
         //如果main中间件是有问题的会走这里
        ctx.response.status = err.statusCode || err.status || 500;
        ctx.response.body = {
          message: err.message//把错误信息返回到页面
        };
      }
    };
    
    const main = ctx => {
      ctx.throw(500);//如果这里是没有问题的就正常执行,如果有问题会走catach
    };
    
    app.use(handler);
    app.use(main);
    app.listen(3000);
    

    解决错误

    4.4 errors事件监听

    运行过程中一旦出错,Koa 会触发一个error事件。监听这个事件,也可以处理错误。

    const Koa = require('koa');
    const app = new Koa();
    
    const main = ctx => {
      ctx.throw(500);
    };
    app.on('error', (err, ctx) => {
       //如果有报错的话会走这里
      console.error('server error', err);//err是错误源头
    });
    
    app.use(main);
    app.listen(3000);
    

    错误监听

    4.5 释放error事件

    需要注意的是,如果错误被try...catch捕获,就不会触发error事件。这时,必须调用ctx.app.emit(),手动释放error事件,才能让监听函数生效。

    const Koa = require('koa');
    const app = new Koa();
    
    const handler = async (ctx, next) => {
        try {
            await next();
        } catch (err) {
            ctx.response.status = err.statusCode || err.status || 500;
            ctx.response.type = 'html';
            ctx.response.body = '<p>有问题,请与管理员联系</p>';
            ctx.app.emit('error', err, ctx);//释放error事件
        }
    };
    
    const main = ctx => {
        ctx.throw(500);
    };
    
    app.on('error', function (err) {
        //释放error事件后这里的监听函数才可生效
        console.log('错误', err.message);
        console.log(err);
    });
    
    app.use(handler);
    app.use(main);
    app.listen(3000);
    

    上面代码main函数抛出错误,被handler函数捕获。catch代码块里面使用ctx.app.emit()手动释放error事件,才能让监听函数监听到。

    五、Web App的功能

    ctx.cookies用来读写 Cookie。

    const Koa = require('koa');
    const app = new Koa();
    
    const main = function(ctx) {
        				//读取cookie//没有返回0
      const n = Number(ctx.cookies.get('view') || 0) + 1;
      ctx.cookies.set('view', n);//设置cookie
      ctx.response.body = n + ' views';//显示cookie
    }
    
    app.use(main);
    app.listen(3000);
    

    5.2 表单

    Web 应用离不开处理表单。本质上,表单就是 POST 方法发送到服务器的键值对。koa-body模块可以用来从 POST 请求的数据体里面提取键值对。

    npm中的koa-body

    npm install koa-body

    const Koa = require('koa');
    const koaBody = require('koa-body');
    const app = new Koa();
    
    const main = async function (ctx) {
        const body = ctx.request.body;
        if (!body.name){
            ctx.throw(400, '.name required')
        };
        ctx.body = { name: body.name };
    };
    
    app.use(koaBody());
    app.use(main);
    app.listen(3000);
    

    模拟post

    上面代码使用 POST 方法向服务器发送一个键值对,会被正确解析。如果发送的数据不正确,就会收到错误提示。

    5.3 文件上传

    koa-body模块还可以用来处理文件上传。

    ./demo/文件上传.js

    const Koa = require('koa');
    const koaBody = require('koa-body');
    const Router = require('koa-router');
    const fs = require('fs');
    const path = require('path');
    const router = new Router()
    const app = new Koa();
    
    app.use(koaBody({
        multipart: true,//解析多部分主体,默认false
        formidable: {
            maxFileSize: 200 * 1024 * 1024    // 设置上传文件大小最大限制,默认2M
        }
    }));
    
    app.use(router.routes()).use(router.allowedMethods());
    
    router.post('/uploadfile', (ctx, next) => {
        // 上传单个文件
        const file = ctx.request.files.file; // 获取上传文件
        // 创建可读流
        const reader = fs.createReadStream(file.path);
        let filePath = path.join(__dirname, 'data/') + `/${file.name}`;
        // 创建可写流
        const upStream = fs.createWriteStream(filePath);
        // 可读流通过管道写入可写流
        reader.pipe(upStream);
        return ctx.body = "上传成功!";
    });
    
    app.listen(3000)
    

    ./demo/web/index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
        <form action="http://127.0.0.1:3000/uploadfile" method="post" enctype="multipart/form-data">
        <input type="file" name="file" id="file" value="" multiple="multiple" />
        <input type="submit" value="提交"/>
    </form>
    </body>
    </html>
    

    nodejs中的fs文件系统模块,可以帮你读文件写文件,但是不会帮你创建文件夹。


    本文思路来源http://www.ruanyifeng.com/blog/2017/08/koa.html

  • 相关阅读:
    gedit保存出现The file has been changed since reading it!!! Do you really want to write to it (y/n)?y
    8051处理器与Quatus Signal TypeⅡ进行板级调试
    DesignWare I2C模块的验证
    Denali NAND FLASH控制器的验证
    openocd安装与调试
    SPI协议介绍
    CAN协议学习(二)MCAN控制器介绍
    CAN协议学习(一)协议介绍
    Remosaic技术学习
    ISP算法:深入聊聊lens shading
  • 原文地址:https://www.cnblogs.com/rope/p/10623678.html
Copyright © 2020-2023  润新知