koa + http-proxy-middleware 搭建一个带转发的静态服务器

news2024/9/23 1:33:39

背景

由于工作中碰到写普通页面(未使用脚手架),需要发起接口请求,但普通页面又无法对接口发起正常请求,故编写一个Koa搭建的带转发功能的静态服务器。

起步

  1. 新建一个文件夹,在文件夹下打开 cmd 或者 git 之类的窗口,输入 pnpm init ,然后输入一路输入各种基础信息后,再次输入pnpm add koa --save ,等待安装完成后,就可以创建简单的koa服务器了。

  2. 在这里,我使用的是esmodule的形式,所以,js 文件名都是以 mjs 结尾的。文件目录结构如下:
    在这里插入图片描述

  3. 要让 koa 能够展示普通html文件,就需要 pnpm add koa-static --save,这个是 koa 文件服务器的中间件,有了它就可以很方便的读取服务器目录下的文件了
    到这一步,koa(文件名我命名为koaServer.mjs)的代码如下:

import Koa from 'koa'
import path from 'path'
import { fileURLToPath } from 'node:url'  // fileURLToPath 作用是将文件 URL 转换为平台特定的本地文件路径
import staticServer from 'koa-static'

const PORT = 3001
const app = new Koa()

// import.meta: 为 import 命令添加了一个元属性import.meta,返回当前模块的元信息
const __fileUrl = fileURLToPath(import.meta.url) // 返回当前模块(js)的 URL 路径
const __dirname = path.dirname(__fileUrl) // 组装当前静态文件所在的目录

// staticServer传入当前静态目录路径
app.use(staticServer(path.join(__dirname, './static')))

app.listen(PORT, () => {
    console.log('listen: ' + PORT)
})

在根目录下,创建static文件夹,写一个简单的index.html文件夹,然后在控制台输入 node koaServer.mjs 启动服务,然后浏览器输入localhost:3001/index.html 可以看到页面被打开
DEMO

  1. 页面已经能打开,接下来要做的就是加入请求转发功能,一般说到转发中间件,常用的就是就是 http-proxy-middleware这个插件。pnpm 安装此插件,并引入到koaServer.js文件。根据npmjs上面的文档,将文档上面的示例复制过来
import Koa from 'koa'
import path from 'path'
import { fileURLToPath } from 'node:url'  // fileURLToPath 作用是将文件 URL 转换为平台特定的本地文件路径
import staticServer from 'koa-static'

import { createProxyMiddleware } from 'http-proxy-middleware'

const PORT = 3001
const app = new Koa()

// import.meta: 为 import 命令添加了一个元属性import.meta,返回当前模块的元信息
const __fileUrl = fileURLToPath(import.meta.url) // 返回当前模块(js)的 URL 路径
const __dirname = path.dirname(__fileUrl) // 组装当前静态文件所在的目录

// staticServer传入当前静态目录路径
app.use(staticServer(path.join(__dirname, './static')))

const exampleProxy = createProxyMiddleware({
  pathFilter: '/api', // 匹配以/api开头的路径
  pathRewrite: {
    '/api': ''
  }, // 把/api去除掉
  target: 'https://jsonplaceholder.typicode.com', // target host with the same base path
  changeOrigin: true, // needed for virtual hosted sites
})

app.use(exampleProxy)

app.listen(PORT, () => {
    console.log('listen: ' + PORT)
})

然后 node koaServer.mjs 运行起来,直接就报错
在这里插入图片描述
然后经过搜索得知,http-proxy-middleware 是 express 社区的插件,所以需要再安装 koa2-connect 这个依赖包,koa-connect作用就是在 Koa2 中可以使用 Express 社区的中间件,起到了一个中转或者适配的作用。执行 pnpm add koa-connect --save,安装成功后,在 koaServer.mjs 中引用进来。修改后的代码如下

import Koa from 'koa'
import path from 'path'
import { fileURLToPath } from 'node:url'  // fileURLToPath 作用是将文件 URL 转换为平台特定的本地文件路径
import staticServer from 'koa-static'

import { createProxyMiddleware } from 'http-proxy-middleware'
import Koa2Connect from 'koa2-connect'

const PORT = 3001
const app = new Koa()

// import.meta: 为 import 命令添加了一个元属性import.meta,返回当前模块的元信息
const __fileUrl = fileURLToPath(import.meta.url) // 返回当前模块(js)的 URL 路径
const __dirname = path.dirname(__fileUrl) // 组装当前静态文件所在的目录

// staticServer传入当前静态目录路径
app.use(staticServer(path.join(__dirname, './static')))

const exampleProxy = createProxyMiddleware({
  pathFilter: '/api', // 匹配以/api开头的路径
  pathRewrite: {
    '/api': ''
  }, // 把/api去除掉
  target: 'https://jsonplaceholder.typicode.com', // target host with the same base path
  changeOrigin: true, // needed for virtual hosted sites
})


app.use(Koa2Connect(exampleProxy))

app.listen(PORT, () => {
    console.log('listen: ' + PORT)
})

再次启动脚本运行,然后在 static/index.html 中,写接口请求
在这里插入图片描述

在控制台中就可以看到打印了请求的响应了在这里插入图片描述

改进

到目前为止,这个 koa2 搭建的简易服务器就可以使用了,但是在日常工作中,转发的请求可能不止一个,并且想要在控制台打印一些信息(响应结果等),就需要做进一步的修改了。

  1. 将转发请求的使用独立出来 proxy.mjs,变成可配置的,也就是批量增加多个 createProxyMiddleware 。http-proxy-middleware 的 createProxyMiddleware 方法每次只能转发到一个目标,故需要安装 koa-compose 来整合多个 createProxyMiddleware ,安装此插件,代码修改如下
import { createProxyMiddleware } from 'http-proxy-middleware'
import Koa2Connect from 'koa2-connect'
import koaCompose from 'koa-compose'

const proxies = [
  {
    pathFilter: '/api1', // 匹配以/api1开头的路径
    pathRewrite: {
      '/api1': ''
    }, // 把/api去除掉
    target: 'https://jsonplaceholder.typicode.com', // target host with the same base path
    changeOrigin: true, // needed for virtual hosted sites
  },
  {
    pathFilter: '/api2',
    pathRewrite: {
      '/api2': ''
    },
    target: 'https://jsonplaceholder.typicode.com', // target host with the same base path
    changeOrigin: true, // needed for virtual hosted site
  },
]

// 导出所有多个转发中间件
export default koaCompose(
  proxies.map(proxy => Koa2Connect(createProxyMiddleware(proxy)))
)

然后在 koaServer.mjs 中引入 proxy.mjs,app.use() 一下就可以了,经过页面的请求测试,均可以将接口转发到配置的目标。

  1. 要查看到转发的响应结果,就需要查看 http-proxy-middleware 的文档,使用到的事件有 proxyReq 和 proxyRes
    在这里插入图片描述
    给代理配置批量增加事件监听,代码如下:
// 给代理配置增加监听
proxies.forEach((proxy) => {
  proxy.on = (() => {
    return {
      // 打印请求转发
      proxyReq: function onProxyReq(proxyReq, req, res) {
        proxyReq.setHeader('Cache-Control', 'no-cache')
        console.log(' 🚀 ', req.method, req.url, '->', proxyReq.host + proxyReq.path)
      },
      proxyRes: async function onProxyRes(proxyRes, req, res) {
        const responseBody = await getBody(proxyRes)
        console.log(' 🌠 ', req.method, proxyRes.statusCode, req.url, '->', responseBody)
      },
      error: (err) => {
        console.log(chalk.red('error'), ' 😱 ', err.code)
      }
    }
  })()
})

// 解析获取转发返回响应内容
function getBody(proxyRes) {
  return new Promise((resolve) => {
    let body = []
    proxyRes.on('data', function (chunk) {
      body.push(chunk)
    })
    proxyRes.on('end', async function (val) {
      body = Buffer.concat(body)
      try {
        // 判断响应是否有使用了gzip
        if (proxyRes.headers['content-encoding']?.toLowerCase() === 'gzip') {
          const ungzip = await zlib.gunzipSync(body) // 同步解压缩数据
          resolve(JSON.parse(ungzip.toString()))
        } else {
          resolve(JSON.parse(body.toString()))
        }
      } catch (error) {
        // JSON.parse 报错直接返回body
        resolve(body)
      }
    })
  })
}

在上面的代码中,有使用到了头部判断。但在ctx 里面的 headers 变量中,它里面的 key 都被 nodejs 转换成小写。在日常的一般业务中,我们对大小写敏感,所以需要对每个到 koa 的请求,进行头部字段的大小写还原。ctx.req.rawHeaders这个变量内部存储着原始字段,需要转换,代码如下,然后引入到 koa 中。

// 设置头部字段为原始内容,而不是node转换后的都是小写的头部字段
const setRawHeaders = async (ctx, next) => {
  const originHeaders = convertRawHeadersToObject(ctx.req.rawHeaders)
  ctx.request.headers = originHeaders
  await next()
}

// ['requestId', '1234565', 'clienntId', '123456'] 转换成 {requestId: '123456', clienntId: '123456'}
const convertRawHeadersToObject = (rawHeaders) => {
  const headers = {}
  for (let i = 0; i < rawHeaders.length; i += 2) {
    headers[rawHeaders[i]] = rawHeaders[i + 1]
  }
  return headers
}

export default setRawHeaders

到此,我们将这个简易服务器搭建起完成,能够实现对普通html页面的接口进行转发,以及在控制台查看相关参数内容。
如果有什么问题,欢迎指教,完整的代码在这里 https://github.com/402931261/simple-koa2-server

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1910927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

侯捷C++面向对象高级编程(上)-11-虚函数与多态

1.虚函数 2.virtual 3.继承&#xff0b;复合关系下的构造和析构 4.委托&#xff0b;继承

Java基础语法--基本数据类型

Java基础语法–基本数据类型 Java是一种静态类型语言&#xff0c;这意味着每个变量在使用前都必须声明其数据类型。Java提供了多种基本数据类型&#xff0c;用于存储整数、浮点数、字符和布尔值等。以下是Java中的基本数据类型及其特点&#xff1a; 1. 整型&#xff08;Integ…

深入剖析预处理

目录 1.预定义符号 2.#define 定义常量 3.#define定义宏 4.带有副作用的宏参数 5.宏替换的规则 6.宏函数的对比 7.#和## 7.1 #运算符 7.2 ## 运算符 8.命名约定 9.#undef 10.命令行定义 11.条件编译 12.头文件的包含 12.1 头文件被包含的方式&#xff1a; 12.1…

C++ 语法习题(2)

第三讲 循环语句 1.偶数 编写一个程序&#xff0c;输出 1 到 100之间&#xff08;包括 1 和 100&#xff09;的全部偶数。 输入格式 无输入。 输出格式 输出全部偶数&#xff0c;每个偶数占一行。 输入样例 No input输出样例 2 4 6 ... 100 参考代码: #include <i…

玩具营销是如何拿捏成年人钱包?

好像现在的成年人逐渐热衷于偏向年轻化&#xff0c;问问题会好奇“尊嘟假嘟”&#xff0c;饭量上的“儿童套餐”&#xff0c;娃娃机前排长队......而最突出的莫过于各类各式的玩具不断收割当代年轻人&#xff0c;除去常给大朋友们小朋友们送去玩具福利的“麦、肯”双门&#xf…

数据分析中excel常用函数总结

1、相对引用&#xff0c;绝对引用和混合引用 相对引用&#xff1a;比如求和&#xff0c;当计算出一个求和值&#xff0c;其他的就可以向下填充&#xff0c;此时求和引用的行列会随着求和区域的行列变化而变化。 绝对引用&#xff1a; 对某一个引用区域进行锁定&#xff0c;让…

华为防火墙 拓扑搭建1

拓扑图 要求 1.DMZ区内的服务器&#xff0c;生产区仅能在办公时间内&#xff08;9&#xff1a;00-18&#xff1a;00&#xff09;可以访问&#xff0c;办公区设备全天可以访问 配置安全策略 设置办公时间 2.生产区不允许访问互联网&#xff0c;办公区和游客区允许访问互联网…

【Mongodb-03】亿级数据从mysql迁移到mongodb辛酸历程

亿级数据从mysql迁移到mongodb辛酸历程 一&#xff0c;亿级数据从mysql迁移到mongodb辛酸历程1&#xff0c;核心业务和前期实现2&#xff0c;分库分表考虑3&#xff0c;nosql的选择4&#xff0c;mongodb服务器购买or自己搭建5&#xff0c;数据从mysq迁移到mongodb5.1&#xff0…

算法力扣刷题记录 三十八【二叉树的层次遍历应用一及二叉树构建】

前言 二叉树层序遍历应用题目。 记录三十八 【二叉树的层次遍历应用一】 继续。 一、【107.二叉树的层次遍历 II】 题目 给你二叉树的根节点 root &#xff0c;返回其节点值 自底向上的层序遍历 。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向…

如何解决英国Facebook直播网络延时问题?

许多商家在英国进行Facebook直播&#xff0c;但网络延时和卡顿问题常常困扰着用户。这不仅影响观众的观看体验&#xff0c;也会给商家带来巨大损失。本文将探讨解决英国Facebook直播网络延时和卡顿问题的方案&#xff0c;以促进业务发展并提升用户满意度。 海外直播的挑战 海外…

3GPP R18 Multi-USIM 是怎么回事?(三)

这篇内容相对来说都是一些死规定,比较枯燥。主要是与MUSIM feature相关的mobility and periodic registration和service request触发过程的一些规定,两部分的内容是有部分重叠的,为保证完整性,重复部分也从24.501中摘了出来。 24.501 4.25 网络和MUSIM UE可以支持MUSIM fe…

优劣分析:重启路由器 vs 使用IP代理

目前更换IP主要有两种常见方法&#xff0c;一种是重启路由器&#xff0c;另一种是使用代理IP&#xff0c;那么&#xff0c;这两种方法有什么优缺点呢&#xff1f;下面我们一起来探讨一下。 方法一&#xff1a;重启路由器变换IP 优点 1. 操作简单&#xff1a;只需断开路由器电…

头歌资源库(22)求组合数

一、 问题描述 二、算法思想 动态规划是一种将问题分解成子问题并保存子问题解以避免重复计算的算法思想。 对于组合数C(n, r)&#xff0c;我们可以将问题分解成子问题C(i, j)&#xff0c;其中i表示从1到n的选择数&#xff0c;j表示选择的元素个数。则C(n, r)可以通过求解C(i…

多标签问题

一、多标签问题与单标签问题的区别&#xff1a; 多标签问题是单标签问题的推广。 举个例子&#xff0c;同时识别图片中的小汽车&#xff0c;公交车&#xff0c;行人时&#xff0c;标签值有三个&#xff1a;小汽车&#xff0c;公交车&#xff0c;行人。 单标签问题仅对一个标签…

ASP.NET MVC Lock锁的测试

思路&#xff1a;我们让后台Thread.Sleep一段时间&#xff0c;来模拟一个耗时操作&#xff0c;而这个时间可以由前台提供。 我们开启两个或以上的页面&#xff0c;第一个耗时5秒(提交5000)&#xff0c;第二个耗时1秒(提交1000)。 期望的测试结果&#xff1a; 不加Lock锁&…

喝酒骰子夜店手灯轮盘扫雷鳄鱼拆弹你演我猜小游戏流量主小程序开源版开发

喝酒骰子夜店手灯轮盘扫雷鳄鱼拆弹你演我猜小游戏流量主小程序开源版开发 喝酒摇骰子、轮盘、扫雷大战、夜店手灯、鳄鱼拔牙、喝酒大叔、指尖光环、拆弹英雄、幸运转转转、你演我猜、眼疾手快、占领方块、你演我猜。 喝酒骰子类小程序通常包含多种互动游戏和娱乐功能&#xf…

音视频质量评判标准

一、实时通信延时指标 通过图中表格可以看到&#xff0c;如果端到端延迟在200ms以内&#xff0c;说明整个通话是优质的&#xff0c;通话效果就像大家在同一个房间里聊天一样&#xff1b;300ms以内&#xff0c;大多数人很满意&#xff0c;400ms以内&#xff0c;有小部分人可以感…

Qt常用基础控件总结—容器部件(QGroupBox类)

五、容器部件 按钮框控件QDialogButtonBox 类(很少用) 按钮组控件QButtonGroup 类(很少用) 组框控件QGroupBox 类 QGroupBox 类介绍 QGroupBox(组框),直接继承自 QWidget 类,因此使用该类创建的对象,可作为窗口使用,组框在外观上是可见的。 QGroupBox 类(组框),…

Odoo14使用hiPrint实现打印功能

使用hiPrint代替odoo原生的打印功能 可以实现快速自定义修改打印模板&#xff0c;无需每次都调整打印模板 无论是表单分页还是各种需求&#xff0c;都能满足 目录 1 使用命令创建新的模块&#xff0c;无用的demo文件可以删除掉 2 新建“打印模板”&#xff0c;用于保存打印…

AFT:Attention Free Transformer论文笔记

原文链接 2105.14103 (arxiv.org) 原文翻译 Abstract 我们介绍了 Attention Free Transformer (AFT)&#xff0c;这是 Transformer [1] 的有效变体&#xff0c;它消除了点积自注意力的需要。在 AFT 层&#xff0c;键key和值value首先与一组学习的位置偏差position biases相结…