如何 运行 StartAsync connection of signalr blazor client in docker image?
how to run StartAsync connection of signalr blazor client in docker image?
我创建了默认的 Blazor 服务器端应用程序。然后添加Microsoft.AspNetCore.SignalR.Client
和ChatHub
class。然后编辑 startup.cs 文件(添加 services.AddSignalR()
和 endpoints.MapHub<ChatHub>("/chatHub")
)和 index.razor
页面。然后通过 IIS express 运行。没关系。
然后添加了 docker 支持和 运行 Docker 主机。它不工作。因为只有不工作的集线器连接 StartAsync 方法。如何运行呢?帮我?
非常感谢你们。
错误是:
An unhandled exception occurred while processing the request.
SocketException: Cannot assign requested address
System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancellationToken)
HttpRequestException: Cannot assign requested address
System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancellationToken)
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"]
我最好的猜测是您的中心客户端正在尝试连接到 "the-public-url-out-of-docker/chatHub":
_hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/chatHub"))
.Build();
NavigationManager.ToAbsoluteUri(...)
会将 /chatHub
转换为 public url 并暴露给最终用户。例如,如果您使用反向代理,它可能是域名。
请注意 url 分为三个不同的级别:
- 暴露的域名public
- 主机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中的运行时,主机的网络始终无法从容器的网络访问。
如果是这样,有几种方法应该有效:
- 避免使用像
.WithUrl(NavigationManager.ToAbsoluteUri("/chatHub"))
这样的 public url。将其硬编码到容器 ip 和端口。例如,如果你的容器监听 80,它应该是 http://localhost/chatHub
.
- 为 docker 配置网络,或为 运行 docker 添加
--network
。有关详细信息,请参阅 this thread
我有同样的问题,其中 运行 通过 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/"
}
}
我创建了默认的 Blazor 服务器端应用程序。然后添加Microsoft.AspNetCore.SignalR.Client
和ChatHub
class。然后编辑 startup.cs 文件(添加 services.AddSignalR()
和 endpoints.MapHub<ChatHub>("/chatHub")
)和 index.razor
页面。然后通过 IIS express 运行。没关系。
然后添加了 docker 支持和 运行 Docker 主机。它不工作。因为只有不工作的集线器连接 StartAsync 方法。如何运行呢?帮我? 非常感谢你们。
错误是:
An unhandled exception occurred while processing the request. SocketException: Cannot assign requested address System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancellationToken)
HttpRequestException: Cannot assign requested address System.Net.Http.ConnectHelper.ConnectAsync(string host, int port, CancellationToken cancellationToken)
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"]
我最好的猜测是您的中心客户端正在尝试连接到 "the-public-url-out-of-docker/chatHub":
_hubConnection = new HubConnectionBuilder() .WithUrl(NavigationManager.ToAbsoluteUri("/chatHub")) .Build();
NavigationManager.ToAbsoluteUri(...)
会将 /chatHub
转换为 public url 并暴露给最终用户。例如,如果您使用反向代理,它可能是域名。
请注意 url 分为三个不同的级别:
- 暴露的域名public
- 主机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中的运行时,主机的网络始终无法从容器的网络访问。
如果是这样,有几种方法应该有效:
- 避免使用像
.WithUrl(NavigationManager.ToAbsoluteUri("/chatHub"))
这样的 public url。将其硬编码到容器 ip 和端口。例如,如果你的容器监听 80,它应该是http://localhost/chatHub
. - 为 docker 配置网络,或为 运行 docker 添加
--network
。有关详细信息,请参阅 this thread
我有同样的问题,其中 运行 通过 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/"
}
}