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

背景

由于工作中碰到写普通页面(未使用脚手架),需要发起接口请求,但普通页面又无法对接口发起正常请求,故编写一个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 pathchangeOrigin: 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 pathchangeOrigin: 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 pathchangeOrigin: true, // needed for virtual hosted sites},{pathFilter: '/api2',pathRewrite: {'/api2': ''},target: 'https://jsonplaceholder.typicode.com', // target host with the same base pathchangeOrigin: 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 {// 判断响应是否有使用了gzipif (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 报错直接返回bodyresolve(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 = originHeadersawait 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.rhkb.cn/news/372697.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手写简单实现IOC

这个小demo是利用反射从最基础一步一步模拟实现了IOC的功能,所有的代码基本都给出了注释,方便大家阅读. 目录结构&#xff1a; 这里需要导入一下junit依赖 <!-- junit测试 --><dependency><groupId>junit</groupId><artifactId>junit</artif…

解决vite 断点调试定位不准确问题

问题&#xff1a;vite构建时&#xff0c;控制台报错行数等信息定位不准确或debugger断点调试定位不准确 解决&#xff1a;F12后打开设置面板&#xff0c;把“JavaScript源代码映射”去掉可临时解决&#xff0c;如需永久解决需升级vite到最新版 还有一种&#xff1a; 参考&…

Unity--射线检测--RayCast

Unity–射线检测–RayCast 1.射线检测的含义 射线检测,根据名称而言,使用一条射线来检测是击中了某个物体/多个物体 射线检测的包含两个部分: 射线和检测 2.射线检测可以用在哪些地方 射击游戏&#xff1a; 玩家的瞄准和射击&#xff1a;检测玩家视线是否与敌人或其他目标…

JRE、JVM、JDK分别是什么。

JDK JDK的英文全称是Java Development Kit。JDK是用于制作程序和Java应用程序的软件开发环境。JDK 是 Java 开发工具包&#xff0c;它是 Java 开发者用来编写、编译、调试和运行 Java 程序的集合。JDK 包括了 Java 编译器&#xff08;javac&#xff09;、Java 运行时环境&…

首席数据官CDO证书报考指南:方式、流程、适考人群与考试难度

在信息泛滥的今天&#xff0c;数据已转变为企业不可或缺的宝贵资源。 面对海量的信息&#xff0c;如何提炼出价值&#xff0c;为企业带来实质性的收益&#xff1f;首席数据官&#xff08;CDO&#xff09;认证的出现正是为了满足这一需求&#xff0c;它不仅是个人专业能力的体现…

【网络安全】这些网络安全知识请牢记!

随着社会信息化深入发展&#xff0c;互联网对人类文明进步将发挥更大促进作用&#xff0c;但与此同时&#xff0c;互联网领域的问题也日益凸显&#xff0c;网络犯罪、网络攻击等时有发生&#xff0c;网络安全与每个人都息息相关&#xff0c;下面一起来了解网络安全知识吧&#…

如何降低电力运维成本,为企业的运维增效、能源数字化和节能降耗提供数据支持?

【电力运维存在问题】 随着全球范围内城镇化、数字化和工业化进程的加速与电力政策的改革&#xff0c;企业用电需求不断攀升&#xff0c;极大冲击了电力企业传统的运维模式&#xff0c;暴露出许多的问题&#xff1a; 变电所较为分散&#xff0c;缺乏统一管理&#xff1b;站内…

从数据仓库到数据湖(下):热门的数据湖开源框架

文章目录 一、前言二、Delta Lake三、Apache Hudi四、Apache Iceberg五、Apache Paimon六、对比七、笔者观点八、总结八、参考资料 一、前言 在上一篇从数据仓库到数据湖(上)&#xff1a;数据湖导论文章中&#xff0c;我们简单讲述了数据湖的起源、使用原因及其本质。本篇文章…

Linux:Ubuntu18.04下开机自启动QT图形化界面

Linux&#xff1a;Ubuntu18.04下开机自启动QT图形化界面 Chapter1 Linux&#xff1a;Ubuntu18.04下开机自启动QT图形化界面一、创建rc.local文件二、建立rc-local.service文件三、启动服务查看启动状态四、重启 Chapter2 将QT应用作为开机自启动&#xff08;Linux系统&#xff…

Simulink生成代码时端口名称乱码问题

写在最前&#xff1a; 在使用Simulink生成代码时发现端口名称与模型中定义的输如输出端口名称不一致&#xff0c;代码生成的端口名称为随机字符名称。 在生成的H文件中发现&#xff0c;端口定义的结构体名称与模型中实际定义的名称不符。 模型中的定义 检查后发现&#xff0c…

【已解决】腾讯云安装了redis,但是本地访问不到,连接不上

汇总了我踩过的所有问题。 查看配置文件redis.conf 1、把bind 127.0.0.1给注释掉&#xff08;前面加个#就是&#xff09;或者改成bind 0.0.0.0&#xff0c;因为刚下载时它是默认只让本地访问。&#xff08;linux查找文档里的内容可以输入/后面加需要匹配的内容&#xff0c;然后…

基于STM主题模型的主题提取分析-完整代码数据

直接看结果: 代码: import re from collections import defaultdict import random import matplotlib.pyplot as plt import numpy as npimport pandas as pd import numpy as np import re from sklearn.feature_extraction.text import CountVectorizer from nltk.corpus…

如何在 Ubuntu上搭建 LAMP

远程登录 Ubuntu系统环境 ssh (User)(IP) # 比如&#xff1a;ssh lennlouis192.168.207.128 为安全起见&#xff0c;建议你使用 root 登录 VPS 后创建一个具有 sudo 权限的帐号。 安装和配置 Apache 2 Apache Http Server 是一个开源的&#xff0c;非常流行&#xff0c;使用…

【Dell R730 折腾记录】风扇调速--在 Ubuntu 系统上开机自启动并每隔30分钟执行一次风扇定速脚本

前段时间升级了一下机柜里的服务器&#xff0c;替换掉了一台旧的 Dell 服务器&#xff0c;换上了这台 R730。但是无奈于噪音的袭扰&#xff0c;搁置了一段时间。我在这台机器上目前安装了一块 Intel Xeon E5-2630v3 芯片以及一张改过散热的 NVIDIA Tesla P4 计算卡。结果就是散…

关于ORACLE单例数据库中的logfile的切换、删除以及添加

一、有关logfile的状态解释 UNUSED&#xff1a; 尚未记录change的空白group&#xff08;一般会出现在loggroup刚刚被添加&#xff0c;或者刚刚使用了reset logs打开数据库&#xff0c;或者使用clear logfile后&#xff09; CURRENT: 当前正在被LGWR使用的gro…

K8S 上部署大数据相关组件

文章目录 一、前言二、Redis 一、前言 Artifact Hub 是一个专注于云原生应用的集中式搜索和发布平台。它旨在简化开发者在 CNCF&#xff08;Cloud Native Computing Foundation&#xff09;项目中寻找、安装和分享包与配置的过程。用户可以通过这个平台方便地发现、安装各类云原…

【CPP】CPP的命名空间输入输出缺省参数函数重载

目录 1 命名空间 -- namespace2 CPP的输入与输出(io)2.1 输入输出流的一些规定2.2 实操一下2.3 关于endl2.4 关于精度控制2.5 效率提高 3 缺省参数(默认参数)3.1 样例3.2 全缺省与半缺省3.3 缺省参数的意义 4 函数重载4.1 函数重载的基本使用4.2 函数重载调用歧义 这里是oldkin…

记录一次MySql锁等待 (Lock wait timeout exceeded)异常

[TOC](记录一次MySql锁等待 (Lock wait timeout exceeded)异常) Java执行一个SQL查询未提交&#xff0c;遇到1205错误。 java.lang.Exception: ### Error updating database. Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transactionCluster…

互联网应用主流框架整合之SpringCloud微服务治理

微服务架构理念 关于微服务的概念、理念及设计相关内容&#xff0c;并没有特别严格的边界和定义&#xff0c;某种意义上说&#xff0c;适合的就是最好的&#xff0c;在之前的文章中有过详细的阐述&#xff0c;微服务[v1.0.0][Spring生态概述]、微服务[设计与运行]、微服务[v1.…

SpringMVC--获取请求参数

1、通过的ServletAPI获取 只需要在控制器的方法的形参位置设置HTTPRequest request 类型的形参就i可以在控制器方法种使用request对象获取请求参数 RequestMapping("/servletAPI")public String getByServletAPI(HttpServletRequest request){HttpSession session…