1

I'm working on a .net core project which currently offers a REST API and I would like to add websocket functionality to this server. According to this developer page by microsoft it should be possible to do the following:

public class MyWebSocketMiddleware : IMiddleware {
    
    private readonly IMyService _myService;

    // I need injection of request-scoped services, which is why I use IMiddleware.
    public MyWebSocketMiddleware(IMyService myService){
        this._myService = myService;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate requestDelegate) {
            if (!context.WebSockets.IsWebSocketRequest) {
                // this case works perfectly fine for regular REST, middleware gets called.
                await requestDelegate.Invoke(context);
                return;
            }
            
            // the execution NEVER gets here
            Log.Info("Received websocket request.");
    }
}

... and register it in Startup.cs like so:

public void ConfigureServices(IServiceCollection services) {

    // ... lots of services...

    // according to the microsoft page linked above, we should use AddTransient here
    services.AddTransient<MyWebSocketMiddleware>();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    // for now, to make sure CORS isn't the problem
    app.UseCors(
        options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()
    );

    // for the REST controllers
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    // for Websockets
    app.UseWebSockets(new WebSocketOptions {
        KeepAliveInterval = TimeSpan.FromSeconds(180)
    });
    app.UseMiddleware<MyWebSocketMiddleware>();

    app.UseEndpoints(endpoints => {
        endpoints.MapControllers();
    });
}

It all compiles and boots nicely without any problems on my Windows 10 machine.

My issue is: No matter which client I try to use, I never get to the "Received websocket request" log output. All HTTP(s) calls work as intended, but my server seems to outright refuse any connections with ws://localhost:5000 (or wss on port 5001 or any permutations thereof).

Am I missing something? Did I mess up the ordering of middleware registration?

Martin Häusler
  • 6,544
  • 8
  • 39
  • 66

1 Answers1

1

After much tinkering and comparing my code to a working minimal sample project from microsoft, I finally got it working in my project as well.

The key is that all REST-specific configurations (in particular app.UseHttpsRedirection()) must occur after the websocket middleware, otherwise the requests might end up blocked and/or redirected before the websocket middleware even has a chance to fire.

The following ordering worked for me:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    // first, register everything websocket-related...
    app.UseWebSockets(new WebSocketOptions {
        KeepAliveInterval = TimeSpan.FromSeconds(180)
    });
    app.UseMiddleware<MyWebSocketMiddleware>();

    // ... then everything related to REST
    app.UseHttpsRedirection();
    
    app.UseRouting();
    
    app.UseCors(...);

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints => {
        endpoints.MapControllers();
    });
}

Bonus tip: if you're trying to get your own websockets to work, the example project linked above contains a single self-contained HTML page that can act as an interactive testing tool.

Martin Häusler
  • 6,544
  • 8
  • 39
  • 66