# ASP.NET Core API + Vue 前后端分离项目 Docker 部署完整指南(Windows/WSL2)

> 适用场景:使用 ASP.NET Core 9.0 开发后端 API,Vue 3 开发前端,本地使用 MySQL 数据库,希望通过 Docker 一键部署整个应用。

## 目录
1. [环境准备](#环境准备)
2. [项目结构建议](#项目结构建议)
3. [编写后端 Dockerfile](#编写后端-dockerfile)
4. [编写前端 Dockerfile 与 Nginx 配置](#编写前端-dockerfile-与-nginx-配置)
5. [编写 docker-compose.yml](#编写-docker-composeyml)
6. [构建与运行](#构建与运行)
7. [常见问题及解决方案](#常见问题及解决方案)
8. [管理命令](#管理命令)

---

## 环境准备

- 安装 [Docker Desktop for Windows](https://www.docker.com/products/docker-desktop/)(使用 WSL 2 后端)
- 确保你的代码已经通过 Git 或直接复制到本地,**建议将整个项目放在 WSL 2 文件系统中**(例如 `\\wsl.localhost\Ubuntu\home\用户名\项目名`),以避免 Windows 文件系统性能问题和路径空格问题。
- 本地 MySQL 数据库需要允许外部连接(若仍使用宿主机 MySQL),并将连接字符串中的 `localhost` 改为 `host.docker.internal`(见下文)。

---

## 项目结构建议

MyFullStackProject/
├── docker-compose.yml
├── backend/ # ASP.NET Core API 项目
│ ├── Dockerfile
│ ├── VidCase.sln
│ └── VidCase.Api/
│ ├── VidCase.Api.csproj
│ ├── Program.cs
│ ├── appsettings.json
│ └── ...
└── frontend/ # Vue 3 项目(注意文件夹名不要有空格)
├── Dockerfile
├── nginx.conf
├── package.json
├── vite.config.js
├── src/
└── ...

text

> 如果你的前端文件夹叫 `vidcase-web`,在 `docker-compose.yml` 中需对应修改 `build: ./frontend` 为 `build: ./vidcase-web`。

---

## 编写后端 Dockerfile

在 `backend` 目录下创建 `Dockerfile`,内容如下(以 .NET 9 为例,若项目是 .NET 8 则将 `9.0` 改为 `8.0`):

```dockerfile
# 构建阶段
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env
WORKDIR /app

# 复制解决方案文件和项目文件(保持目录结构)
COPY VidCase.sln .
COPY VidCase.Api/*.csproj ./VidCase.Api/

# 还原依赖
RUN dotnet restore

# 复制所有源代码并发布
COPY . .
RUN dotnet publish VidCase.Api/VidCase.Api.csproj -c Release -o out

# 运行时阶段
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build-env /app/out .

# 注意:这里的 DLL 名称必须与你的项目名称一致
ENTRYPOINT ["dotnet", "VidCase.Api.dll"]

关键点

  • 使用 COPY *.csproj 的替代方式,因为项目文件在子文件夹中。

  • ENTRYPOINT 必须使用 JSON 数组格式(双引号、方括号),且换行符应为 LF(避免 Windows CRLF 导致解析错误)。


编写前端 Dockerfile 与 Nginx 配置

1. 前端 Dockerfile(frontend/Dockerfile

dockerfile

# 阶段一:构建 Vue 应用
FROM node:lts-alpine AS build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
# 如果你的项目使用了 TypeScript 但是实际是纯 JS 项目,请确保 package.json 中的 build 脚本为 "vite build"(而不是 "tsc && vite build")
RUN npm run build

# 阶段二:使用 Nginx 提供静态文件服务
FROM nginx:stable-alpine AS production-stage
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

2. Nginx 配置文件(frontend/nginx.conf

nginx

server {
    listen 80;
    server_name localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;   # 解决 Vue Router 刷新 404
    }

    # API 代理到后端容器(必须使用 docker-compose 中的服务名,而非 localhost)
    location /api {
        proxy_pass http://backend:5000;     # 注意端口与后端实际监听端口一致
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
        root /usr/share/nginx/html;
    }
}

后端服务名在 docker-compose.yml 中定义为 backend,且后端监听端口为 5000,所以代理地址为 http://backend:5000


编写 docker-compose.yml

在项目根目录创建 docker-compose.yml

yaml

services:
  backend:
    build: ./backend
    ports:
      - "5000:5000"                 # 暴露宿主机 5000 端口,映射到容器 5000
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:5000
      # 若使用宿主机 MySQL,连接字符串需要将 localhost 改为 host.docker.internal
      - ConnectionStrings__DefaultConnection=Server=host.docker.internal;Port=3306;Database=VidCaseDB;Uid=root;Pwd=your_password;
    networks:
      - app-network

  frontend:
    build: ./frontend               # 如果前端文件夹不是 frontend,请修改
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

注意:

  • 环境变量中的 ConnectionStrings__DefaultConnection 使用了 __(双下划线),这是 ASP.NET Core 读取连接字符串的约定。

  • 如果你直接在 appsettings.json 中修改了连接字符串为 host.docker.internal,就不需要在 docker-compose.yml 中重复设置。


构建与运行

  1. 打开终端(PowerShell 或 WSL 终端),切换到项目根目录(包含 docker-compose.yml)。

  2. 执行以下命令:

bash

# 构建镜像并启动所有服务
docker-compose up --build

# 若要在后台运行,添加 -d 参数
docker-compose up -d --build

首次启动会拉取基础镜像(.NET SDK、Node、Nginx 等),可能需要几分钟。后续构建会利用缓存,速度较快。

  1. 访问应用:

    • 前端:http://localhost

    • 后端 API(如 Swagger):http://localhost:5000/swagger


常见问题及解决方案

1. 前端构建失败:error TS18003: No inputs were found

原因:项目是纯 JavaScript(没有 .ts 文件),但 package.json 中的 build 脚本包含了 tsc 命令。
解决:将 "build": "tsc && vite build" 修改为 "build": "vite build"

2. 后端构建失败:error MSB1003 或 找不到项目文件

原因:Dockerfile 中 COPY *.csproj ./ 无法匹配到子文件夹中的 .csproj
解决:按本文提供的 Dockerfile 写法,先复制解决方案文件和项目文件到对应子目录。

3. 后端启动报错:/bin/sh: 1: [dotnet,: not found

原因ENTRYPOINT 中的 JSON 数组因 Windows CRLF 或不可见字符被错误解析。
解决:确保 Dockerfile 换行符为 LF(可在 VS Code 右下角切换),并重新手打 ENTRYPOINT ["dotnet", "YourDll.dll"]
备选:改用 shell 格式 ENTRYPOINT dotnet YourDll.dll

4. 后端无法连接本地 MySQL:Unable to connect to any of the specified MySQL hosts

原因:容器内的 localhost 指向容器自身,而非宿主机。
解决:将连接字符串中的 localhost 改为 host.docker.internal。同时确保 MySQL 允许远程连接(bind-address=0.0.0.0 且用户授权 %)。

5. 前端无法调用后端 API(404 或 CORS)

原因:Nginx 代理配置中的 proxy_pass 写成了 http://localhost:5000 或端口错误。
解决:检查 nginx.conf,代理地址必须为 http://backend:5000(服务名与 docker-compose.yml 中定义的名称一致,端口为后端实际监听端口)。

6. 构建非常慢或拉取镜像失败

原因:网络问题。
解决:配置 Docker 镜像加速器(阿里云、中科大等),或提前手动拉取基础镜像:

bash

docker pull mcr.microsoft.com/dotnet/sdk:9.0
docker pull mcr.microsoft.com/dotnet/aspnet:9.0
docker pull node:lts-alpine
docker pull nginx:stable-alpine

7. 端口冲突:bind: address already in use

原因:宿主机 80 或 5000 端口被占用。
解决:修改 docker-compose.yml 中的端口映射,例如 "8080:80" 将宿主机 8080 映射到容器 80,访问 http://localhost:8080


管理命令

操作命令
构建并启动(前台)docker-compose up --build
后台启动docker-compose up -d
停止并删除容器docker-compose down
查看日志docker-compose logs -f [服务名]
重启某个服务docker-compose restart backend
进入后端容器 shelldocker exec -it vidcase-backend-1 sh
清理未使用的镜像/容器docker system prune -a

总结

通过 Docker Compose,我们成功地将 ASP.NET Core 后端和 Vue 前端容器化,并解决了跨平台、数据库连接、构建缓存等一系列问题。该方案适用于开发、测试甚至小型生产环境。你可以根据自己的项目结构调整路径、端口和连接字符串,一键启动整个应用。

更多推荐