Why am I getting no data from my Azure SignalR Service back to my KendoUI Grid? - asp.net-core

I'm using a KendoUI Grid in my ASP.NET Core 5 application. The grid is SignalR enabled and my application is hosted in Azure. I also use the Azure SignalR Service to handle the hubs which is what is recommended by the documentation.
I have 4 hubs powering my 4 different grids and yet, none of them are receiving data back from the SignalR Service in Azure it seems and I have no idea what is wrong. The grids seem to load, missing data, I can see the negotiations in the console with signalR and they return 200 OK. However, there is just no data being returned.
I'm not sure if it's a problem with the way I've set up my application or my Azure SignalR Service.
Here is how I have implemented the Azure SignalR Service in my application.
Startup.cs (ConfigureServices)
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var mvcBuilder = services.AddControllersWithViews(options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
mvcBuilder.AddMicrosoftIdentityUI().AddJsonOptions(options =>
{
//Use the default property (Pascal) casing.
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
services.AddRazorPages();
services.AddSignalR(options => {
//Debug only
options.EnableDetailedErrors = true;
}).AddAzureSignalR()
.AddJsonProtocol(options =>
{
//Prevents signalr converting all text to lower case.
options.PayloadSerializerOptions.PropertyNamingPolicy = null;
});
//Kendo
services.AddKendo();
services.AddHealthChecks();
}
Startup.cs (Configure)
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//Localization needed for date formats on datepickers
var supportedCultures = new[] {
new CultureInfo("en-GB")
};
//Localization set to En GB for datetime using the above supported cultures
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-GB"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/healthcheck");
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
app.UseAzureSignalR(endpoints =>
{
endpoints.MapHub<RequirementHub>("/requirementHub");
endpoints.MapHub<PositionHub>("/positionHub");
endpoints.MapHub<FixtureHub>("/fixtureHub");
endpoints.MapHub<NewswireHub>("/newswireHub");
});
}
I am using SignalR installed in my client library in it's latest version.
libman.js
{
"provider": "unpkg",
"library": "#microsoft/signalr#latest",
"destination": "wwwroot/vendor/signalr/",
"files": [
"dist/browser/signalr.js",
"dist/browser/signalr.min.js"
]
},
I have hub controllers for each of my grids that uses SignalR. I have shown the code for one of the hub controllers but they are all structured exactly the same, the only thing that changes is the name of the repository they are getting data from.
RequirementHub
using MyCompany.Data;
using MyCompany.Repo;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MyCompany.UI.Hubs
{
public class RequirementHub : Hub
{
private readonly IRepository<Requirement> _requirement;
public RequirementHub(IRepository<Requirement> requirement)
{
_requirement = requirement;
}
public override Task OnConnectedAsync()
{
Groups.AddToGroupAsync(Context.ConnectionId, GetGroupName());
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception e)
{
Groups.RemoveFromGroupAsync(Context.ConnectionId, GetGroupName());
return base.OnDisconnectedAsync(e);
}
public IEnumerable<Requirement> Read()
{
var data = _requirement.GetAll();
return data;
}
public async Task Update(Requirement model)
{
await _requirement.UpdateAsync(model);
await Clients.OthersInGroup(GetGroupName()).SendAsync("update", model);
}
public string GetGroupName()
{
return GetRemoteIpAddress();
}
public string GetRemoteIpAddress()
{
return Context.GetHttpContext()?.Connection.RemoteIpAddress.ToString();
}
}
}
This now brings us to the grid itself. Here is the code for the requirement grid, again, there are 4 grids and they are all structured the same but with different names and referencing their respective hubs.
Home.cshtml
<div id="requirement-grid"></div>
<script>
$('#requirement_grid').kendoGrid({
dataSource: {
type: "signalr",
autoSync: true,
pageSize: 20,
sort: [
{
field: "Id",
dir: "desc"
}
],
schema: {
model: {
id: "Id",
fields: {
"Id": {
editable: false,
nullable: true
}
}
}
},
transport: {
signalr: {
promise: requirement_hub_start,
hub: requirement_hub,
server: {
read: "read",
update: "update",
create: "create",
destroy: "destroy"
},
client: {
read: "read",
update: "update",
create: "create",
destroy: "destroy"
}
}
},
autoBind: true,
reorderable: true,
sortable: true,
pageable: {
pageSize: 30,
refresh: true
},
columns: [
{
field: "Id"
}
]
});
</script>
You'll notice that the promise and hub are defined as requirement_hub_start and requirement_hub the code for those lives in its own JavaScript file as:
hubs.js
//Hub URL
var requirement_url = "/requirementHub";
var fixture_url = "/fixtureHub";
var position_url = "/positionHub";
var newswire_url = "/newswireHub";
//Connection Builder
var requirement_hub = new signalR.HubConnectionBuilder().withUrl(
requirement_url, {
transport: signalR.HttpTransportType.LongPolling
}).build();
var position_hub = new signalR.HubConnectionBuilder().withUrl(
position_url, {
transport: signalR.HttpTransportType.LongPolling
}).build();
var fixture_hub = new signalR.HubConnectionBuilder().withUrl(
fixture_url, {
transport: signalR.HttpTransportType.LongPolling
}).build();
var newswire_hub = new signalR.HubConnectionBuilder().withUrl(
newswire_url, {
transport: signalR.HttpTransportType.LongPolling
}).build();
//Hub Start
var position_hub_start = position_hub.start({
json: true
});
var requirement_hub_start = requirement_hub.start({
json: true
});
var fixture_hub_start = fixture_hub.start({
json: true
});
var newswire_hub_start = newswire_hub.start({
json: true
});
I looked at the documentation and have placed my SignalR Service connection string in my appsettings.json (until I can get this working):
appsettings.json
"Azure": {
"SignalR": {
"ConnectionString": "Endpoint=https://mycompanysignalr.service.signalr.net;AccessKey=xxx=;Version=1.0;",
"Enabled": "true"
}
}
Additionally, I have ensured that web sockets are switched ON in my Azure web app which was another recommendation of the documentation. I'm a little confused at this point, I've been over my setup 100 times and I can't see anything obvious that could be preventing data coming back.
Is there another step I need to follow or have I done something wrong? Has anyone had this problem before?

Please check the Signalr ConnectionStrings in your appsettings.json. You can copy it from azure portal and replace it, then try.
The above suggestion is to consider whether you have used different Signalr ConnectionStrings from the Dev environment to the production environment, which caused this problem.
In addition, what pricing tier are you using? If you use Free tier, azure signalr service will limit your use.
That will happen, you have no problem at the beginning, the program is normal, and when the limit is exceeded, the program will be abnormal. At the beginning of the next day, it can be used for a while. This is a restriction given by the Free pricing tier.

Related

ASP.NET Core: OpenIdConnect: message.State is null or empty

I get this error:
OpenIdConnectAuthenticationHandler: message.State is null or empty.
with the URL https://localhost:7208/home/index, but the authentication works with the url https://localhost:7208/.
Can anyone help me understand this?
enter image description here
enter image description here
This is my code:
Program.cs:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using System.Security.Claims;
using System.Web.Mvc;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddMvc().AddSessionStateTempDataProvider();
builder.Services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
});
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
})
.AddOpenIdConnect(options =>
{
options.Authority = builder.Configuration["OpenIdConfigurations:Authority"];
options.MetadataAddress = builder.Configuration["OpenIdConfigurations:MetadataAddress"];
options.ResponseType = builder.Configuration["OpenIdConfigurations:ResponseType"];
options.GetClaimsFromUserInfoEndpoint = Convert.ToBoolean(builder.Configuration["OpenIdConfigurations:GetClaimsFromUserInfoEndpoint"]);
options.RequireHttpsMetadata = Convert.ToBoolean(builder.Configuration["OpenIdConfigurations:RequireHttpsMetadata"]);
options.ClientId = builder.Configuration["OpenIdConfigurations:ClientId"];
options.ClientSecret = builder.Configuration["OpenIdConfigurations:ClientSecret"];
options.CallbackPath = builder.Configuration["OpenIdConfigurations:CallbackPath"];
});
var app = builder.Build();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
app.Run();
Controller:
namespace OIDCMVC.Controllers
{
[Authorize]
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
return View();
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Note: We use a private provide and expects the call back path.
Callbackpath = "/home/index"
In my case I am using blazor (.net 6) and trying to protect hangfire with Microsoft oauth. To get the auth screen of microsoft login when locating to /hangfire. The solution to this error was as simple as removing CallbackPath from my settings:
// settings found under Azure AD -> App Registration -> Your registration
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "domain", // found under Branding and properties -> publisher domain like : ...outlook.onmicrosoft.com
"TenantId": "tenantid", // found under Overview Directory (tenant) ID GUID
"ClientId": "client_id" // found under Overview Application (client) ID GUID
//"CallbackPath": "/hangfire" REMOVED To get rid off message.State is null error
}
The setup from Program.cs:
services.AddAuthentication().AddMicrosoftIdentityWebApp(configuration.GetSection("AzureAd"));
services.AddAuthorization(options =>
{
options.AddPolicy("Hangfire", builder =>
{
builder
.AddAuthenticationSchemes(OpenIdConnectDefaults.AuthenticationScheme)
.RequireAuthenticatedUser();
});
});
services.AddHangfire(x =>
{
x.UseSqlServerStorage(connectionString);
x.UseConsole();
});
services.AddHangfireServer();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHangfireDashboard("/hangfire", new DashboardOptions()
{
Authorization = new List<IDashboardAuthorizationFilter> { new HangfireAuthorizeFilter() },
}).RequireAuthorization("Hangfire");
});
app.UseHangfireDashboard();
Authorization filter:
public class HangfireAuthorizeFilter:
IDashboardAuthorizationFilter
{
public bool Authorize(DashboardContext context)
{
var userIdentity = context.GetHttpContext().User.Identity;
return userIdentity is { IsAuthenticated: true };
}
}
And the app registration from Azure AD:
Under your app registration click: Authentication -> Mark "ID tokens" and enter your redirect urls, like: https://localhost:52908/hangfire
I've been integrating multiple custom policies (different flows) in a single app. Removing CallbackPath didn't work as request were coming there from multiple sources, not just the default sign in policy. We were receiving "message.State is null or empty" page, but after navigating to the base path of the app, the user was authenticated and properly logged in.
What ultimately helped was setting SkipUnrecognizedRequests property to true:
.AddOpenIdConnect(options =>
{
...
options.SkipUnrecognizedRequests = true;
});
or using appsettings:
{
...
"AzureAdB2C": {
"Instance": "",
"Domain": "",
"ClientId": "",
...
"SkipUnrecognizedRequests": true
},
...
}
According to the OpenIdConnectOptions.SkipUnrecognizedRequests documentation:
Indicates if requests to the CallbackPath may also be for other components. If enabled the handler will pass requests through that do not contain OpenIdConnect authentication responses. Disabling this and setting the CallbackPath to a dedicated endpoint may provide better error handling. This is disabled by default.
Also, maybe related issue here: OIDC handler running on '/' crashes even with SkipUnrecognizedRequests set to true #10028

.NET Core 6 Web Api Authentication/ Authorization not working

I have written a small .NET 6 minimal web api, which is heavily based on / copied from a tutorial I did. The tutorial worked as expected, but I have two problems in my version.
The first problem is with swagger. When I uncomment the line c.OperationFilter<SecurityRequirementsOperationFilter>(); in AddSwaggerGen() I get a swagger error in the browser "Failed to load API definition. Fetch error. response status is 500 https://localhost:7123/swagger/v1/swagger.json"
The second problem is (when the swagger line is commented out to get it to run) nothing is secured. Either from swagger or from the browser I can access all endpoints.
Here is my program.cs. It certainly looks like I have everything in the right place. Even so, I would have expected that if it was misconfigured, it would prevent me from accessing the endpoints.
using DataAccess.DbAccess;
using MyAuthAPI;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(
c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyAPI", Version = "v1" });
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme {
Description = "Standard Authorization header using the Bearer scheme. Example: \"bearer {token}\"",
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
// When this line is uncommented I get a swagger error- Failed to load api
// c.OperationFilter<SecurityRequirementsOperationFilter>();
});
builder.Services.AddSingleton<ISqlDataAccess, SqlDataAccess>();
builder.Services.AddSingleton<IPersonAuthData, PersonAuthData>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes(builder.Configuration.GetSection("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
// app.UseStatusCodePages();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyAPI v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.ConfigureRoutes();
app.Run();
The routes and db access etc all works fine, but here is the routes.cs file just in case.
using DataAccess.DTOs;
using Microsoft.AspNetCore.Authorization;
namespace MyAuthAPI;
[Authorize]
public static class Routes
{
public static void ConfigureRoutes(this WebApplication app)
{
app.MapGet("PersonAuths", GetPersonAuths);
app.MapGet("PersonAuths/{id}", GetPersonAuth);
app.MapPost("Login", Login);
}
private static async Task<IResult>GetPersonAuths(IPersonAuthData data)
{
try
{
return Results.Ok(await data.GetPersonAuths());
} catch(Exception ex)
{
return Results.Problem(ex.Message);
}
}
private static async Task<IResult> GetPersonAuth(int id, IPersonAuthData data)
{
try
{
var results = await data.GetPersonAuth(id);
if (results == null) return Results.NotFound();
return Results.Ok(results);
}
catch (Exception ex)
{
return Results.Problem(ex.Message);
}
}
[AllowAnonymous]
private static async Task<IResult> Login(LoginDTO loginDetails, IPersonAuthData data)
{
try
{
string jwt = await data.Login(loginDetails);
if (jwt != "")
{
return Results.Ok(jwt);
} else
{
return Results.Unauthorized();
}
}
catch (Exception ex)
{
return Results.Problem(ex.Message);
}
}
}
Thanks
Edit
The swagger issue looks like it may be due to Swashbuckle.AspNetCore.Filters not supporting minimal apis.
Swashbuckle.AspNetCore.Filters

AddWebhookNotification to call Method in Controller

I have this configured in my StartUp.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.ConfigureEmail(Configuration)
.AddHealthChecksUI(setupSettings: setup =>
{
setup
.AddWebhookNotification("WebHookTest", "/WebhookNotificationError",
"{ message: \"Webhook report for [[LIVENESS]]: [[FAILURE]] - Description: [[DESCRIPTIONS]]\"}",
"{ message: \"[[LIVENESS]] is back to life\"}",
customMessageFunc: report =>
{
var failing = report.Entries.Where(e => e.Value.Status == UIHealthStatus.Unhealthy);
return $"{failing.Count()} healthchecks are failing";
},
customDescriptionFunc: report =>
{
var failing = report.Entries.Where(e => e.Value.Status == UIHealthStatus.Unhealthy);
return $"HealthChecks with names {string.Join("/", failing.Select(f => f.Key))} are failing";
});
})
.AddControllers();
}
public void Configure(IApplicationBuilder app)
{
var pathBase = Configuration["PATH_BASE"];
if (!string.IsNullOrEmpty(pathBase))
{
app.UsePathBase(pathBase);
}
app.ConfigureExceptionHandler();
app
.UseRouting()
.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecksUI(options =>
{
options.ResourcesPath = string.IsNullOrEmpty(pathBase) ? "/ui/resources" : $"{pathBase}/ui/resources";
options.UIPath = "/hc-ui";
options.ApiPath = "/api-ui";
});
endpoints.MapDefaultControllerRoute();
});
}
And in the Controller:
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
public async Task<IActionResult> WebhookNotificationError([FromBody] string id)
{
MimeMessage mimeMessage = new MimeMessage { Priority = MessagePriority.Urgent };
mimeMessage.To.Add(new MailboxAddress(_configuration.GetValue<string>("ConfiguracionCorreoBase:ToEmail")));
mimeMessage.Subject = "WebHook Error";
BodyBuilder builder = new BodyBuilder { HtmlBody = id };
mimeMessage.Body = builder.ToMessageBody();
await _appEmailService.SendAsync(mimeMessage);
return Ok();
}
The watchdog application is configured in the appSettings.json to listen to different APIs.
So far everything works fine, but, if I force an error, I'd like to receive a notification email.
The idea is that, when an error occurs in any of the Healths, you send an email.
Environment:
.NET Core version: 3.1
Healthchecks version: AspNetCore.HealthChecks.UI 3.1.0
Operative system: Windows 10
It's look like you problem in routes. Did you verify that method with Postman?
Also check if your webHook request body is a text, try to change your template payload:
{ "message": "Webhook report for [[LIVENESS]]: [[FAILURE]] - Description: [[DESCRIPTIONS]]"}",
and in the controller change string to object. And check what you receive in DEBUG.
Try using Api/WebhookNotificationError inst. of /WebhookNotificationError if your controller is ApiController. The controller name seems to be missing
I think you should try this. It works for me.
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
public async Task<IActionResult> WebhookNotificationError()
{
using (var reader = new StreamReader(
Request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false))
{
var payload = await reader.ReadToEndAsync();
//do whatever with your payloade here..
//I am just returning back for a example.
return Ok(payload);
}
}

.Net Core 2.2 Health Checks UI gives blank page

I follow all the articles I can find including this one Microsoft Health Checks article.
The health checks work properly and the /health url of the application returns json, healthy status as expected.
However, the /healthchecks-ui returns blank page. On the console developer tools I can see the error, "Uncaught SyntaxError: Unexpected token {" in .healthchecks-bundle.js:1.
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<Model>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// Health checks services
services
.AddHealthChecks()
.AddMemoryHealthCheck("memory")
.AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);
services.AddHealthChecksUI();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
//app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseHealthChecks("/health", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecksUI();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "defaultApi",
template: "api/{controller=MyController}/{action=Get}/{value?}");
});
}
}
appsettings.json
{
"HealthChecksUI": {
"HealthChecks": [
{
"Name": "HealthChecksService",
"Uri": "http://localhost:42008/health"
}
],
"Webhooks": [],
"EvaluationTimeInSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}
}
I also tried using HealthChecks-UI but it didn't affect.
I included of course the nuget packages Microsoft.AspNetCore.Diagnostics.HealthChecks (2.2.0) , AspNetCore.HealthChecks.UI (2.2.35).
Again, the /health returns json indicates healthy application but the UI returns blank page with js error. (Tried on both chrome and IE).
The environment is a closed one so if the ui tries to refer external resources from the internet it will fail but I don't see such a call.
There are 2 problems with what you're doing:
You need to set the HealthChecks.UI.Configuration.Options correctly in the UseHealthChecksUI method. You need to override the default paths and the UseRelativeApiPath and UseRelativeResourcesPath settings.
The path you are going to to view the UI in the browser (according to your settings) should be /health-ui
Here is the code I used to get your example working:
appsettings.json:
{
"HealthChecksUI": {
"HealthChecks": [
{
"Name": "HealthChecksService",
"Uri": "http://localhost:42008/health"
}
],
"Webhooks": [],
"EvaluationTimeInSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddHealthChecks()
.AddMemoryHealthCheck("memory")
.AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"]);
services.AddHealthChecksUI();
...
}
public void Configure(IApplicationBuilder app)
{
...
app.UseCustomHealthChecks();
...
}
ApplicationBuilderExtensions.cs:
public static IApplicationBuilder UseCustomHealthChecks(this IApplicationBuilder builder)
{
const string healthCheckApiPath = "/health";
return builder
.UseHealthChecks(healthCheckApiPath,
new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
})
.UseHealthChecksUI(options =>
{
options.UIPath = $"{healthCheckApiPath}-ui";
options.ApiPath = $"{healthCheckApiPath}-api";
options.UseRelativeApiPath = false;
options.UseRelativeResourcesPath = false;
});
}
HTH
Here's what worked for me:
I am using netcore 2.2 with AspNetCore.HealthChecks.UI Version=2.2.2.
Inside my Configure method:
app.UseHealthChecks("/healthz", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseHealthChecksUI(setup =>
{
setup.UIPath = "/healthchecks-ui"; // ui path
setup.ApiPath = "/health-ui-api"; // this is the internal ui api
});
appsettings.json
"HealthChecks-UI": {
"HealthChecks": [
{
"Name": "HealthChecksService",
"Uri": "http://localhost:42008/healthz"
}
],
"Webhooks": [],
"EvaluationTimeOnSeconds": 120,
"MinimumSecondsBetweenFailureNotifications": 3660
}

SignalR Core not generating(mapping) Client methods

The SignalR Core is generating Hub proxies script, but not adding the "client" methods. (No errors in server or client - only not working)
Generated JS from <script src="http://localhost/signalr/hubs">
proxies['messageHub'] = this.createHubProxy('messageHub');
proxies['messageHub'].client = { };
proxies['messageHub'].server = {
handleMessage: function (receivedString) {
return proxies['messageHub'].invoke.apply(proxies['messageHub'], $.merge(["HandleMessage"], $.makeArray(arguments)));
}
};
Here's the Hub in Server Side:
public class MessageHub : Hub
{
public void HandleMessage(string receivedString)
{
var responseString = string.Empty;
MessageHandler.HandleMessage(receivedString, ref responseString);
Clients.All.sendMessage(responseString);
}
}
The sendMessage methos should be included in the messageHub client proxies in the JS file.
$.connection.messageHub.client.sendMessage is undefined
Only the handleMessage for server proxies was created (and working).
Here's my StartUp.cs inclusions for SignalR:
ConfigureServices:
services.AddMvc(options =>
{
options.Filters.Add(new RoleFilterAttribute());
}).AddJsonOptions(options => options.SerializerSettings.ContractResolver =
new DefaultContractResolver());
services.AddSignalR(options => options.Hubs.EnableDetailedErrors = true)
Configure:
app.UseWebSockets();
app.UseSignalR();
project.json:
"Microsoft.AspNetCore.Mvc": "1.0.0-*",
"Microsoft.AspNetCore.WebSockets": "1.0.0",
"Microsoft.AspNetCore.SignalR.Server": "0.2.0-*",
SOME ADDITIONAL TENTATIVES:
1 - Change method case in Server Side to see if it's mapped:
Clients.All.SendMessage(responseString);
Did not work!
2 - Change the client side to dynamic mapping:
var connection = $.hubConnection('http://localhost/');
var proxy = connection.createHubProxy('messageHub');
connection.start({ withCredentials: false }).done(function () { console.log("CONNECTED") });
proxy.on("sendMessage", function (result) {console.log(result);});
proxy.invoke("handleMessage", msg).done(function(result)console.log(result);});
Again only the handleMessage (server) worked.
Well according to the docs you are missing method name so the send all line should look like this
public void HandleMessage(string receivedString)
{
var responseString = string.Empty;
MessageHandler.HandleMessage(receivedString, ref responseString);
Clients.All.SendMessage("SendMessage",responseString);
}
Also in the following is the correct way
app.UseSignalR(routes =>
{
routes.Hub<MessageHub>("/messageHub");
});
and finally
var connection = $.hubConnection('http://localhost/');
var proxy = connection.createHubProxy('messageHub');
connection.start({ withCredentials: false }).done(function () { console.log("CONNECTED") });
proxy.on("SendMessage", function (result) {console.log(result);});
proxy.invoke("HandleMessage", msg).done(function(result)console.log(result);});
ASP.NET Core SignalR doesn't generate client proxies. There's good advice in the comment to follow the tutorial https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-6.0&tabs=visual-studio