0

I have a Blazor web application. It running in a docker compose with 2 databases. It uses a SignalR Hub references as TacticsHub. It was working fine with Visual Studio and Visual Studio code. I could debug and work with the sites. Now I tried to Deploy the website to a Server and Domain. For this I am using Nginx on a Linux server. When I was visiting the page that used SignalR, I get this exception:

HttpRequestException: Response status code does not indicate success: 405 (Method Not Allowed).

System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.NegotiateAsync(Uri url, HttpClient httpClient, ILogger logger, CancellationToken cancellationToken)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.GetNegotiationResponseAsync(Uri uri, CancellationToken cancellationToken)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.SelectAndStartTransport(TransferFormat transferFormat, CancellationToken cancellationToken)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsyncCore(TransferFormat transferFormat, CancellationToken cancellationToken)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartAsync(TransferFormat transferFormat, CancellationToken cancellationToken)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken)
Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionFactory.ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken)
System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable+ConfiguredValueTaskAwaiter.GetResult()
Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncCore(CancellationToken cancellationToken)
Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsyncInner(CancellationToken cancellationToken)
Microsoft.AspNetCore.SignalR.Client.HubConnection.StartAsync(CancellationToken cancellationToken)
Wildblood.Tactics.Services.HubConnectionService.Register(Func<HubConnection, IDisposable> method) in HubConnectionService.cs
Wildblood.Tactics.Services.TacticExplorerService..ctor(IMongoDatabase mongoDatabase, IHubConnectionService hubConnectionService, IUserService userService) in TacticExplorerService.cs
System.RuntimeMethodHandle.InvokeMethod(object target, Void** arguments, Signature sig, bool isConstructor)
System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(object obj, Span copyOfArgs, BindingFlags invokeAttr)
System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor<TArgument, TResult>.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor<TArgument, TResult>.VisitCallSite(ServiceCallSite callSite, TArgument argument)
Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine+<>c__DisplayClass2_0.b__0(ServiceProviderEngineScope scope)
Microsoft.AspNetCore.Components.ComponentFactory+<>c__DisplayClass9_0.g__Initialize|1(IServiceProvider serviceProvider, IComponent component)
Microsoft.AspNetCore.Components.ComponentFactory.InstantiateComponent(IServiceProvider serviceProvider, Type componentType, IComponentRenderMode callerSpecifiedRenderMode, Nullable parentComponentId) Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateChildComponentOnFrame(RenderTreeFrame[] frames, int frameIndex, int parentComponentId) Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(ref DiffContext diffContext, int frameIndex) Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(ref DiffContext diffContext, int newFrameIndex) Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(ref DiffContext diffContext, int oldStartIndex, int oldEndIndexExcl, int newStartIndex, int newEndIndexExcl) Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, out Exception renderFragmentException) Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue() Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue() Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged() Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync() Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync() Microsoft.AspNetCore.Components.Rendering.ComponentState.SupplyCombinedParameters(ParameterView directAndCascadingParameters) Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters) Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(int componentId, ParameterView initialParameters) Microsoft.AspNetCore.Components.HtmlRendering.Infrastructure.StaticHtmlRenderer.BeginRenderingComponent(IComponent component, ParameterView initialParameters) Microsoft.AspNetCore.Components.Endpoints.EndpointHtmlRenderer.RenderEndpointComponent(HttpContext httpContext, Type rootComponentType, ParameterView parameters, bool waitForQuiescence) System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult() Microsoft.AspNetCore.Components.Endpoints.RazorComponentEndpointInvoker.RenderComponentCore(HttpContext context) Microsoft.AspNetCore.Components.Endpoints.RazorComponentEndpointInvoker.RenderComponentCore(HttpContext context) Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext+<>c+<b__10_0>d.MoveNext() Microsoft.AspNetCore.Builder.ServerRazorComponentsEndpointConventionBuilderExtensions+<>c__DisplayClass1_1+<b__1>d.MoveNext() Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

I can't access this page. Everything else works fine (database / auth).

This is my Program.cs:

namespace BlazorApp.Tactics;

using Microsoft.AspNetCore.HttpOverrides;
using System.Net;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using MudBlazor.Services;
using BlazorApp.Tactics.Components;
using BlazorApp.Tactics.Components.Account;
using BlazorApp.Tactics.Data;
using BlazorApp.Tactics.Services;

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddSignalR();

        // MongoDB stuff
        var mongoConnectionString = Environment.GetEnvironmentVariable("MONGO_CONNECTION_STRING");

        builder.Services.AddSingleton<IMongoClient, MongoClient>(sp => new MongoClient(mongoConnectionString));

        builder.Services.Configure<MongoDbSettings>(options =>
        {
            options.ConnectionString = mongoConnectionString;
            options.DatabaseName = "MongoDB"; // Setzen Sie hier den Namen Ihrer Datenbank
        });

        builder.Services.AddSingleton(sp =>
        {
            var settings = sp.GetRequiredService<IOptions<MongoDbSettings>>().Value;
            var client = sp.GetRequiredService<IMongoClient>();
            return client.GetDatabase(settings.DatabaseName);
        });

        builder.Services.AddSingleton<MongoDbInitializer>();

        // Add services to the container.
        builder.Services.AddRazorComponents()
            .AddInteractiveServerComponents()
            .AddInteractiveWebAssemblyComponents();


        builder.Services.AddCascadingAuthenticationState();
        builder.Services.AddScoped<IdentityUserAccessor>();
        builder.Services.AddScoped<IdentityRedirectManager>();
        builder.Services.AddScoped<AuthenticationStateProvider, PersistingRevalidatingAuthenticationStateProvider>();
        builder.Services.AddMudServices();

        builder.Services.AddScoped<IHubConnectionService, HubConnectionService>();
        builder.Services.AddScoped<IUserService, UserService>();
        builder.Services.AddScoped<ITacticToolService, TacticToolService>();
        builder.Services.AddScoped<ITacticExplorerService, TacticExplorerService>();
        builder.Services.AddScoped<ITacticMemberListService, TacticMemberListService>();
        builder.Services.AddScoped<ITacticMapSelectorService, TacticMapSelectorService>();
        builder.Services.AddScoped<ITacticCanvasService, TacticCanvasService>();

        builder.Services.AddAuthentication().AddGoogle(googleOptions =>
        {
            googleOptions.ClientId = builder.Configuration["Google:ClientId"] ?? throw new InvalidOperationException("Google ClientId not found.");
            googleOptions.ClientSecret = builder.Configuration["Google:ClientSecret"] ?? throw new InvalidOperationException("Google ClientSecret not found.");
        });

        builder.Services.AddAuthentication(options =>
            {
                options.DefaultScheme = IdentityConstants.ApplicationScheme;
                options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
            })
            .AddIdentityCookies();

        var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
        builder.Services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(connectionString));
        builder.Services.AddDatabaseDeveloperPageExceptionFilter();

        builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddSignInManager()
            .AddDefaultTokenProviders();

        builder.Services.AddTransient<IEmailSender<ApplicationUser>, EmailSender>();

        var app = builder.Build();


        app.UseForwardedHeaders(new ForwardedHeadersOptions
        {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        });

        using (var scope = app.Services.CreateScope())
        {
            var mongoDbInitializer = scope.ServiceProvider.GetRequiredService<MongoDbInitializer>();
            mongoDbInitializer.Initialize();
        }

        // Configure the HTTP request pipeline.
        if (app.Environment.IsDevelopment())
        {
            app.UseWebAssemblyDebugging();
            app.UseMigrationsEndPoint();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // 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.MapStaticAssets();
        app.UseAntiforgery();

        app.MapRazorComponents<App>()
            .AddInteractiveServerRenderMode()
            .AddInteractiveWebAssemblyRenderMode()
            .AddAdditionalAssemblies(typeof(Client._Imports).Assembly);

        app.MapHub<TacticsHub>("/tacticsHub");

        // Add additional endpoints required by the Identity /Account Razor components.
        app.MapAdditionalIdentityEndpoints();

        app.Run();
    }
}

This is my HubConnectionService creating the HubConnection:

namespace BlazorApp.Tactics.Services;

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using Wildblood.Tactics.Models.Messages;

public class HubConnectionService : IHubConnectionService, IAsyncDisposable
{
    private HubConnection hubConnection;

    public HubConnectionService(NavigationManager navigationManager)
    {
        hubConnection = new HubConnectionBuilder()
            .WithUrl(navigationManager.ToAbsoluteUri("/tacticsHub"))
            .WithAutomaticReconnect()
            .Build();
    }

    public IDisposable Register(Func<HubConnection, IDisposable> method)
    {
        var connection = method(hubConnection);

        if (hubConnection.State == HubConnectionState.Disconnected)
        {
            hubConnection.StartAsync().GetAwaiter().GetResult();
        }

        return connection;
    }

    public async Task UpdateTactic(
        string tacticId, string folderId, string slideId, UpdateTacticMessage message)
    {
        await hubConnection.SendAsync("UpdateTactic", tacticId, folderId, slideId, message);
    }

    public async Task UpdateEntities(
        string tacticId, string folderId, string slideId, UpdateEntitiesMessage message)
    {
        await hubConnection.SendAsync("UpdateEntities", tacticId, folderId, slideId, message);
    }

    public async ValueTask DisposeAsync()
    {
        if (hubConnection != null)
        {
            await hubConnection.DisposeAsync();
        }
    }
}

This is the Hub itself:

namespace BlazorApp.Tactics
{
    using Microsoft.AspNetCore.SignalR;
    using Microsoft.AspNetCore.SignalR.Client;

    public class TacticsHub : Hub
    {
        public async Task UpdateTactic(string tacticId, string folderId, string slideId, object message)
        {
            Console.WriteLine($"Received update for tactic {tacticId}: {message}");
            await Clients.Others.SendAsync("UpdateTactic", tacticId, folderId, slideId, message);
        }

        public async Task UpdateEntities(string tacticId, string folderId, string slideId, object message)
        {
            Console.WriteLine(
                $"Received update for entities {tacticId} {folderId} {slideId}: {message}");
            await Clients.Others.SendAsync("UpdateEntities", tacticId, folderId, slideId, message);
        }
    }
}

This is my Nginx site in the sites-enabled directory:

server {
    listen 80;
    server_name example.com

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # HSTS & Sicherheit
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    location / {
        proxy_pass         http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "keep-alive";
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;

        # für Blazor Server & SignalR wichtig
        proxy_set_header   X-Forwarded-Host $host;
        proxy_set_header   X-Forwarded-Port $server_port;
    }
    location /tacticsHub/ {
       proxy_pass http://localhost:8080/tacticsHub/;
       proxy_http_version 1.1;

       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "Upgrade";

       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
    }
}

This is my docker-compose.yml:

services:
  BlazorApp:
    image: ${DOCKER_REGISTRY-}BlazorImage
    build:
      context: .
      dockerfile: dockerfile reference
    networks:
      - site-network
    environment:
      - MONGO_CONNECTION_STRING=mongodb://username:password@mongodb:27017
      - ASPNETCORE_URLS=http://+:8080;
    depends_on:
      - mongodb

  mssql:
    image: mcr.microsoft.com/mssql/server:2019-latest
    container_name: mssql_container
    ports:
      - "1433:1433"
    environment:
      SA_PASSWORD: "password"
      ACCEPT_EULA: "Y"
    volumes:
      - mssql_data:/var/opt/mssql
    networks:
      - site-network

  mongodb:
    image: mongo:6.0
    container_name: mongodb
    hostname: mongodb
    restart: always
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: username
      MONGO_INITDB_ROOT_PASSWORD: password
    volumes:
      - mongodb_data:/data/db
    networks:
      - site-network

volumes:
  mssql_data:
  mongodb_data:

networks:
  site-network:

I cant think of a problem on why it does not work.

I expect my website to function the same way it does while developing. Its dockerized for this reason.

What actually happened: Lots of different behaiviours because of the server hosting.

What am I doing wrong and how can I configure the server or code that it works on my server.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.