【问题标题】:how to run StartAsync connection of signalr blazor client in docker image?如何在 Docker 映像中运行 Signalr Blazor 客户端的 StartAsync 连接?
【发布时间】:2020-06-10 00:04:22
【问题描述】:

我创建了默认的 blazor 服务器端应用程序。然后添加Microsoft.AspNetCore.SignalR.ClientChatHub 类。然后编辑startup.cs文件(添加services.AddSignalR()endpoints.MapHub<ChatHub>("/chatHub"))和index.razor页面。然后由 IIS express 运行。没关系。

然后添加 docker 支持并运行 Docker 主机。它不工作。因为只有集线器连接的 StartAsync 方法不起作用。如何运行它?帮我? 非常感谢你们。

错误是:

处理请求时发生未处理的异常。 SocketException:无法分配请求的地址 System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancelToken)

HttpRequestException:无法分配请求的地址 System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancelToken)

index.razor 代码:

@code {
    private HubConnection _hubConnection;

    protected override async Task OnInitializedAsync()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub"))
            .Build();

        _hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var encodedMsg = $"{user}: {message}";
            StateHasChanged();
        });

        await _hubConnection.StartAsync(); // **DON'T WORK IN DOCKER HOST.**
    }
}

Docker 文件:

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80

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

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

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "BlazorApp1.dll"]

【问题讨论】:

    标签: docker asp.net-core signalr blazor


    【解决方案1】:

    我的最佳猜测是您的中心客户端正在尝试连接到“the-public-url-out-of-docker/chatHub”:

        _hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub"))
            .Build();
    

    NavigationManager.ToAbsoluteUri(...) 会将/chatHub 转换为公开给最终用户的网址。例如,如果您使用反向代理,它可能是域名。

    请注意,网址分为三个不同的级别:

    • 公开的域名
    • 主机ip和端口
    • 容器 ip 和端口
      +----------------------------------+
      | HOST  (5000)                     |
      |   +                              |
      |   |Port Mapping---------------+  |
      |   >-->-->|Container (80)      |  |
      |          |                    |  |
      |          +--------------------+  |
      +-----^----------------------------+
            |  reverse proxy
    +-------+----------------------------+
    | nginx                              |
    |  https://www.myexample.com/chatHub
    |                                    |
    +-------^----------------------------+
            |
            |
            |
            |
    +-------+-----------+
    |                   |
    |   Browser         | (Brazor sees only the public url via NavgiationManager  )
    |                   |
    +-------------------+
    

    但是,在 docker 中运行时,主机的网络始终无法从容器的网络中访问。

    如果是这样的话,有几种方法应该可行:

    1. 避免使用像.WithUrl(NavigationManager.ToAbsoluteUri("/chatHub")) 这样的公共网址。将其硬编码到容器 ip&port。例如,如果您的容器在 80 上侦听,则应为 http://localhost/chatHub
    2. 为 docker 配置网络,或在运行 docker 时添加--network。更多详情请见this thread

    【讨论】:

    • 非常感谢 itminus。这是我的例子。我将尝试我的真实项目。你给了我一个很好的机会,我知道了一些新的东西。再次感谢您。
    • 我刚刚更新了我的真实项目。它正在工作。但我无法获取 http 客户端中间件令牌的令牌。我在 startup.cs 文件中添加了 services.AddSignalR() 。那就不能拿token了。因为http上下文为空。然后我从 startup.cs 文件中删除了 services.AddSignalR() 。这是正常工作。然后我可以从 http 客户端获取令牌。你怎么看待这件事?谢谢。
    • @batbayar SingalR 使用 WebSocket,无法自定义标头。作为一种解决方法,您可以通过查询字符串发送令牌,例如,如果您使用 JwtBearer 令牌,请自定义事件处理程序来接收令牌。顺便说一句,如果我的上述解决方案解决了原始问题,您能否接受它作为答案?
    • 对不起。我的英语不是很好。这是问题。如何解决?
    • @batbayar "这是个问题。如何解决":你的意思是如何解决令牌问题或如何接受我的回复作为答案?
    【解决方案2】:

    我遇到了同样的问题,通过 IIS 运行应用程序按预期工作,但是通过 docker SignalR 运行时无法连接到集线器。我通过定义自己的导航管理器来解决此问题,该管理器继承自 NavigationManager 并覆盖 EnsureInitialized() 方法以设置基本 URI。

    public class CustomNavigationManager : NavigationManager
    {
        protected override void EnsureInitialized()
        {
            var baseUri = Environment.GetEnvironmentVariable("blazor-app-url");
            Initialize(baseUri, baseUri);
        }
    }
    

    还需要将容器名称和环境变量添加到我的docker-compose.override.yml 文件中,以便在CustomNavigationManager 中使用。

    version: '3.4'
    
    networks:
      mynetwork:
        external: true      
    
    services:
      my-blazor-app:
        networks: 
          - mynetwork
        container_name: blazor-app    
        environment:
          blazor_app_url: http://blazor-app:8080/
        ports:
          - "49101:8080"
    

    CustomNavigationManager可以在启动时注册为Singleton,注入相关组件(替换NavigationManager),并继续像之前的NavigationManager一样使用,例如:

    public class WeatherForecastService : IWeatherForecastService
    {
        private readonly HttpClient _httpClient;
        private readonly CustomNavigationManager _navigationManager;
    
        public WeatherForecastService(HttpClient httpClient, CustomNavigationManager navigationManager)
        {
            _httpClient = httpClient;
            _navigationManager = navigationManager;
        }
    
        public async Task<IEnumerable<WeatherForecast>?> GetWeatherForecast(DateTime startDate)
        {
            var uri = _navigationManager.ToAbsoluteUri($"/weatherforecast/get?startDate={startDate:s}");
            return await _httpClient.GetFromJsonAsync<WeatherForecast[]>(uri);
        }
    }
    

    为了让应用程序在通过 IIS express 运行时继续工作,还需要在launchSettings.json 中定义环境变量。

    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "blazor_app_url": "http://localhost:5282/"
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-26
      • 2020-07-02
      • 2021-12-05
      • 2012-04-05
      • 2012-04-22
      相关资源
      最近更新 更多