1 问题背景
在开发 React Native + Expo 应用时,使用 axios
请求后端服务 API 接口,发现 Web 端控制台报错:
Access to XMLHttpRequest at 'http://192.168.31.5:13378/api/xxx' from origin 'http://localhost:8081' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
经测试发现:
- ✅ 安卓/iOS 端请求正常
- ❌ Web 端跨域请求被浏览器拦截
- ❗ 后端服务未配置 CORS 响应头且不可修改
2 排查思路
为什么移动端正常?
移动端应用不受浏览器安全策略限制,实际是通过 React Native 的 Network API 发起请求,不存在跨域问题。
Web 端为何失败?
当使用 expo start:web
启动开发服务时,实际运行在浏览器环境中,受同源策略限制。
Expo 支持配置 proxy 吗?
查了 Expo 的文档发现expo-cli
的 app.json
配置文件中没有关于proxy配置的内容,只找到了web模式下的打包采用了 metro bundler
打包和运行。于是找到对应文档,发现可以通过 enhanceMiddleware
添加中间件 http-proxy-middleware
来启用 proxy。
3 解决方案:前端代理
- 安装依赖
npm install http-proxy-middleware -D
- 在项目根目录下创建 metro.config.js
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");
const { createProxyMiddleware } = require("http-proxy-middleware");
const defaultConfig = getDefaultConfig(__dirname);
const config = {
...defaultConfig, // 不改变Expo定制的metro配置
server: {
...defaultConfig.server,
enhanceMiddleware: (middleware) => {
return (req, res, next) => {
if (req.url.startsWith("/mock")) { // mock 按你的喜好配置和axios中的拦截一致即可
return createProxyMiddleware({
target: "http://192.168.31.5:13378", // 替换为你的后端地址
changeOrigin: true,
pathRewrite: { "^/mock": "/" },
})(req, res, next);
}
return middleware(req, res, next);
};
},
},
};
module.exports = config;
- 修改axios拦截器请求地址
axios.interceptors.request.use(async (config) => {
...其它业务代码
if (__DEV__ && Platform.OS === "web") {
config.baseURL = "/mock";
}
...其它业务代码
return config;
});
- 重启 expo cli
expo start --web
至此已经能在Web模式下正常访问后端接口。
4 原理说明
- 代理路径匹配
所有以/mock
开头的请求会被代理中间件捕获 - 路径重写
pathRewrite
将/mock
前缀替换为后端 API 的基础路径 - Header 处理
changeOrigin: true
会修改请求头中的 Host 字段,确保后端服务能正确接收请求
5 注意事项
- 仅开发环境生效
该配置仅在通过expo start:web
启动时生效,生产环境部署需另行配置代理 - 网络请求可见性
在 Chrome DevTools 的 Network 面板中,代理请求仍会显示原始路径 - 移动端兼容性
移动端请求需要保持原始地址,可通过环境变量区分运行环境
本篇已被阅读 次