【问题标题】:How to call an ASP.NET Core Web API endpoint from within separate Docker Containers如何从单独的 Docker 容器中调用 ASP.NET Core Web API 端点
【发布时间】:2021-04-16 18:30:30
【问题描述】:

我有两个项目:

  1. UI(.NET Core 3.1 MVC 字体端)
  2. API(.NET Core 3.1 Web API)

每个都在两个单独的容器中运行。他们共享一个构建它们的 docker-compose 文件。在 UI 项目中,我希望能够进行 AJAX 调用以访问 API 项目中的端点。 会到达我的端点的 URL 是什么?

我尝试过的:

  1. 如果我使用 IIS 单独运行 API 项目,我可以通过导航到 https://localhost:49239/weatherforecast 成功地到达端点。但是,再次,我想通过在 UI 项目中从客户端调用它来访问这个端点。
  2. 如果我使用 docker-compose 启动这两个容器,然后从 UI 项目中单击执行我的 AJAX 调用的按钮到端点 https://localhost:49239/weatherforecast 它不起作用。我已尝试对 URL 的主机部分进行多种变体来访问它。

根据Networking in Compose

Compose 为您的应用设置单一网络。每个容器为一个 服务加入默认网络,并且都可以被其他网络访问 该网络上的容器,并且可以在主机名上被它们发现 与容器名称相同。

因此,我还尝试了许多主机 URL 变体,例如 https://api:49243/weatherforecast,其中“api”是我的 docker 映像的名称,“49243”是 docker ps 列出的端口。我也试过:

  • https://api:80/weatherforecast
  • https://api:433/weatherforecast
  • https://api:PORT_NUM/weatherforecast 其中“PORT_NUM”是使用 docker ps 查看时为容器列出的任何端口号

那我怎么打这个端点???

注意:

  • 我已经运行 docker inspect CONTAINER_NAME 并且我知道两个容器都在同一个网络上。

文件:

  1. UI > Index.cshtml:
@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
    <button type="button" class="btn btn-primary" onclick="callAPI()">Call API</button>
</div>

@section scripts {
    <script>
        function callAPI() {
            console.log("calling API...");

            $.ajax({
                url: `https://api:49221/weatherforast/get`,
                method: 'GET',
                success: function (data) {
                    console.log(data);
                },
                error: function (error) {
                    console.log(error);
                }
            });
        }
    </script>
}
  1. UI > Dockerfile:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["UI/UI.csproj", "UI/"]
RUN dotnet restore "UI/UI.csproj"
COPY . .
WORKDIR "/src/UI"
RUN dotnet build "UI.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "UI.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "UI.dll"]
  1. API > WeatherForecastController.cs:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace API.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            var rng = new Random();
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateTime.Now.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}
  1. API > Dockerfile:
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["API/API.csproj", "API/"]
RUN dotnet restore "API/API.csproj"
COPY . .
WORKDIR "/src/API"
RUN dotnet build "API.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "API.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "API.dll"]
  1. 码头工人撰写:
version: '3.4'

services:
  ui:
    image: ${DOCKER_REGISTRY-}ui
    build:
      context: .
      dockerfile: UI/Dockerfile

  api:
    image: ${DOCKER_REGISTRY-}api
    build:
      context: .
      dockerfile: API/Dockerfile
  1. 为了更好地理解解决方案结构,这里是我的Solution Explorer的屏幕截图

【问题讨论】:

标签: docker asp.net-core docker-compose asp.net-core-mvc asp.net-core-webapi


【解决方案1】:

据我了解,您有 2 个 ASP.NET 项目希望彼此通信。这很简单,而且您走在正确的轨道上。在您的 docker compose 上,您想为 2 个容器创建一个网络。如果您想同时打开 API 和 UI(端口:80)的端口,您可以映射它们,或者如果您只想访问 UI 并且不关心从 UI 之外的任何其他地方访问 API,那么只需打开将端口 80 连接到您的主机网络(或您选择的任何其他端口)。继续创建一个网络进行试验。 docker network create MyNetwork。编辑完docker-compose.yml 后(注意,您可以在 docker compose 中创建网络,我只是这样描述,以便您了解幕后最终发生的事情)

version: '3.4'

services:
  ui:
    # Always a good practice to set container names.
    container_name: asp_net_ui
    image: ${DOCKER_REGISTRY-}ui
    build:
      context: .
      dockerfile: UI/Dockerfile
    ports:
      - "80:80"

  api:
    container_name: asp_net_api
    image: ${DOCKER_REGISTRY-}api
    build:
      context: .
      dockerfile: API/Dockerfile
      
# As I said before this is refrenceing a pre excisting network but you could
# Create a new network here aswell. I tend to use ths approach as I have multiple
# Diffrent stacks running in a network, e.g. Caddy, Jenkins, Dev Staging Env.
networks:
  default:
    external:
      name: MyNetwork

在回答您关于将访问我的端点的 URL 是什么?
您可以使用 DNS 解析。只需提供容器名称即可。

对于容器到容器的通信:
要访问您的 API: http://asp_net_api/{controller}
要点击您的UI: http://asp_net_ui/{view}

从浏览器访问您的UIhttp://localhost/{view}

我希望这对您有所帮助,如果您还有任何问题,请随时开火或参考官方网络文档here

【讨论】:

  • 感谢您提供如此清晰的答案。今天早上我只有 5 分钟的时间来尝试它,但无法让它工作,但我会在有时间制作更多原型时重新开始。
  • 请耐心等待,因为我对网络知之甚少,但我无法让它发挥作用。所以我用docker network create TestNetwork 创建了一个网络。然后我在 docker-compose.yml 的末尾添加了networks: default: external: name: TestNetwork,所以我知道它们在同一个网络上。然后我只将ports: - "80:80" 添加到我的ui 服务中。之后,调用http://asp_net_api/{controller}http://asp_net_api:80/{controller} 仍然没有命中我在API 项目中的方法。我也尝试将端口移动到 api 服务。
  • 上传到 github 并通过链接发送给我,我会查看并推送更改。
  • 我在 GitHub 上向您发送了合作邀请。
【解决方案2】:

最终我发现 Visual Studio 创建了一个我有一段时间没有注意到的文件,名为docker-compose.override.yml。该文件公开了端口 80 和 443,但没有指定主机端口,我相信这导致我的应用程序每次运行时都以随机端口号运行。这让我很难知道将 API 调用指向何处。

基本上,只需将端口添加到我的docker-compose.yml(如下面的代码块所示)并(重要的是)从docker-compose.override.yml 中的每个服务中删除端口部分我就能够让 UI 和 WebAPI 应用程序分别在指定端口上运行。从那里,我需要将主机名 "https://localhost:49254" 添加到我的 WebAPI 应用程序的 CORS 策略中,这样它就不会阻止来自我的 UI 应用程序的请求。

version: '3.4'

services:
  ui:
    container_name: ui_container
    image: ${DOCKER_REGISTRY-}ui
    build:
      context: .
      dockerfile: UI/Dockerfile
    ports:
      - "49254:443"
  api:
    container_name: api_container
    image: ${DOCKER_REGISTRY-}api
    build:
      context: .
      dockerfile: API/Dockerfile
    ports:
      - "49255:443"

所以,要访问我的 API 端点,我必须 ajax 到 https://localhost:49255/WeatherForecast

要查看我的所有代码,请查看我的GitHub repository

如果我要将这个应用程序投入生产,显然我需要调整主机名。

如果有人知道更好/更清洁的解决方案,请提出建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-16
    • 1970-01-01
    • 2023-01-19
    • 2019-07-06
    • 2019-08-16
    • 2019-04-27
    • 2022-11-04
    • 2017-08-01
    相关资源
    最近更新 更多