Angular结合Docker/docker-compose部署后本地页面无法访问
Angular+ASP.NET Core Docker部署后页面无法访问问题排查
我本地可以正常运行Angular应用,但通过Docker部署后出现“本地页面无法找到”的问题(docker-compose中还有其他未列出的服务)。以下是我的配置信息:
Docker-compose配置
version: '3.4' services: shopfrontspa: container_name: lost_chapters_shop_front image: ${DOCKER_REGISTRY-}shopfrontspa build: context: . dockerfile: FrontEnds/ShopFrontSpa/Dockerfile networks: - lost_chapters_network
Docker-compose.override配置
version: '3.4' services: shopfrontspa: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:443;http://+:80 ports: - "5110:80" - "7110:443" volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro networks: lost_chapters_network: external: true
Dockerfile配置
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["FrontEnds/ShopFrontSpa/ShopFrontSpa.csproj", "FrontEnds/ShopFrontSpa/"] RUN dotnet restore "FrontEnds/ShopFrontSpa/ShopFrontSpa.csproj" COPY . . WORKDIR "/src/FrontEnds/ShopFrontSpa" RUN dotnet build "ShopFrontSpa.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "ShopFrontSpa.csproj" -c Release -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "ShopFrontSpa.dll"]
Package.json配置
"name": "shopfrontspa", "version": "0.0.0", "scripts": { "ng": "ng", "prestart": "node aspnetcore-https", "start": "run-script-os", //"start": "ng serve --host 0.0.0.0", "start:windows": "ng serve --port 44436 --ssl --ssl-cert %APPDATA%\\ASP.NET\\https\\%npm_package_name%.pem --ssl-key %APPDATA%\\ASP.NET\\https\\%npm_package_name%.key", "start:default": "ng serve --port 44436 --ssl --ssl-cert $HOME/.aspnet/https/${npm_package_name}.pem --ssl-key $HOME/.aspnet/https/${npm_package_name}.key", "build": "ng build", "build:ssr": "ng run ShopFrontSpa:server:dev", "watch": "ng build --watch --configuration development", "test": "ng test"
Program.cs配置
var builder = WebApplication.CreateBuilder(args); builder.WebHost.UseContentRoot(Directory.GetCurrentDirectory()); // Add services to the container. builder.Services.Configure<AppSettings>(builder.Configuration); builder.Services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder .SetIsOriginAllowed((host) => true) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); }); builder.Services.AddControllersWithViews().AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; }); builder.Services.AddHttpClient<IOrderService, OrderService>(p => { //p.BaseAddress = new Uri(builder.Configuration["MicroservicAddress:ApiGatewayForWeb:Uri"]); }); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseCors("CorsPolicy"); // Here we add Angular default Antiforgery cookie name on first load. https://angular.io/guide/http#security-xsrf-protection // This cookie will be read by Angular app and its value will be sent back to the application as the header configured in .AddAntiforgery() //app.Use((context, next) => //{ // string path = context.Request.Path.Value; // //Do work that does not write to the Response // return next(context); // //Do logging or other work that does not write to the Response. //}); var pathBase = app.Configuration["PATH_BASE"]; if (!string.IsNullOrEmpty(pathBase)) { app.UsePathBase(pathBase); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); endpoints.MapControllers(); }); app.MapFallbackToFile("index.html"); ; // Handles all still unnatended (by any other middleware) requests by returning the default page of the SPA (wwwroot/index.html). app.UseSpa(spa => { // To learn more about options for serving an Angular SPA from ASP.NET Core, // see https://go.microsoft.com/fwlink/?linkid=864501 // the root of the angular app. (Where the package.json lives) spa.Options.SourcePath = "Client"; if (app.Environment.IsDevelopment()) { // use the SpaServices extension method for angular, that will make the application to run "ng serve" for us, when in development. spa.UseAngularCliServer(npmScript: "start"); } }); app.Run();
应用运行日志
2023-05-02 13:06:04 warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60] 2023-05-02 13:06:04 Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed. 2023-05-02 13:06:04 warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35] 2023-05-02 13:06:04 No XML encryptor configured. Key {47f19310-8c91-40f1-a9a1-8599d772d586} may be persisted to storage in unencrypted form. 2023-05-02 13:06:04 info: Microsoft.Hosting.Lifetime[14] 2023-05-02 13:06:04 Now listening on: https://[::]:443 2023-05-02 13:06:04 info: Microsoft.Hosting.Lifetime[14] 2023-05-02 13:06:04 Now listening on: http://[::]:80 2023-05-02 13:06:04 info: Microsoft.Hosting.Lifetime[0] 2023-05-02 13:06:04 Application started. Press Ctrl+C to shut down. 2023-05-02 13:06:04 info: Microsoft.Hosting.Lifetime[0] 2023-05-02 13:06:04 Hosting environment: Development 2023-05-02 13:06:04 info: Microsoft.Hosting.Lifetime[0] 2023-05-02 13:06:04 Content root path: /app/ 2023-05-02 13:19:29 warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60] 2023-05-02 13:19:29 Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed. 2023-05-02 13:19:29 info: Microsoft.Hosting.Lifetime[14] 2023-05-02 13:19:29 Now listening on: https://[::]:443 2023-05-02 13:19:29 info: Microsoft.Hosting.Lifetime[14] 2023-05-02 13:19:29 Now listening on: http://[::]:80 2023-05-02 13:19:29 info: Microsoft.Hosting.Lifetime[0] 2023-05-02 13:19:29 Application started. Press Ctrl+C to shut down. 2023-05-02 13:19:29 info: Microsoft.Hosting.Lifetime[0] 2023-05-02 13:19:29 Hosting environment: Development 2023-05-02 13:19:29 info: Microsoft.Hosting.Lifetime[0] 2023-05-02 13:19:29 Content root path: /app/
问题原因及解决方案
1. Angular构建产物未被正确打包进容器
你的Dockerfile仅执行了dotnet publish,但ASP.NET Core不会自动构建Angular项目。需要在发布阶段添加Angular的构建步骤,将静态产物输出到ASP.NET Core的wwwroot目录。
修改Dockerfile的publish阶段:
FROM build AS publish # 安装Node.js环境用于构建Angular RUN apt-get update && apt-get install -y nodejs npm # 切换到Angular项目根目录 WORKDIR "/src/FrontEnds/ShopFrontSpa/Client" # 安装依赖 RUN npm install # 构建Angular项目,输出到上级目录的wwwroot RUN npm run build -- --output-path ../wwwroot # 返回ASP.NET项目目录完成发布 WORKDIR "/src/FrontEnds/ShopFrontSpa" RUN dotnet publish "ShopFrontSpa.csproj" -c Release -o /app/publish /p:UseAppHost=false
2. 开发环境下容器内尝试启动ng serve失败
Program.cs中,开发环境会调用spa.UseAngularCliServer启动ng serve,但容器的base镜像(aspnet:6.0)不含Node.js环境,且ng serve默认绑定localhost,容器外部无法访问。
解决方式:
- 推荐采用静态文件托管(即上面修改Dockerfile的方式),同时可以删除Program.cs中开发环境启动ng serve的逻辑,或在Docker部署时设置环境为Production;
- 如果非要在容器内用ng serve,需在镜像中安装Node.js,且修改Angular的start命令添加
--host 0.0.0.0(不推荐,性能差且不符合Docker部署最佳实践)。
3. 静态文件目录检查
进入容器确认wwwroot目录是否存在Angular构建产物:
docker exec -it lost_chapters_shop_front ls /app/wwwroot
如果目录为空,说明Angular构建步骤有问题,检查npm依赖安装、构建命令是否执行成功。
4. PathBase配置影响
如果配置了PATH_BASE环境变量,访问URL需要包含对应路径;如果不需要,可直接删除这部分配置。
5. 端口与网络验证
- 检查本地5110/7110端口是否被占用;
- 进入容器内部直接访问服务,确认服务本身是否正常:
docker exec -it lost_chapters_shop_front curl http://localhost:80
如果能返回index.html内容,说明端口映射或本地网络(防火墙、代理)有问题。
内容的提问来源于stack exchange,提问作者Ports
相关产品推荐
相关产品推荐

