I have ASP.NET MVC app and I am using SignalR for real-time bids. I need to use clients both via JavaScript and C# (from one of my controllers).
This is my Startup.cs file:
namespace DesignAndBuilding.Web
{
using System.Reflection;
using AutoMapper;
using DesignAndBuilding.Data;
using DesignAndBuilding.Data.Common;
using DesignAndBuilding.Data.Common.Repositories;
using DesignAndBuilding.Data.Models;
using DesignAndBuilding.Data.Repositories;
using DesignAndBuilding.Data.Seeding;
using DesignAndBuilding.Services;
using DesignAndBuilding.Services.Mapping;
using DesignAndBuilding.Services.Messaging;
using DesignAndBuilding.Web.Hubs;
using DesignAndBuilding.Web.ViewModels;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
public class Startup
{
private readonly IConfiguration configuration;
public Startup(IConfiguration configuration)
{
this.configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer(this.configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(IdentityOptionsProvider.GetIdentityOptions)
.AddRoles<ApplicationRole>().AddEntityFrameworkStores<ApplicationDbContext>();
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
})
.AddMessagePackProtocol();
services.Configure<CookiePolicyOptions>(
options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAutoMapper(typeof(Startup), typeof(MappingProfile));
services.AddMemoryCache();
services.AddControllersWithViews(
options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
}).AddRazorRuntimeCompilation();
services.AddRazorPages();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSingleton(this.configuration);
// Data repositories
services.AddScoped(typeof(IDeletableEntityRepository<>), typeof(EfDeletableEntityRepository<>));
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
services.AddScoped<IDbQueryRunner, DbQueryRunner>();
// Application services
services.AddTransient<IEmailSender, NullMessageSender>();
services.AddTransient<IBuildingsService, BuildingsService>();
services.AddTransient<IUsersService, UsersService>();
services.AddTransient<IAssignmentsService, AssignmentsService>();
services.AddTransient<IBidsService, BidsService>();
services.AddTransient<INotificationsService, NotificationsService>();
services.AddTransient<AuthenticationService>();
// Auto Mapper Configurations
services.AddSingleton(provider => new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
}).CreateMapper());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
AutoMapperConfig.RegisterMappings(typeof(ErrorViewModel).GetTypeInfo().Assembly);
// Seed data on application startup
using (var serviceScope = app.ApplicationServices.CreateScope())
{
var dbContext = serviceScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
dbContext.Database.Migrate();
new ApplicationDbContextSeeder().SeedAsync(dbContext, serviceScope.ServiceProvider).GetAwaiter().GetResult();
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(
endpoints =>
{
endpoints.MapHub<NotificationsHub>("/notificationshub");
endpoints.MapHub<BidsHub>("/bidshub");
endpoints.MapControllerRoute("areaRoute", "{area:exists}/{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
}
This is my JS client:
console.log('Bids script loaded successfully!');
setupConnection = async function start() {
let connection = null;
connection = await new signalR.HubConnectionBuilder()
.withUrl("/bidshub")
.build();
await connection.on('newbidplaced', (obj) => {
console.log(obj);
})
await connection.start()
.catch(err => console.error(err.toString()))
.then(() => {
connection.invoke('NewBid', '1', 'testUser', 5.4);
});
}
setupConnection();
And this is my C# client from one of my controllers:
var connection = new HubConnectionBuilder()
.AddMessagePackProtocol()
.WithUrl($"https://{this.Request.Host}/bidshub", options =>
{
options.UseDefaultCredentials = true;
})
.WithAutomaticReconnect()
.Build();
await connection.StartAsync();
await connection.InvokeAsync("NewBid", strId, user.Id, bidViewModel.BidPrice);
await connection.DisposeAsync();
My problem is that when I use the C# client, Context.User is always null in the SignalR hub. However, when I use the JS client the user is authenticated and Context.User is not null. What is the difference and what might be the problem?
After analyzing the code, you use cookies for authentication. By default,
Javascript clients(browser) are supported, butC# Clientsare not.So you should get cookie and use it in C# client like below: