GraphQL后端与Angular前端Docker Compose部署CORS问题求助
问题描述
我有基于GraphQL的.NET后端和Angular前端,两者单独通过Docker部署均可正常运行,但使用docker-compose联合部署后,前端可正常访问,却无法获取后端响应,出现CORS错误。后端已在Startup.cs中配置允许所有源、请求头和请求方法的跨域策略,非Docker环境下运行完全正常。前端已使用http://weather-test_backend_1:8080/graphql作为后端请求地址。
相关配置文件
后端Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build ARG configuration=Release WORKDIR /src COPY ["WeatherBackend.csproj", "./"] RUN dotnet restore "WeatherBackend.csproj" COPY . . WORKDIR "/src/." RUN dotnet build "WeatherBackend.csproj" -c $configuration -o /app/build FROM build AS publish ARG configuration=Release RUN dotnet publish "WeatherBackend.csproj" -c $configuration -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "WeatherBackend.dll"]
前端Dockerfile
FROM node:18 AS build RUN mkdir -p /app WORKDIR /app RUN npm install -g @angular/cli RUN npm install -g http-server COPY package*.json ./ RUN npm ci COPY . . CMD ["ng", "serve", "--host", "0.0.0.0", "--port", "8081"] # # Build the Angular app for production RUN npm run build --configuration=production FROM nginx:latest COPY nginx.conf /etc/nginx/nginx.conf COPY --from=build /app/dist/weather-frontend/browser/ /usr/share/nginx/html
docker-compose.yml
version: '3.8' services: frontend: image: weather-frontend-image ports: - "8081:80" environment: - BACKEND_URL=http://weather-test_backend_1:8080/graphql backend: image: weather-backend-image
容器状态(docker-compose up -d后)
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b256527de2bf weather-frontend-image "/docker-entrypoint.…" 2 days ago Up 2 days 0.0.0.0:8081->80/tcp, :::8081->80/tcp weather-test_frontend_1 b720df4c5247 weather-backend-image "dotnet WeatherBacke…" 2 days ago Up 2 days weather-test_backend_1
后端Startup.cs配置
using HotChocolate.AspNetCore.Playground; namespace WeatherBackend { public class Startup { private readonly IWebHostEnvironment _env; public Startup(IWebHostEnvironment env) { _env = env; } public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddHttpClient<WeatherService>(); services.AddGraphQLServer() .AddQueryType<WeatherQueryType>() .AddApolloTracing() .AddType<WeatherType>() .ModifyRequestOptions(opt => opt.IncludeExceptionDetails = _env.IsDevelopment()); services.AddCors(options => { options.AddPolicy("AllowAnyOrigin", builder => { builder.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); }); services.AddOptions(); services.Configure<OpenWeatherMapOptions>(options => { options.ApiKey = Environment.GetEnvironmentVariable("OpenWeatherMapApiKey");; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseCors(builder => { builder.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); app.UseCors("AllowAnyOrigin"); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapGraphQL("/graphql"); }); } } }
解决方案
核心问题分析
- 请求源与容器地址不匹配:前端通过浏览器访问
localhost:8081,实际请求源是http://localhost:8081,而非Docker内部的后端容器域名。浏览器无法解析容器内部域名,且即使能解析,CORS校验的是浏览器端的请求源。 - 后端中间件顺序错误:
UseCors必须在UseAuthorization和UseEndpoints之前执行,当前配置中UseCors在UseAuthorization之后,导致CORS头无法正确注入响应。 - 后端端口未映射:docker-compose中后端未暴露端口到宿主机,浏览器无法直接访问后端服务。
具体修复步骤
1. 调整后端Startup.cs中间件顺序
将UseCors移至UseAuthorization之前,且仅保留一次有效策略应用:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // 开发环境可临时关闭HttpsRedirection,避免证书问题 // app.UseHttpsRedirection(); app.UseRouting(); // 优先启用CORS策略 app.UseCors("AllowAnyOrigin"); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapGraphQL("/graphql"); }); }
2. 修改docker-compose.yml配置
- 给后端添加端口映射,让宿主机可访问;
- 调整前端
BACKEND_URL为宿主机地址(匹配浏览器请求源); - 指定容器名称避免自动生成名称变化:
version: '3.8' services: frontend: image: weather-frontend-image ports: - "8081:80" environment: - BACKEND_URL=http://localhost:8080/graphql depends_on: - backend backend: image: weather-backend-image container_name: weather-backend ports: - "8080:8080"
3. 验证后端监听端口
确保.NET后端在容器内监听8080端口,可在appsettings.json中配置:
{ "Urls": "http://0.0.0.0:8080" }
或在Dockerfile的ENTRYPOINT中指定:
ENTRYPOINT ["dotnet", "WeatherBackend.dll", "--urls", "http://0.0.0.0:8080"]
可选:用Nginx反向代理彻底消除跨域
修改前端nginx.conf,添加反向代理规则,让前端请求/api/graphql转发到后端,避免浏览器直接跨域请求:
server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/graphql { proxy_pass http://weather-backend:8080/graphql; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
此时前端请求地址改为/api/graphql,docker-compose中后端无需映射端口到宿主机(Nginx在容器内部访问后端)。
测试步骤
- 重新构建前后端镜像;
- 执行
docker-compose down && docker-compose up -d重启服务; - 访问
http://localhost:8081,检查浏览器控制台是否还有CORS错误; - 用Postman请求
http://localhost:8080/graphql,验证后端响应头是否包含Access-Control-Allow-Origin: *。
内容的提问来源于stack exchange,提问作者Elec
相关产品推荐
相关产品推荐

