详解KOA2如何手写中间件(装饰器模式)(3)

const Koa = require("koa"); const views = require("koa-views"); const path = require("path"); const app = new Koa(); // 使用中间件 app.use(views(path.resolve(__dirname, "views"), { extension: "ejs" })); app.use(async (ctx, next) => { await ctx.render("index", { name: "panda", age: 20, arr: [1, 2, 3] }); }); app.listen(3000);

可以看出我们使用了 koa-views 中间件后,让 ctx 上多了 render 方法帮助我们实现对模板的渲染和响应页面,就和直接使用 ejs 自带的 render 方法一样,并且从用法可以看出 render 方法是异步执行的,所以需要使用 await 进行等待,接下来我们就来模拟实现一版简单的 koa-views 中间件。

文件:my-koa-views.js

const fs = require("fs"); const path = require("path"); const { promisify } = require("util"); // 将读取文件方法转换成 Promise const readFile = promisify(fs.radFile); // 到处中间件 module.exports = function (dir, options) { return async (ctx, next) => { // 动态引入模板依赖模块 const view = require(options.extension); ctx.render = async (filename, data) => { // 异步读取文件内容 let tmpl = await readFile(path.join(dir, `${filename}.${options.extension}`), "utf8"); // 将模板渲染并返回页面字符串 let pageStr = view.render(tmpl, data); // 设置响应类型并响应页面 ctx.set("Content-Type", "text/html;charset=utf8"); ctx.body = pageStr; } // 继续向下执行 await next(); } }

挂在 ctx 上的 render 方法之所以是异步执行的是因为内部读取模板文件是异步执行的,需要等待,所以 render 方法为 async 函数,在中间件内部动态引入了我们使的用模板,如 ejs ,并在 ctx.render 内部使用对应的 render 方法获取替换数据后的页面字符串,并以 html 的类型响应。

koa-static 中间件模拟

下面是 koa-static 中间件的用法,代码使用的依赖如下,使用前需安装。

npm install koa koa-static mime

koa-static 具体用法如下:

koa-static 的用法

const Koa = require("koa"); const static = require("koa-static"); const path = require("path"); const app = new Koa(); app.use(static(path.resolve(__dirname, "public"))); app.use(async (ctx, next) => { ctx.body = "hello world"; }); app.listen(3000);

通过使用和分析,我们知道了 koa-static 中间件的作用是在服务器接到请求时,帮我们处理静态文件,如果我们直接访问文件名的时候,会查找这个文件并直接响应,如果没有这个文件路径会当作文件夹,并查找文件夹下的 index.html ,如果存在则直接响应,如果不存在则交给其他中间件处理。

文件:my-koa-static.js

const fs = require("fs"); const path = require("path"); const mime = require("mime"); const { promisify } = require("util"); // 将 stat 和 access 转换成 Promise const stat = promisify(fs.stat); const access = promisify(fs.access) module.exports = function (dir) { return async (ctx, next) => { // 将访问的路由处理成绝对路径,这里要使用 join 因为有可能是 / let realPath = path.join(dir, ctx.path); try { // 获取 stat 对象 let statObj = await stat(realPath); // 如果是文件,则设置文件类型并直接响应内容,否则当作文件夹寻找 index.html if (statObj.isFile()) { ctx.set("Content-Type", `${mime.getType()};charset=utf8`); ctx.body = fs.createReadStream(realPath); } else { let filename = path.join(realPath, "index.html"); // 如果不存在该文件则执行 catch 中的 next 交给其他中间件处理 await access(filename); // 存在设置文件类型并响应内容 ctx.set("Content-Type", "text/html;charset=utf8"); ctx.body = fs.createReadStream(filename); } } catch (e) { await next(); } } }

上面的逻辑中需要检测路径是否存在,由于我们导出的函数都是 async 函数,所以我们将 stat 和 access 转化成了 Promise,并用 try...catch 进行捕获,在路径不合法时调用 next 交给其他中间件处理。

koa-router 中间件模拟

在 Express 框架中,路由是被内置在了框架内部,而 Koa 中没有内置,是使用 koa-router 中间件来实现的,使用前需要安装。

npm install koa koa-router

koa-router 功能非常强大,下面我们只是简单的使用,并且根据使用的功能进行模拟。

koa-router 的简单用法

const Koa = require("Koa"); const Router = require("koa-router"); const app = new Koa(); const router = new Router(); router.get("/panda", (ctx, next) => { ctx.body = "panda"; }); router.get("/panda", (ctx, next) => { ctx.body = "pandashen"; }); router.get("/shen", (ctx, next) => { ctx.body = "shen"; }) // 调用路由中间件 app.use(router.routes()); app.listen(3000);

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/24763fb3ff4d0edb870965d4e62b2e48.html