Express / Koa

Express

通过 express 我们可以轻松的构建一个 web 服务器 例如以下代码就在 3000 端口创建了一个 web 服务器

const express = require('express');

const app = express();

app.listen(3000, () => {
  console.log('start');
});

在我的理解中 express 就是一个对一系列中间件调用的函数

比如常见的处理 get 和 post 请求的方法都是中间件的调用

当我们 require express 的时候 本质上是导入了一个函数

可以查看源码 以下是入口文件 express.js 的截取代码

exports = module.exports = createApplication;

/**
 * Create an express application.
 *
 * @return {Function}
 * @api public
 */

function createApplication() {
  var app = function (req, res, next) {
    app.handle(req, res, next);
  };

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, false);

  // expose the prototype that will get set on requests
  app.request = Object.create(req, {
    app: { configurable: true, enumerable: true, writable: true, value: app },
  });

  // expose the prototype that will get set on responses
  app.response = Object.create(res, {
    app: { configurable: true, enumerable: true, writable: true, value: app },
  });

  app.init();
  return app;
}

express.js 导出了一个函数 createApplication 也就是我们 require 导入的函数

在这个函数中初始化了一个 app 的函数 拥有 req / res / next 等参数 并通过 minin 这个函数混入了很多属性到 app 中

具体的 API 可查阅 http://expressjs.com/en/4x/api.html

中间件

传递给 express 的一个回调函数 中间件位于请求和响应之间 所以它可以

• 执行任何代码

• 更改请求和响应对象

• 结束请求响应周期 res.end

• 调用下一个中间件

我们可以使用 app.use 的方法在全局注册中间件 这样所有的路由都会匹配到这个中间件

也可以在具体的路由中使用中间件 例如如下

托管静态文件

get

get 请求的参数都在 url 中 我们可以通过 query 和 params 这两种形式来获取

post

post 请求的参数在 body 但是如果我们直接打印 body 是看不到任何结果的

这是需要引入 body-parser 这个库

这个库和不同版本的 express 集成情况如下

  • 3.x 内置

  • 4.x 分离

  • 4.16 内置函数

所以如果是 4.x 4.16 以前的版本 我们需要手动安装这个第三方库

文件上传

如果使用原生的方法实现文件上传 后台处理起来会非常麻烦 因为二进制流中不仅有文件的信息 还有 header 的一些相关信息 我们可以打印一些相关的数据来看一下

然后我们访问 localhost:8080/upload 并上传一个文件 就可以看到打印台输出了一段乱码的二进制流和请求头的 headers

headers 信息如下

如果仔细对比 你就会发现二进制流中包含了 header 中的 content-type 中的 boundary 还有文件的 mime 类型等 如果不加处理的直接使用 fs 模块将这个二进制流写入到文件中 那么最终文件肯定无法被正确解析

如果引入 multer 模块 那么文件上传功能就会变得很简单了

例如 我们需要用户上传头像 代码如下

上述方法演示的是单文件上传 req.file 中存储了文件的相关信息 如下

如果需要支持多文件上传 例如还需要上传用户背景图 代码可改写成如下

如果是需要多张背景图上传 同一个 field 则可以用 array 的写法 第二个参数为最大可上传的数量

如果超过了最大数量 multer 会返回 MulterError: Unexpected field

router

如果将所有的请求处理都放在 index.js 中处理 那么 index.js 的代码就会变的很臃肿 这个时候我们可以使用路由来拆分我们的代码

例如项目中有一个用户模块 实现增删改查的功能

我们就可以新建一个 user.js 文件

然后在 index.js 中导入这个路由

DEMO

express 中的中间件都是同步代码 即一个中间件执行完毕才会去执行另一个中间件中的代码

例如我们有如下功能要实现 在 ABC 三个中间件中 分别追加给 message 追加内容 然后在 A 中间件中输出结果

next 函数会在中间件栈中找到下一个中间件并执行 所以 middlewareA 中的 res.end 会在所有中间件都执行完毕后才执行 输入 ABC

那么 如果我们在第三个中间件中用定时器来模拟异步操作 最终的结果会怎么样呢

将 middlewareC 改造成如下

通过访问 8000 端口 我们可以看到这次最终的输出变成了 AB 由此可见

中间件中的代码都是同步调用的

而这 也是 express 面对异步场景下的一种无力 而 koa 则不一样

Koa

koa 支持 async 和 await 的用法 这就意味着在 koa 中可以抛去 express 中回调函数的写法 用一种更优雅的方式来解决异步场景

基本使用

与 express 不同的是 koa 导出的不是函数 而是一个名为 Application 的对象

所以在使用上我们只需要 new 一个实例即可 其他用法和 Express 基本相似

路由

这里我们借助第三方模块 koa-router 因为 koa 本身很纯净 基本所有功能都要通过第三方插件来实现

新建一个 user.js 的路由模块 Koa 将 express 中的 request 和 response 都合成到了上下文对象 context 中 简写为 ctx

然后在 index 中引入 user.js

处理请求

koa 中需要引入 koa-bodyparser 来解析 json 和 urlencoded

注意 koa-bodyparser 中间件需要最先被使用

异步处理

重新回到 express 中的 demo 如果想在 koa 中处理异步操作就变的非常简单了

洋葱模型

洋葱模型其实不是什么高大尚的概念 通过下图我们不难发现

所有中间件都会被 request 访问两次 就像剥洋葱一样 这就是洋葱模型

express 对比 koa

  • express 是完整和强大的,其中帮助我们内置了非常多好用的功能;

  • koa 是简洁和自由的,它只包含最核心的功能,并不会对我们使用其他中间件进行任何的限制。 甚至是在 app 中连最基本的 get、post 都没有给我们提供;我们需要通过自己或者路由来判断请求方式或者其他功能

  • koa 中间件支持 async await

Last updated

Was this helpful?