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?
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.