基于Module Federation的Angular微前端部署时出现CORS错误
本地开发时,Angular 14宿主应用与两个微前端运行正常,但容器化部署到Docker后,宿主容器请求微前端的remoteEntry.js时触发CORS错误:
Access to script at 'http://localhost:3000/remoteEntry.js' from origin 'http://localhost:5000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
微前端webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); const mf = require("@angular-architects/module-federation/webpack"); const path = require("path"); const share = mf.share; const sharedMappings = new mf.SharedMappings(); sharedMappings.register( path.join(__dirname, '../../tsconfig.json'), [ '@shared' ]); module.exports = { devServer: { allowedHosts: 'all', headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization" } }, output: { uniqueName: "medicalcoding", publicPath: "auto" }, optimization: { runtimeChunk: false }, resolve: { alias: { ...sharedMappings.getAliases(), } }, experiments: { outputModule: true }, plugins: [ new ModuleFederationPlugin({ library: { type: "module" }, name: "medicalcoding", filename: "remoteEntry.js", exposes: { './Module': './projects/medicalcoding/src/app/app.module.ts', }, shared: share({ "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, ...sharedMappings.getDescriptors() }) }) , sharedMappings.getPlugin() ], };
宿主应用webpack.config.js
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin"); const mf = require("@angular-architects/module-federation/webpack"); const path = require("path"); const share = mf.share; const sharedMappings = new mf.SharedMappings(); sharedMappings.register( path.join(__dirname, '../../tsconfig.json'), [ '@shared' ]); module.exports = { output: { uniqueName: "mainapp", publicPath: "auto", scriptType: 'text/javascript' }, optimization: { runtimeChunk: false }, resolve: { alias: { ...sharedMappings.getAliases(), } }, experiments: { outputModule: true }, plugins: [ new ModuleFederationPlugin({ library: { type: "module" }, remotes: { "medicalcoding": "http://localhost:3000/remoteEntry.js", "paymentposting": "http://localhost:4000/remoteEntry.js" }, shared: share({ "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' }, ...sharedMappings.getDescriptors() }) }), sharedMappings.getPlugin() ], };
解决方案
1. 明确webpack devServer配置的适用范围
你在微前端webpack.config.js中配置的devServer.headers仅对本地开发阶段的webpack-dev-server生效,Docker部署时用的是生产构建后的静态文件,这部分配置不会被加载,所以需要在生产环境的静态资源服务器中单独配置CORS。
2. 在Docker容器的静态服务器中添加CORS头
如果用Nginx托管微前端静态文件,修改Nginx配置文件:
server { listen 80; root /usr/share/nginx/html; # 添加CORS响应头 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS; add_header Access-Control-Allow-Headers X-Requested-With,content-type,Authorization; # 处理OPTIONS预检请求 if ($request_method = OPTIONS) { return 204; } location / { try_files $uri $uri/ /index.html; } }
将该配置文件复制到Docker镜像的Nginx配置目录(如/etc/nginx/conf.d/default.conf)即可。
3. 统一域名路径(生产环境最优方案)
跨域问题本质是域名/端口不一致,更优雅的解决方式是用反向代理(Nginx/Traefik等)将所有应用映射到同一个域名的不同路径下:
- 宿主应用:
http://your-domain.com/main - medicalcoding微前端:
http://your-domain.com/medicalcoding - paymentposting微前端:
http://your-domain.com/paymentposting
然后修改宿主应用的webpack.config.js中的remotes配置为相对路径:
remotes: { "medicalcoding": "/medicalcoding/remoteEntry.js", "paymentposting": "/paymentposting/remoteEntry.js" }
微前端的publicPath保持auto即可自动适配路径,彻底避免跨域问题。
4. 其他静态服务器的CORS配置
如果用Node.js的serve包托管静态文件,启动时添加CORS参数:
serve -s dist/medicalcoding --cors
如果用自定义Express服务器,直接在中间件中设置响应头:
const express = require('express'); const app = express(); app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,PATCH,OPTIONS'); res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type,Authorization'); next(); }); app.use(express.static('dist/medicalcoding')); app.listen(3000);
内容的提问来源于stack exchange,提问作者Alphonse

