I'm trying to use app.UseExceptionHandler("/error") so I can handle all the errors using the ProblemDetails.
However, I never get rerouted to the ErrorController. When I set a breakpoint, the debugger never gets inside this endpoint.
Does anyone know why I'm not hitting my breakpoint?
The code in the program.cs is :
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
{
// 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
.AddApplication()
.AddAInfrastructure(builder.Configuration);
}
WebApplication app = builder.Build();
{
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(q => q.SwaggerEndpoint("/swagger/v1/swagger.json","PowerPlanner v1"));
}
app.UseExceptionHandler("/error"); // <===============
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
}
app.Run();
Controller:
using Microsoft.AspNetCore.Mvc;
[ApiController]
public class ErrorsController : ControllerBase
{
[HttpGet("/error")]
public IActionResult Error()
{
return Problem();
}
}
Solution:
Because I Used Swagger, I couldn't declare the route with the attribute [Route("/error"]) without getting an error. To solve this, the entire controller has to be marked with :
[ApiExplorerSettings(IgnoreApi = true)]
like this:
[ApiExplorerSettings(IgnoreApi = true)] //<====
public class ErrorController : ControllerBase
{
[Route("/error")] //<=== this can only be 'route' when the controller is marked with ignoreApi=false
public IActionResult ErrorPost()
{
return Problem(;
}
}
Documentation : https://learn.microsoft.com/en-us/aspnet/core/web-api/handle-errors?view=aspnetcore-7.0
Related
I have custom routing for my ASP.NET Core 6 Web API:
[ApiController]
[Route("ForecastService")]
public class WeatherForecastController : ControllerBase
{
[HttpGet("forecast")]
public IEnumerable<string> Get1()
{
return new[] { "forecast" };
}
}
I have made this change in the Swagger configuration:
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
//app.UseSwagger();
//app.UseSwaggerUI();
app.UseSwagger(c => c.RouteTemplate = "ForecastService/{documentName}/swagger.json");
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("ForecastService/v1/swagger.json", "Forcast API");
c.RoutePrefix = "ForecastService/swagger";
});
}
Now while trying to run browse to http://localhost:5113/ForecastService/swagger/index.html, I am getting an error:
Not found http://localhost:5113/ForecastService/swagger/ForecastService/v1/swagger.json
What am I missing here?
Change your configuration for swagger like below:
//miss the swagger after ForecastService.....
app.UseSwagger(c => c.RouteTemplate = "ForecastService/swagger/{documentName}/swagger.json");
app.UseSwaggerUI(c =>
{
//miss the `/` in the beginning and also miss the swagger after ForecastService...
c.SwaggerEndpoint("/ForecastService/swagger/v1/swagger.json", "Forcast API");
c.RoutePrefix = "ForecastService/swagger";
});
modify below lines as
app.UseSwagger();
app.UseSwaggerUI();
Use URL as
http://localhost:5113/swagger
I create an empty asp.net core project base on .net6 and do load testing using python locust. The average response time is about 900ms, but using MiniProfile monitors the API executed time, almost zero. That's why? How to optimize it?
Load test result:
Use MiniProfile to monitor the APIs execution time:
Resource usage:
Program.cs:
var builder = WebApplication.CreateBuilder(args);
// 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.AddMemoryCache();
builder.Services.AddMiniProfiler(opt =>
{
opt.RouteBasePath = "/profiler";
});
var app = builder.Build();
app.UseMiniProfiler();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
// app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
HomeController.cs:
using Microsoft.AspNetCore.Mvc;
namespace del.Controllers;
[Route("[controller]")]
public class HomeController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok();
[HttpPost]
public IActionResult Post() => Ok();
}
Python script:
from locust import HttpUser, task
class Date(HttpUser):
#task
def get_date(self):
self.client.get('/home')
locust has some performance problems
I've found this in official documentation -
We recommend against combining policies. Use the [EnableCors]
attribute or middleware, not both in the same app.
My scenario is quite simple - I want to enable CORS globally but disable it only for one specific controller endpoint (endpoint is used on frontend widget which can be embedded on any site so I can't have CORS on that endpoint).
I don't understand why they are recommending against combining both approaches - not only that they don't recommend but it just doesn't work.
This is the setup of CORS:
services.AddCors(opts =>
{
opts.AddPolicy(nameof(MyCorsPolicy), new MyCorsPolicy());
});
And this is registration in Configure method of startup
public void Configure(
IApplicationBuilder app,
IWebHostEnvironment env)
{
app.UseRouting();
app.UseCors(nameof(MyCorsPolicy));
app.UseHsts();
app.UseExceptionHandler(env);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
And now in my XY controller method I have [DisableCors] attribute which just doesn't work.
Any help would be appreciated. Thank you.
After hundreds of tests and internal .NET Core debugging, only way I could implement this is by using global CORS:
services.AddCors(opts =>
{
opts.AddPolicy(nameof(MyCorsPolicy), new MyCorsPolicy());
});
Then I'd create another policy
public class AllowAnyCorsPolicy : CorsPolicy
{
public AllowAnyCorsPolicy()
{
Origins.Clear();
IsOriginAllowed = origin => true;
Headers.Clear();
Headers.Add("*");
Methods.Clear();
Methods.Add("*");
SupportsCredentials = true;
}
}
And apply that policy to specific controller method e.g.
[EnableCors(nameof(AllowAnyCorsPolicy))]
[HttpPost("/user/add")]
[AllowAnonymous]
public async Task<IActionResult> AddUser(UserRequestModel requestModel)
{
// ...
}
If I used [DisableCors] or even used default policy registration and then added pure [EnableCors] attribute to controller method, it just wouldn't work. Pretty weird way of their implementation because I think this can be simplified a lot, and I have no idea how this might behave in future, so we might even consider writing our own full CORS middleware.
Way 1. Because a default policy hasn't been configured, app.UseCors() alone doesn't enable CORS. Use RequireCors to enable all controllers.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireCors(MyCorsPolicy);//Enable Cors with endpoint routing
// /xy/getvalues2 and Razor Pages not allow cross-origin requests because no default policy was specified.
endpoints.MapGet("/xy/getvalues2",
context => context.Response.WriteAsync("xy/getvalues2")); //do XY Controller Action logic
endpoints.MapRazorPages();
});
}
Way 2. The [DisableCors] attribute does not disable CORS that has been enabled by endpoint routing. Uses [EnableCors("MyCorsPolicy")] to enable the "MyCorsPolicy" CORS policy for each controller. Disables CORS for the GetValues2 method.
[EnableCors("MyCorsPolicy")]
[Route("api/[controller]")]
[ApiController]
public class XYController : ControllerBase
{
// GET api/values
[HttpGet]
public IActionResult Get() =>
ControllerContext.MyDisplayRouteInfo();
// GET: api/values/GetValues2
[DisableCors]
[HttpGet("{action}")]
public IActionResult GetValues2() =>
ControllerContext.MyDisplayRouteInfo();
}
So I was following this tutorial: https://www.yogihosting.com/aspnet-core-identity-create-read-update-delete-users/ teaching how to make a CRUD for Identity users.
I've reached a point where I am getting 2 errors which I think are tied together. Firstly, I will present the code:
AdminController.cs
public ViewResult Create() => View();
[HttpPost]
public async Task<IActionResult> Create(User user)
{
if (ModelState.IsValid)
{
AppUser appUser = new AppUser
{
UserName = user.Name,
Email = user.Email
};
IdentityResult result = await userManager.CreateAsync(appUser, user.Password);
if (result.Succeeded)
return RedirectToAction("Index");
else
{
foreach (IdentityError error in result.Errors)
ModelState.AddModelError("", error.Description);
}
}
return View(user);
}
AppUser.cs
public class AppUser : IdentityUser
{
}
When accessing the `localhost/Admin/Create' link after running the application, I am getting this error:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[Intersection.Models.AppUser]' while attempting to activate 'Filters.Controllers.AdminController'.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
Then, I figured that something might be wrong in the Startup.cs, therefore, after a bit of research, I added this line: services.AddIdentity<AppUser, IdentityRole>().AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
In an attempt to fix the first error, I got this second issue:
HTTP Error 500.30 - ANCM In-Process Start Failure
My Startup.cs class looks like this:
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.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddIdentity<AppUser, IdentityRole>().AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// 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.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
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.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
So what could be the issue? I tried following the tutorial closely but apparently I missed something out... Or things changed since it was posted. Any help is highly appreciated!
HTTP Error 500.30 - ANCM In-Process Start Failure
This was due to methods AddDbContext and AddIdentity having duplicates in the Startup.cs. After commenting out the duplicates, I got rid of it.
Secondly, in _LoginPartial.cshtml, I had this:
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
I had to replace IdentityUser with my AppUser. This fixed the first error.
Yesterday I searched solution how to use swagger on Core Odata, I tried few libraries but with no success, it seams that currently it's not fully supported.
May be this info could be useful for somebody. Actually It's possible to use NSwag and create documentation for Odata Core from the box. There is workaround.
Just add swagger and Odata settings to Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(options =>
{
options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
options.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.DefaultContractResolver();
});
services.AddOData();
//etc
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
var builder = new ODataConventionModelBuilder(app.ApplicationServices));
builder.EntitySet<Test>(nameof(Test));
app.UseMvc(routebuilder =>
{
routebuilder.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
});
app.UseSwaggerUi(typeof(Startup).GetTypeInfo().Assembly,
settings =>
{
settings.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
});
app.UseMvc();
//etc
}
Next mark Controller with route attribute as it would WebApi. Note: route should be different from odata.
Add [EnableQuery] to your IQueryable Action. Note2: you can't use [FromODataUri] for swagger docs Action with it should be marked as [SwaggerIgnore]
[Produces("application/json")]
[Route("api/test")]
public class TestController : Controller
{
[HttpGet]
[EnableQuery]
public IQueryable<Test> Get()
{
return _testService.Query();
}
//etc
}
Get swagger run!