Koa - Node.js框架学习@郝晨光

作者: 郝晨光 | 来源:发表于2019-07-21 01:30 被阅读79次

    本文由郝晨光整理总结并编写,未经允许禁止转载。

    前言

    学习koa,我之前学习过express,但是在使用express的时候,还是一直使用的回调函数的方式来处理异步,现在想想真是恐怖,后来了解到koa这个框架,它相对于express来说,小巧了很多,对于异步的处理也变得更加优雅了。用官方的话来说,koa是基于Node.js平台的下一代web平台开发框架。


    Image 3.png

    正文

    1. 首先肯定要先开始一个新的项目,新建一个文件夹

    2. 在文件夹目录中,使用命令提示符执行npm init -y,初始化package.json文件;

    3. 安装

      • Koa 依赖 node - v7.6.0 或 ES2015及更高版本和 async 方法支持。
      • 先查看node的版本 node -v;
      • 然后执行npm install koa 进行安装;
    4. 使用

      • 新建index.js;
      • 先上官网上最经典的hello wrold案例;
      • const Koa = require('koa');
        const app = new Koa();
        app.use(async ctx => {
            ctx.body = 'hello word!'
        })
        app.listen(8000, () => {
            console.log('Koa server listen in http://localhost:8000/');
        })
        
      • 打开浏览器,输入localhost:8000/,就可以看到我们的页面上显示了hello world!;
      • 那我们的第一个koa应用就已经运行起来了~。
    5. 读取 HTML 页面

      • 在Koa中,我们如果要使用 HTML 应该怎么做呢?
      • 在原生nodejs或者express中,我们使用的是fs模块,然后使用回调函数,一层套一层;
      • 在Koa中,我们依旧使用fs模块,但是,要对fs模块的读取文件,进行进一步的封装;
      • 在目录下,我们新建一个 index.html,并随便写一点东西;
      • 接着开始我们渐行渐远的程序生涯;
      • const Koa = require('koa');
        const fs = require('fs');
        const app = new Koa();
        function readFile(url) {
          return new Promise((resolve, reject) => {
              fs.readFile(url, (err,file) => {
                  if(err) {
                      reject(err);
                  }else {
                      resolve(file);
                  }
              })
          })
        }
        app.use(async ctx => {
            ctx.response.type = 'html'; // 重点
            ctx.body = await readFile('index.html');
        })
        app.listen(8000, () => {
            console.log('Koa server listen in http://localhost:8000/');
        })
        // 本文由郝晨光整理总结并编写,未经允许禁止转载。
        
      • 可以看到,我们利用Promise将fs模块的readFile方法进行了二次封装,读取了本地的index.html文件
      • 重点的地方我已经标出来了,为什么要标重点呢?
      • 因为在Koa中,ctx.body默认返回的类型是text/plain;而我们要返回html文件,所以应该在返回文件之前,设置好返回类型,让客户端可以正确的接收;
      • 然后我们使用async + await组合,在数据读取完成之后,返回读取的文件;
      • 最后,打开浏览器,可以看到浏览器上输出的就是我们index.html文件中写的内容
    6. 读取静态资源

      • html文件我们已经可以正确的读取并返回了,在我们的页面上,避免不了会使用很多的静态资源,例如css、js、image、font等等;这些静态资源我们应该怎么处理呢?
      • 在这里,我们使用一个Koa的中间件koa-static
      • 在命令提示符中执行npm install koa-static
      • 接着书写我们的程序
      • // ~~ 省略 ~~
        const app = new Koa(); // 新建koa对象
        const KoaStatic = require('koa-static'); // koa静态文件读取
        app.use(KoaStatic(__dirname)); // 使用中间件
        // ~~ 省略 ~~
        
        • 接着,打开我们的浏览器,可以看到静态资源已经可以正确的请求到了;
        • 当然了,这个时候,我们可以把之前返回html文件时设置响应类型的一步省略掉了,因为koa-static已经帮我们做了这件事了。
    7. 定义路由

      • 所谓的路由,就是根据不同的请求路径,响应不同的内容;
      • 在Koa中,我们可以定义原生路由,也可以使用封装好的路由中间件;
      • 先看一下原生路由的写法吧!
      • app.use(async ctx => {
          if(ctx.request.method==='GET') { // GET请求
              switch (ctx.request.path) {
                  case '/': // 匹配默认路由
                      ctx.body = await readFile('index.html'); // 返回index.html文件
                      break;
                  case '/about': // 匹配/about路由
                      ctx.body = 'about路由页面'; // 返回about路由页面
                      break;
                  default:
                      ctx.body = await readFile('index.html'); // 默认返回index.html文件
                      break;
              }
          }else if(ctx.request.method==='POST') { // POST请求
              switch (ctx.request.path) {
                  default:
                      ctx.body = 'post 请求'; // 响应post请求
                      break;
              }
          }
        });
        //本文由郝晨光整理总结并编写,未经允许禁止转载。
        
      • 可以看到我上边写的,对请求方式以及路由进行了处理,在不同的请求方式,不同的请求路径下,执行不同的操作,响应不同的结果。


        原生路由
    8. 路由中间件

      • 我们使用原生方式定义路由,未免有些太过繁琐,并且代码不便于阅览和维护;所以,建议使用中间件的方式进行路由配置。
      • 我这里使用的是 koa-router 这个中间件,当然,koa的路由中间件不是只有这一种。
      • 在命令提示符中安装 npm install koa-router;
      • 接着,进行我们的程序生涯;
      • // ~~ 省略 ~~
        const KoaRouter = require('koa-router');
        const router = new KoaRouter();
        router.get('/',async ctx => {
            ctx.body = await readFile('index.html');
        });
        
        router.get('/about',async ctx=> {
             ctx.body = 'about路由页面'
        });
        
        router.post('/form',async ctx => {
            ctx.body = 'post请求'
        });
        
        app.use(router.routes());
        // ~~ 省略 ~~
        
      • 打开页面,并进行路由切换,可以看到没有任何问题;
      • 但是这样所有的路由配置都写在了index.js中,不利于维护,所以我们要将路由内容和公用方法提取出来。


        路由中间件
    1. 代码模块化

      • 首先对路由进行模块化
      • 在目录下新建routes文件夹,并新建home.js,用来存放关于首页的一些路由配置。
      • 将上边的路由代码单独写到home.js中,并抛出,如下:
      • home.js
      • const KoaRouter = require('koa-router');
        const router = new KoaRouter();
        
        router.get('/',async ctx => {
          ctx.body = await readFile('index.html');
        });
        
        router.get('/about',async ctx=> {
          ctx.body = 'about 路由'
        });
        
        router.post('/from',async ctx => {
           ctx.body = 'post请求'
        });
        
        module.exports = router;
        
      • index.js
      • const homeRouter = require('./routes/home');
        
        app.use(KoaStatic(__dirname));
        
        app.use(homeRouter.routes());
        
      • 在index.js中,直接引入使用即可,和原有代码没有任何区别。
      • 需要注意的是,对于功能中间件,例如koa-static这种,我们应该放在路由中间件之前。
      • 否则的话可能会出错,在路由中不能正确的读取静态文件等。
    2. 二级路由

      • 已经学会了一级路由的定义,我们趁热打铁,学习二级路由
      • 在routes目录下,新建user.js,定义用户路由
      • // user.js
        const KoaRouter = require('koa-router');
        const router = new KoaRouter({
          prefix: '/user'
        });
        
        router.get('/',async ctx => {
          ctx.body = '用户界面'
        });
        
        router.get('/:id',async ctx => {
          ctx.body = '用户详情'+ctx.params.id;
        });
        
        router.post('/login',async ctx => {
          ctx.body = '用户注册成功!'
        });
        
        module.exports = router;
        
        // index.js
        // ~~ 省略 ~~
        const userRouter = require('./routes/user');
        // ~~ 省略 ~~
        app.use(userRouter.routes());
        
      • 接着,打开浏览器,我们测试一下我们的二级路由,是没有任何问题的。


        二级路由.gif
    3. post请求处理

      • 我们都知道,使用post请求一般用来提交表单,或者修改数据等。而post请求中的数据,存放在请求体中,使用原生Node的方法的话,我们需要监听原生req对象上的data方法以及end方法,并将数据格式转化。而express中,我们有一个body-parser的插件可以使用。
      • 那么在Koa中,我们应该使用什么呢?
      • 在Koa中,我们使用 koa-bodyparser 这个中间件来处理post请求的数据
      • 在命令提示符安装 npm install koa-bodyparser;
      • 接着我们在index.js中使用这个中间件;
      • 加入下面两行代码,与原先使用koa-static中间件位置一样即可;
      • const KoaBodyParser = require('koa-bodyparser');
        
        app.use(KoaBodyParser());
        
      • 使用了这个中间件,我们就可以在ctx.request.body中拿到post请求的数据了;
      •  router.post('/login',async ctx => {
            ctx.body = ctx.request.body;
         });
        
      • 接着改造一下我们的html文件,提交一下表单;
      •   <form action="/user/login" method="post">
              姓名:<input type="text" name="name" autocomplete="off"/>
              <br>
              <input type="radio" name="sex" value="男"/>男
              <input type="radio" name="sex" value="女"/>女
              <input type="submit" value="提交form">
          </form>
        
        • 可以看到,表单提交正常,我们也正确的拿到了表单提交的数据。


          Koa提交表单
    4. CORS跨域配置

      • 在我们现在的服务器生涯中,避免不了要使用跨域,特别是CORS跨域。
      • 那么,在Koa中,我们应该怎么去设置跨域呢?
      • 我们可以使用中间件,也可以使用原生方法手写。
      • 老规矩,先看原生方法。
      •   app.use(async (ctx, next) => {
              // 允许来自所有域名请求
              ctx.set("Access-Control-Allow-Origin", "*");
              // 这样就能只允许 http://localhost:8080 这个域名的请求了
              // ctx.set("Access-Control-Allow-Origin", "http://localhost:8080");
        
              // 设置所允许的HTTP请求方法
              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字段以后,确认允许跨源请求,就可以做出回应。
        
              // Content-Type表示具体请求中的媒体类型信息
              ctx.set("Content-Type", "application/json;charset=utf-8");
        
              // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。
              // 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*";
              ctx.set("Access-Control-Allow-Credentials", true);
        
              // 该字段可选,用来指定本次预检请求的有效期,单位为秒。
              // 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
              // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证
              ctx.set("Access-Control-Max-Age", 300);
        
              /*
              CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
              Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。
              */
              // 需要获取其他字段时,使用Access-Control-Expose-Headers,
              // getResponseHeader('myData')可以返回我们所需的值
              ctx.set("Access-Control-Expose-Headers", "myData");
              await next();
          })
        
        • 原生方法来自:node.js 应答跨域请求实现(以koa2-cors为例)
        • 接着我们来看中间件吧!
        • 在Koa2中,我们使用koa2-cors这个中间件来设置CORS跨域请求。
        • 先安装 npm install koa2-cors;
        • 接着,在index.js中使用。
        • const KoaCors = require('koa2-cors');
          // ~~ 省略 ~~
          // CORS跨域
          app.use(KoaCors({
              origin: ctx => {
                  return ctx.request.header.origin;
              },
              exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
              maxAge: 5,
              credentials: true,
              allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
              allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'Origin'],
          }));
          //本文由郝晨光整理总结并编写,未经允许禁止转载。
          
        • 特别需要注意的是,在app.use()使用koa2-cors的时候,我们应该把它放在别的中间件的前边,特别是静态资源处理和路由处理中间件的前边,这样可以保证我们在跨域请求静态资源的时候不会出问题。

    如果本文对您有帮助,可以看看本人的其他文章:
    前端常见面试题(十三)@郝晨光
    前端常见面试题(十二)@郝晨光
    前端常见面试题(十一)@郝晨光

    结言
    感谢您的查阅,本文由郝晨光整理并总结,代码冗余或者有错误的地方望不吝赐教;菜鸟一枚,请多关照

    相关文章

      网友评论

        本文标题:Koa - Node.js框架学习@郝晨光

        本文链接:https://www.haomeiwen.com/subject/ognflctx.html