You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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");
            });
        }
    }
}

解决方案

核心问题分析

  1. 请求源与容器地址不匹配:前端通过浏览器访问localhost:8081,实际请求源是http://localhost:8081,而非Docker内部的后端容器域名。浏览器无法解析容器内部域名,且即使能解析,CORS校验的是浏览器端的请求源。
  2. 后端中间件顺序错误:UseCors必须在UseAuthorization和UseEndpoints之前执行,当前配置中UseCors在UseAuthorization之后,导致CORS头无法正确注入响应。
  3. 后端端口未映射: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在容器内部访问后端)。

测试步骤

  1. 重新构建前后端镜像;
  2. 执行docker-compose down && docker-compose up -d重启服务;
  3. 访问http://localhost:8081,检查浏览器控制台是否还有CORS错误;
  4. 用Postman请求http://localhost:8080/graphql,验证后端响应头是否包含Access-Control-Allow-Origin: *。

内容的提问来源于stack exchange,提问作者Elec

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:08:15