React项目,setupProxy.js如何生效?http-proxy-middleware
发布于 作者:苏南大叔 来源:程序如此灵动~在create-react-app
项目里面,有个src/setupProxy.js
文件,这个文件是如何代理【测试环境下】的网络接口请求?代码中的http-proxy-middleware
,只能应用于接口请求么?带着问题阅读苏南大叔的文章。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0
,create-react-app@5.0.1
,react-router-dom@6.27.0
,react@18.3.1
,http-proxy-middleware@2.0.3
。
前文阅读
任何react
项目里面,类似ajax
的请求,都是本文的目标接口测试内容。参考文章:
本地项目为localhost:3000
,远程接口服务器项目为localhost:3222
。域名虽然相同,但是端口不一致,所以必须解决一个“跨域”的问题。
解决跨域的方式很多,在以前的解决方案,是在express
接口中配置了cors
。而本文描述的方案,就是create-react-app
中的react-scripts
所自带的src/setupProxy.js
解决方案。
当然,本文还有个更加简单直接的跨域方案,直接在package.json
里面配置proxy
项目。参考文章:
代码配置
http-proxy-middleware
库借助于node-http-proxy
,用于将node
服务器接收到的请求转发到目标服务器,实现代理服务器的功能。
生效途径
本文的src/setupProxy.js
本身的功能就相当于上面的cors
,并不需要接口服务器(express
)端做跨域配置。src/setupProxy.js
文件,是react-scripts
项目所约定俗成的文件名。只要符合规范,就可以被create-react-app
创建的项目所识别。参考下面的代码截图。
简单例子
这个代理代码的配置,很程式化,可以参考下面的范例。
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
// /sunan/plus/aa?bb=cc
createProxyMiddleware("/sunan", {
target: "http://localhost:3222",
changeOrigin: true,
}),
// /api/sunan/plus/aa?bb=cc
createProxyMiddleware("/api", {
target: "http://localhost:3222",
changeOrigin: true,
pathRewrite: {
// 匹配到就停止匹配,不继续匹配
"^/api/sunan/plus/": "/sunan_plus/",
"^/api/sunan/ojbk/": "/sunan_ojbk/",
"^/api/": "",
},
})
);
};
效果是这样的:
本地请求 | 实际请求 |
---|---|
localhost:3000/sunan/* | localhost:3222/sunan/* |
localhost:3000/api/sunan/plus/* | localhost:3222/sunan_plus/* |
localhost:3000/api/sunan/ojbk/* | localhost:3222/sunan_objk/* |
localhost:3000/api/* | localhost:3222/* |
参数解释:
changeOrigin: true
就是相当于“跨域”设置,这个跨域的原理后续文章再做讨论。对于本文来说,无脑设置为true
即可,没有什么理由设置为默认的false
。pathRewrite
,路径匹配规则。这里可以配置多条规则,并且从上往下,一旦匹配成功,则不再继续匹配。也就是说,并不是单纯的replace
,有多少个地方,就替换多少次。功能很强大,但是基本上也用不到。因为,做接口的都是自己人,为啥自己人要难为自己人呢?对吧?生生的制造【正则表达式】的应用场景么?
新的配置例子
如果上面的例子看懂的话,这段例子也没有什么可以看的,就是再细分处理而已。下面的例子中,显示了可定制的headers
,里面有签权token
的传递方式。
下面的代码中,导出函数createProxyMiddleware()
被:proxy
重命名为了proxy()
。
const { createProxyMiddleware: proxy } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
// /login/sunan/ok?true=1
proxy("/login", {
target: "http://localhost:3222",
changeOrigin: true,
headers: {
Authorization: "Bearer sunan_token",
},
pathRewrite: {
"^/login/sunan/": "/superman/",
},
}),
// /img/aaa.png
proxy("/img/", {
target: "http://localhost:3222",
changeOrigin: true,
pathRewrite: {
"^/img/": "",
},
})
);
};
在这个新的例子里面,请求的接口(文件)的匹配表是:
本地请求 | 实际请求 |
---|---|
localhost:3000/login/sunan/ok?true=1 | localhost:3222/superman/ok?true=1 |
localhost:3000/img/*.png | localhost:3222/*.png |
测试代码
修改这个src/setupProxy.js
文件,并不会像预想中的一样实施生效,需要再次【重启npm start
】。具体的原因,这里就不深究了,没人会天天修改这个文件。
react部分
测试的接口请求部分,是使用create-react-app
创建的基础代码,使用ahooks
的useRequest
的官方demo
修改版,发起不同的被proxy
的请求。参考文章:
App.js
:
import React from "react";
import { useRequest } from "ahooks";
function fetchData(url = "/api/default") {
console.log(url, "#########");
return fetch(url)
.then(async (response) => {
if (!response.ok) {
return await response.text();
}
return response.json();
})
.then((data) => {
console.log(data);
return data;
});
}
function Status(props) {
if (props.loading) {
return <p>Loading...</p>;
}
if (props.error) {
return <p>Error: {props.error.message}</p>;
}
}
function Button(props) {
return (
<button onClick={props.run.bind({}, props.url)} disabled={props.loading}>
{props.loading ? "loading" : props.url}
</button>
);
}
function App() {
const { data, loading, error, run } = useRequest(fetchData);
return (
<div>
<Button run={run} loading={loading} url="/sunan/123"></Button>
<Button run={run} loading={loading} url="/api/sunan/plus/ss?bb=cc"></Button>
<Button run={run} loading={loading} url="/api/sunan/ojbk/nn?cc=dd" ></Button>
<Button run={run} loading={loading} url="/api/pku/ok?ss=nn"></Button>
<Button run={run} loading={loading} url="/login/sunan/ok?oo=kk"></Button>
<Status loading={loading} error={error}></Status>
<pre>{!loading && !error && JSON.stringify(data, null, 2)}</pre>
<img src="/img/aaa.png" />
</div>
);
}
export default App;
express部分
另外,本文的代码正确执行,需要一个接口服务配合。这里使用express.js
来做这件事情。苏南大叔定制的express
,接管了所有的请求,然后显示req
的相关信息,已验证接口是否正确请求。
express.js
:
const express = require("express");
const app = express();
app.use(express.static("public2"));
app.all("*", (req, res) => {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
ret = {
host: req.headers.host,
url: req.url,
path: req.path,
method: req.method,
token: token + "," + (token === "sunan_token"),
time: Date.now().toString().slice(-6),
};
res.send(JSON.stringify(ret));
});
const server = app.listen(process.env.PORT || 3222, () => {
const port = server.address().port;
console.log("http://localhost:%d", port);
});
注意这里的代码,没有配置cors
。并且为了匹配静态资源,设置了静态资源目录public2
。例如:这里演示的被“proxy”的图片aaa.png
,位于3222
端口这边的express
项目下的public2
目录下面。
文章内容的主要目标是接管网络接口请求,实际上,任何不存在的请求。无论接口、图片、css
文件、js
文件等,本项目中不存在的资源请求,都可以被http-proxy-middleware
所接管。
build之后 等价物
值得特别说明的是:npm run build
之后,这个端口的代理功能,就是由nginx
进行配置的了。目前的src/setupProxy.js
文件是没有什么用的了。
单就接口中转这件事来说,使用这么专业的代码,真的是杀鸡用宰牛刀了。因为普通的node
里面的fetch()
请求/php
端的file_get_contents()
,就能做到这个效果。跨域啥的,完全不惧。为啥还非要发明个changeOrigin: true
呢?
结语
http-proxy-middleware
不但可以应用在react-scripts
支持的项目中,在其它项目里面也可以做到类似的资源代理请求功能的。参考后续文章。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。