Upload SignalR Server to Windows Server IIS

65 Views Asked by At

I've done a Notifications-Service with .NET 6.0 using SignalR as real-time technology. I need upload this backend to Windows Server so I can try the project, however, when I uploaded, I got few errors.

Web Browser Response

I've enabled web sockets on the IIS

enter image description here

This is my web.config:

    <?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="/Connect" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\NotificationService.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
  </location>
</configuration>

As you can see, I've added into web.config the path of SignalR Hub (/Connect). I already have installed SDKS and also I've enabled web sockets features from Server Manager.

I will share with you below the backend's Code:

This is My Program.cs:

//JWT Bearer
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("siudhg987IJHASDhaisd7ASDihasdjkqUIHasd123098345ASOIDU"))
    };
    o.TokenValidationParameters = new TokenValidationParameters
    {
     
        IssuerSigningKey = new SymmetricSecurityKey
            (Encoding.UTF8.GetBytes(builder.Configuration["secreatKey"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };

    o.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];

            // If the request is for our hub...
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) &&
                (path.StartsWithSegments("/Connect"))) // Change "/Connect" to your hub route
            {
                // Read the token from the query string
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});
//Rabbit
builder.Services.AddScoped<IRabbitConnection, RabbitConnection>();
//SignalR
builder.Services.AddSignalR();
builder.Services.AddSignalR(opt => { opt.HandshakeTimeout = TimeSpan.MaxValue; });
builder.Services.AddSingleton<INotificationServiceRepository, NotificationServiceRepository>();
builder.Services.AddSingleton<IUserConnectionManager, UserConnectionManager>();
builder.Services.AddSingleton<INotificationStore,MemoryNotificationStore>();
builder.Services.AddSingleton<NotificationMemoryStorage>();

builder.Services.AddScoped<INotificationServiceRepository, NotificationServiceRepository>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseCors();
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseWebSockets(); ;
//Ruta para conectarse al servidor de ws
app.MapHub<NotificationHub>("/Connect");

app.UseAuthorization();

app.MapControllers();

app.Run();

And this My Hub:

  public class NotificationHub : Hub<INotificationHub>
    {
        private readonly IUserConnectionManager _userConnectionManager;
        private readonly INotificationStore _notificationStore;

        public NotificationHub(IUserConnectionManager userConnectionManager, INotificationStore notificationStore)
        {
            _userConnectionManager = userConnectionManager;
            _notificationStore = notificationStore;
        }

        //<------------------------------------------------------------------------------->

        public override async Task OnConnectedAsync()
        {
            var token = Context.GetHttpContext().Request.Headers["access_token"];

            if (!string.IsNullOrEmpty(token))
            {
                var result = await UserConnectionAsync(token);
                await Clients.Client(Context.ConnectionId).ConnectionResponse(result.response);

                if (result.succesfulConnection)
                {
                    await SendStoredNotifications(result.userId);
                }
                else
                {
                    var response = new ServerResponse
                    {
                        status = "Rejected",
                        message = "Error en la autorizacion"
                    };
                    await Clients.Client(Context.ConnectionId).ConnectionResponse(response);
                    Context.Abort();
                }
            }
            else
            {
                var response = new ServerResponse
                {
                    status = "Rejected",
                    message = "Token no proporcionado"
                };
                await Clients.Client(Context.ConnectionId).ConnectionResponse(response);
                Context.Abort();
            }
        }

        //<------------------------------------------------------------------------------->
        private async Task<(bool succesfulConnection, ServerResponse response, string userId)> UserConnectionAsync(string token)
        {
            var handler = new JwtSecurityTokenHandler();
            var jsonToken = handler.ReadToken(token) as JwtSecurityToken;

            if (jsonToken != null)
            {
                string userId = jsonToken.Claims.FirstOrDefault(claim => claim.Type == "email" || claim.Type == "uid")?.Value;
                var response = new ServerResponse();
                if (!string.IsNullOrEmpty(userId))
                {
                    await GetConnectionId(userId);
                    response.status = "Accepted";
                    response.message = "Conexion Exitosa";
                    return (true, response, userId);
                }
                else
                {
                    response.status = "Rejected";
                    response.message = "Error en la autorizacion";
                    return (false, response, null);
                }
            }
            else
            {
                var response = new ServerResponse
                {
                    status = "Rejected",
                    message = "Error en la autorizacion"
                };
                return (false, response, null);
            }
        }

        //<------------------------------------------------------------------------------->

        private async Task SendStoredNotifications(string userId)
        {
            if (!string.IsNullOrEmpty(userId))
            {
                var notifications = _notificationStore.RetrieveNotifications(userId);

                foreach (var notification in notifications)
                {
                    await Clients.Client(Context.ConnectionId).SendNotification(notification);
                }

                _notificationStore.ClearNotifications(userId);
            }
        }

        //<------------------------------------------------------------------------------->
        public async Task<string> GetConnectionId(string userId)
        {
            _userConnectionManager.KeppUserConnection(userId, Context.ConnectionId);
            return Context.ConnectionId;
        }

        //<------------------------------------------------------------------------------->

        public override async Task OnDisconnectedAsync(Exception? exception)
        {
            var connectionId = Context.ConnectionId;
            _userConnectionManager.RemoveUserConnection(connectionId);
            await base.OnDisconnectedAsync(exception);
        }

        //<------------------------------------------------------------------------------->

        public async Task MessageCheck(NotificationRequestModel request)
        {
            //meter validaciones
            switch (request.Target.Device)
            {
                case "Admin":
                    if (request.Target.User.Contains("*"))
                    {
                        SendNotificationToAllUsers(request.Notification);
                    }
                    else
                    {
                        SendNotificationToSpecificUser(request.Notification, request.Target.User);
                    }
                    break;
               
            }
        }

        //<------------------------------------------------------------------------------------->

        public async Task SendNotificationToAllUsers(NotificationModel message)
        {
            var allUserIds = _userConnectionManager.GetAllUserIds();
            foreach (var userId in allUserIds)
            {
                var connectionsIds = _userConnectionManager.GetUserConnections(userId);
                if (connectionsIds.Any())
                {
                    foreach (var connectionId in connectionsIds)
                    {
                        await Clients.Client(connectionId).SendNotification(message);
                    }
                }
                else
                {
                    _notificationStore.StoreNotification(userId, message);
                }
            }
        }

        //<------------------------------------------------------------------------------->

        public async Task SendNotificationToSpecificUser(NotificationModel message, List<string> target)
        {
            foreach (var userId in target)
            {
                var connectionIds = _userConnectionManager.GetUserConnections(userId);
                if (connectionIds.Count() > 0)
                {
                    foreach (var connectionId in connectionIds)
                    {
                        await Clients.Client(connectionId).SendNotification(message);
                    }
                }
                else
                {
                    _notificationStore.StoreNotification(userId, message);
                }
            }
        }

        //<------------------------------------------------------------------------------->
    }

I apologizes for the code.

And in my launchsettings.json I have activated IIS Express:

"IIS Express": {
   "commandName": "IISExpress",
   "launchBrowser": true,
   "launchUrl": "swagger",
   "environmentVariables": {
     "ASPNETCORE_ENVIRONMENT": "Development"
   }

Also I've tried to connect by Postman: HTTP:

enter image description here

WS:

enter image description here What am I doing wrong or what am I missing? I honestly, idk what i'm doing.

2

There are 2 best solutions below

0
Jason Pan On

wss not support append Headers like yours, you should use querystring to implement this requirement.

enter image description here

0
Eliass On

Update

This is my web.config, is the original from publish.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\NotificationService.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
    </system.webServer>
  </location>
</configuration>
<!--ProjectGuid: F6D37998-C9EE-4ECC-AAAB-186BDCE5550C-->

The curious is the following, I can connect to Hub but instanteanly it disconnect to me, without show me any errors: enter image description here

enter image description here

Headers that I sent by Postman: enter image description here

through from Windows Server's Logging: enter image description here

Logs return me: enter image description here

What could be happening that It doesn't work and I don't have details of the error??

Finally this is my Program.cs:

var builder = WebApplication.CreateBuilder(args);
ConfigurationManager configuration = builder.Configuration;
// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors();
//JWT Bearer
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("siudhg987IJHASDhaisd7ASDihasdjkqUIHasd123098345ASOIDU"))
    };
    o.TokenValidationParameters = new TokenValidationParameters
    {
        IssuerSigningKey = new SymmetricSecurityKey
            (Encoding.UTF8.GetBytes(builder.Configuration["secreatKey"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };

    o.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];

            // If the request is for our hub...
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) &&
                (path.StartsWithSegments("/Connect"))) // Change "/Connect" to your hub route
            {
                // Read the token from the query string
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});
//Rabbit
builder.Services.AddScoped<IRabbitConnection, RabbitConnection>();
//SignalR
builder.Services.AddSignalR();
builder.Services.AddSignalR(opt => { opt.HandshakeTimeout = TimeSpan.MaxValue; });
builder.Services.AddSingleton<INotificationServiceRepository, NotificationServiceRepository>();
builder.Services.AddSingleton<IUserConnectionManager, UserConnectionManager>();
builder.Services.AddSingleton<INotificationStore, MemoryNotificationStore>();
builder.Services.AddSingleton<NotificationMemoryStorage>();

builder.Services.AddScoped<INotificationServiceRepository, NotificationServiceRepository>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseCors();
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseWebSockets(); ;
//Ruta para conectarse al servidor de ws
app.MapHub<NotificationHub>("/Connect");
app.UseCors(builder =>
{
    builder.WithOrigins("example.com")
           .AllowAnyHeader()
           .AllowAnyMethod()
           .AllowCredentials();
});
app.UseAuthorization();

app.MapControllers();

app.Run();