.Net Core 2.2 Health Checks UI gives blank page - asp.net-core

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
}

Related

Why am I getting no data from my Azure SignalR Service back to my KendoUI Grid?

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.

ASP.NET Core 5: OpenIDConnect breaking default/root route

I have an ASP.NET Core 5 MVC app, with the default/root route set like this inside PageController:
[AllowAnonymous]
[Route("/")]
public IActionResult __Home(int? parent)
{
return View();
}
This worked fine until I added OpenIdConnect authentication. After that, the root (/) page no longer routes to __Home in the PageController, it just returns a blank page. All other pages route just fine.
When I comment out this:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration, "AzureAdB2C");
then / works again, so I know it's something to do with the authentication. As you can see, I have added [AllowAnonymous] to that action.
I have this in my startup:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
});
Any ideas on how to fix this? I know it's unconventional to have the default/root route in a weird controller/action like that, but there are reasons for it, so I'm hoping it can still work.
More Info:
I found that if I move app.UseEndpoints above app.UseAuthentication, then the home page shows. After logging in (with B2C), however, it goes into an infinite loop (i.e. the authentication token doesn't stick?).
EDIT: My Startup.cs class
using Blank.Models;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
namespace Blank
{
public class Startup
{
private readonly AppSettings appSettings = null;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
this.appSettings = new AppSettings();
this.Configuration.Bind(this.appSettings);
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration, "AzureAdB2C");
services.AddSession();
services.Configure<OpenIdConnectOptions>(Configuration.GetSection("AzureAdB2C"));
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.Configure<AppSettings>(this.Configuration);
services.AddEntityFrameworkSqlServer().AddDbContext<BlankDBContext>(
Options => Options.UseSqlServer(Microsoft.Extensions.Configuration.ConfigurationExtensions.GetConnectionString(this.Configuration, "BlankDatabase"))
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Page}/{action=Index}/{id?}");
});
}
}
}
Edit 2
I think that app.UseAuthentication() is breaking/returning the blank page, because when I put the following code before app.UseAuthentication() I get something on the home page, and if it's after then blank:
app.Use(async (context, next) =>
{
var endpoint = context.GetEndpoint();
if (endpoint != null)
{
await context.Response.WriteAsync("<html> Endpoint :" + endpoint.DisplayName + " <br>");
if (endpoint is RouteEndpoint routeEndpoint)
{
await context.Response.WriteAsync("RoutePattern :" + routeEndpoint.RoutePattern.RawText + " <br>");
}
}
else
{
await context.Response.WriteAsync("End point is null");
}
await context.Response.WriteAsync("</html>");
await next();
});
So perhaps it has to do with my authentication? Here's my appsettings.json:
"AzureAdB2C": {
"Instance": "https://abc.b2clogin.com",
"Domain": "abc.onmicrosoft.com",
"ClientId": "62...f1",
"TenantId": "7e...ae",
"SignUpSignInPolicyId": "B2C_1_SUSI",
"SignedOutCallbackPath": "/"
},
Turns out the problem was this in my appsettings.json:
"SignedOutCallbackPath": "/"
Removing this fixed the problem, and the home page now loads correctly.

Swagger doc has empty paths

My Swagger document is not properly generated, I have the basic information (title, name, licence etc) but no doc on my routes.
Here's the setup in Startup.cs :
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<APIContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "OpenWeb Challenge",
Description = "A 'simple' example ASP.NET Core Web API",
TermsOfService = new Uri("https://example.com/terms"),
Contact = new OpenApiContact
{
Name = "Anthony Da Silva Ferreira",
Email = string.Empty,
Url = new Uri("https://twitter.com/spboyer"),
}
});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "OpenWebChallenge V1");
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "default",pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
And the content of the swagger :
{
"openapi": "3.0.1",
"info": {
"title": "OpenWeb Challenge",
"description": "A 'simple' example ASP.NET Core Web API",
"termsOfService": "https://example.com/terms",
"contact": {
"name": "Anthony Da Silva Ferreira",
"url": "https://twitter.com/spboyer",
"email": ""
},
"license": {
"name": "Licence",
"url": "https://example.com/license"
},
"version": "v1"
},
"paths": { },
"components": { }
}
Controller sample :
public class ContactsController : Controller
{
private readonly APIContext _context;
public ContactsController(APIContext context)
{
_context = context;
}
// GET: Contacts
public async Task<IActionResult> Index()
{
return View(await _context.Contacts.ToListAsync());
}
// GET: Contacts/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var contact = await _context.Contacts
.Include(c => c.ContactSkills)
.ThenInclude(cs => cs.Skill)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.Id == id);
if (contact == null)
{
return NotFound();
}
return View(contact);
}
}
Am I missing any configuration or something? It's my first time creating API from scratch.
Have you added the PropertyGroup to the PROJECT_NAME.csproj file?
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
Without it the compiler will not generate the documentation file.
Microsoft Documentation
edit
in the documentation it says: "You must use attribute routing for any controllers that you want represented in your Swagger document(s)" Link.
so for your controller it would be:
[Route("api/contacts")]
public class ContactsController : Controller
{
...
// GET: Contacts
[HttpGet("")]
public async Task<IActionResult> Index()
{
...
}
// GET: Contacts/Details/5
[HttpGet("/details/{id?}")]
public async Task<IActionResult> Details([FromRoute] int? id)
{
...
}
}
edit
i used wrong route template syntax:
[HttpGet("/details/:id?")] -> [HttpGet("/details/{id?}")]
? for optional parameter
From your code, the ContactsController seems like a MVC controller (it will return views), instead of API controller.
The Swagger (OpenAPI) is a language-agnostic specification for describing REST APIs, instead of MVC Controller. So, it will not generate Swagger document for the MVC controller. Try to add a API controller, then, add API info and description using Swagger.
More details information about using Swagger, check Get started with Swashbuckle and ASP.NET Core

.Net Core 2.0 Web API OpenIddict Authorization: redirecting to index instead of returning json data

So, the problem is that when I use the AuthorizeAttribute on top of my api controller, it stops working the expected way.
When I call a getAllUsers action, instead of returning the users in json format, the Identity somehow redirects to index.html and then I get a json parser error in my Angular client app, because html is not valid json data that can be parsed.
This started to happen after upgrading to Asp.Net Core 2.0.
I think that perhaps I have to change something in my Startup.cs or Program.cs. But I can't figure out what.
I have followed the Refresh Token Sample on OpenIddict for the new Core 2.0, and everything seems to be ok.
So here is my code...
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options => {
options.UseSqlServer(Configuration.GetConnectionString("LocalDB"))
.UseOpenIddict();
});
services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IRoleRepository, RoleRepository>();
services.AddScoped<IManadRepository, ManadRepository>();
services.AddScoped<IManadRubricaRepository, ManadRubricaRepository>();
services.AddScoped<IManadSistemaRepository, ManadSistemaRepository>();
services.AddScoped<IRestituicaoRepository, RestituicaoRepository>();
services.AddTransient<ApplicationDbSeedData>();
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.User.RequireUniqueEmail = true;
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddOpenIddict(options =>
{
options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/connect/token");
options.AllowPasswordFlow();
options.AllowRefreshTokenFlow();
if (!_env.IsProduction())
options.DisableHttpsRequirement();
});
// Add framework services.
services.AddMvc();
services.AddAuthentication()
.AddOAuthValidation();
services.AddAuthorization();
services.AddTransient<IMailSender, MailjetSender>();
services.AddScoped<IManadParser, ManadParser>();
}
public void Configure(IApplicationBuilder app, ApplicationDbSeedData dbDataSeeder)
{
if (_env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
}
Mapper.Initialize(cfg =>
{
cfg.AddProfile<AutoMapperProfile>();
});
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
dbDataSeeder.EnsureSeedData().Wait();
}
UsersController.cs
[Route("api/[controller]")]
[Authorize]
public class UsersController : Controller
{
[HttpGet]
[Authorize(Roles = "Administrador")]
public IActionResult GetAllUsers()
{
try
{
var result = _repository.GetAllUsers();
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError($"Failed to get all users: {ex}");
return BadRequest(ex.Message);
}
}
}
If I put a breakpoint in the GetAllUsers method, it never gets hitted. Somehow because of authorization, the application redirects to index.html before.
Program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
By the way, authentication is working. I am able to get the tokens, but unable to authorize the controller access.
Solved it. Just needed some bit of configuration just like I thought. Just add DefaultAuthenticateScheme option like this:
services.AddAuthentication(options => options.DefaultAuthenticateScheme = OAuthValidationDefaults.AuthenticationScheme)
.AddOAuthValidation();
After adding this, the controller started to work correctly, resulting json data and not index.html.

Asp.net Core MVC Authorize Attribute not blocking

Authorize attribute is not working. I am not logged in and it allows me to access this function.
i have played around with my Startup.cs attached at the bottom. Please help me get started with this. I have successfully used these methods on previous version of MVC, but I am not successful yet with MVC core.
After this I am looking to add roles. Any direction on where to start with that would be appreciated.
Thanks
public class SecurityAccessController : Controller
{
private SecurityAccessDbContext SecurityAccessDbContext { get; set; }
public SecurityAccessController([FromServices] SecurityAccessDbContext SecurityAccessDbContext)
{
this.SecurityAccessDbContext = SecurityAccessDbContext;
}
// GET: /<controller>/
[Authorize]
public IActionResult Index()
{
return View();
}
}
This is my Start Up.cs
Updated as recommended by the below comment
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMemoryCache();
services.AddSession();
//Added
services.AddBootstrapPagerGenerator(options => {options.ConfigureDefault();});
//Database services
services.AddEntityFrameworkSqlServer().AddDbContext<SecurityAccessDbContext>(options => { options.UseSqlServer(Configuration["ConnectionStrings:Accumatica"]); });
services.AddEntityFrameworkSqlServer().AddDbContext<AcumaticaDbContext>(options => { options.UseSqlServer(Configuration["ConnectionStrings:Accumatica"]); });
services.AddEntityFrameworkSqlServer().AddDbContext<RMADbContext>(options => { options.UseSqlServer(Configuration["ConnectionStrings:Accumatica"]); });
services.AddEntityFrameworkSqlServer().AddDbContext<WarrantyDbContext>(options => { options.UseSqlServer(Configuration["ConnectionStrings:Accumatica"]); });
services.AddEntityFrameworkSqlServer().AddDbContext<GenericDbContext>(options => { options.UseSqlServer(Configuration["ConnectionStrings:Accumatica"]); });
services.AddEntityFrameworkSqlServer().AddDbContext<ApplicationIdentityDbContext>(options => { options.UseSqlServer(Configuration["ConnectionStrings:Accumatica"]); });
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Cookies.ApplicationCookie.LoginPath = "/Account/Login";
options.Cookies.ApplicationCookie.AccessDeniedPath = "/Home/AccessDenied";
})
.AddEntityFrameworkStores<ApplicationIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
services.AddTransient<IEmailSender, AuthMessageSender>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSession();
app.UseIdentity();
app.UseMvcWithDefaultRoute();
}
Add Identity before adding Mvc. Furthermore you don't need to add Authorization as that's already done when adding Identity as seen here. You also can configure your identity options such as the login path, without needing configure CookieAuthenticationOptions. Instead you can configure it when adding Identity.
Here's a snippet of what the code could look like.
// Remove me
// services.AddAuthorization();
// Remove me too
// services.Configure<CookieAuthenticationOptions>(options =>
// ....
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.Cookies.ApplicationCookie.LoginPath = "/Account/Login";
options.Cookies.ApplicationCookie.AccessDeniedPath = "/Home/AccessDenied";
options.Cookies.ApplicationCookie.AutomaticChallenge = true;
options.Cookies.ApplicationCookie.AutomaticAuthenticate = true;
})
.AddEntityFrameworkStores<ApplicationIdentityDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
I found the issue
the file launchsettings.json had
"iisSettings": {
"windowsAuthentication": true,
I changed to
"iisSettings": {
"windowsAuthentication": false,
The above answer also helped me, but I can add that if you want the [AllowAnonymous] attribute to work you will also need to change the anonymousAuthentication to true:
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,