本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议 》和 《 阿里云开发者社区知识产权保护指引 》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单 进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

koa 是一个轻量级(对比express 区别请查看我上一篇文章)web 服务的框架,里面没有自带的中间件,因此我们需要来自己实现中间件或者使用第三方人家开发好的中间件。


在写一个服务端应用的时候,一般都会使用到作为静态服务器,node 搭建静态服务请查看我以前文章,express中间件实现静态资源服务的话可以使用 express.static()来实现,那么koa如何实现呢? 其实对于一个静态服务的话,只要你会node这个底层的原理,express 或者 koa 这些上层应用都是基于底层原理来的


步骤


1.获取路径的path,作为服务的话,需要提供一个根路径和静态资源的目录;

2.判断提供的path 是文件夹、文件或者不存在;

3.去读指定文件的内容,设置响应头;

4.给浏览器返回一个文件内容


代码如下:


const path = require('path');
const fs = require('fs')
// 这里使用mime 这个库来判断文件的类型,从而设置响应头
const mime = require('mime');
 * 获取文件名称
 * @param {*} filename 
 * @param {*} root 
 * @returns 
async function getFileName(filename, root) {
  // 获取文件的全路径,node 中规定,如果是以 \ 开头,就是绝对路径是一个跟路径
  const fullName = path.resolve(root, filename.substring(1));
  // 判断当前路径是文件还是文件夹或者不存在
  try {
    const fsStat = await fs.promises.stat(fullName);
    if (fsStat.isDirectory()) {
      // 如果是目录的话,那就需要补上默认文件
      const adtFileName = path.join('/index.html');
      return await getFileName(adtFileName, root);
    } else {
      return fullName;
  } catch (error) {
    return null;
 * 读取静态资源目录
 * @param {*} root 
 * @returns 
module.exports = function (root) {
  return async (ctx, next) => {
    if (ctx.method !== 'GET') {
      await next();
      return;
    const filename = await getFileName(ctx.path, root)
    if (!filename) {
      // 文件不存在
      await next();
      return;
    // 根据文件的后缀名响应文件
    const mimeType = mime.getType(filename);
    // 返回一个文件流
    ctx.body = fs.createReadStream(filename);
    // 设置文件的类型
    ctx.type = mimeType;
    await next();
复制代码


手动实现 connect-history-api-fallback 中间件


单页应用中,我们会使用路由的history模式,刷新浏览器的时候,需要重新回到index.html页面,这个中间件在koa 中怎么实现呢?connect-history-api-fallback官网告诉我们有几个匹配规则,如下:


规则


39ac45f7b6ae2c1f251f5a637aedd856.png


``


1.只能匹配get请求


2.请求的header 的文件类型是text/html


3.请求不是一个重定向的请求,请求的path中不能包含·,因为带点的就意味着是请求的是一个静态资源,如,js,css,图片等


代码


module.exports = async function (ctx, next) {
  // 判断当前请求是不是get请求,请求的路径中是否包含.,并且请求头的类型是html文件
  if (ctx.method === "GET" &&
    ctx.headers.accept.includes("text/html") &&
    !ctx.path.includes(".")) {
    ctx.path = '/index.html'
  await next();
复制代码


这个地方有一个小插曲,那就是,在使用静态资源的时候,需要使用绝对路径,不能使用相对路径,不然静态资源会不对,你可以打开单页应用打的包的路径绝对是以/开头的(前提是history模式的路由,hash路由也不会使用这个插件)。


550b5443819f1f7de537d61d0638834b.png


常用KOA中间件


Koa中间件 功能
@koa/router 官方中间件。借鉴了 koa-router
用于处理路由的中间件,用法类似 express.Router
koa-bodyparser 解析请求体的中间件,支持
x-www-form-urlencoded, application/json格式的请求体
koa-views 渲染模板引擎的中间件,一般用于传统的服务端渲染
koa-static 用于搭建静态资源服务器的中间件
koa-static-cache 实现了http缓存的静态资源中间件
koa-session session中间件
koa-jwt 支持jwt的中间件
koa-compress 支持gzip动态压缩的中间件
koa-logger 日志记录中间件
@koa/cors 官方中间件。支持CORS跨域的中间件
@koa/multer 官方中间件,借鉴了 koa-multer
用户处理文件上传的中间件
koa-connect 将express或connect中间件转换为koa中间件
http-proxy-middleware 代理中间件(这个中间件需要使用 koa-cennect 后才可以使用)
connect-history-api-fallback 单页应用支持
(这个中间件需要使用 koa-cennect 后才可以使用)
koa-convert 用于将旧版本的koa中间件转换为koa2中间件
一转眼,都2023年了,你是否在满意的公司?拿着理想的薪水? 虽然“钱多、事少、离家近”的工作可能离技术人比较远,但是找到一份合适的工作,其实并不像想象中那么难。但是,有些技术人确实是认真努力工作,但在面试时表现出的能力水平却不足以通过面试,或拿到高薪,其实不外乎以下 2 个原因: 第一,“知其然不知其所以然”。做了多年技术,开发了很多业务应用,但似乎并未思考过种种技术选择背后的逻辑。所以,他无法向面试官展现出自己未来技术能力的成长潜力。面试官也不会放心把具有一定深度的任务交给他。 第二,知识碎片化,不成系统。在面试中,面试者似乎无法完整、清晰地描述自己所开发的系统,或者使用的相关技术。
愿天堂没有BUG(公众号同名)