dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.21k stars 9.95k forks source link

Using middleware in asp net Core 8.0 #57025

Open vsfeedback opened 1 month ago

vsfeedback commented 1 month ago

This issue has been moved from a ticket on Developer Community.


I have a simpe asp core 8.0 application

with a startup.cs

public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

public void ConfigureServices(IServiceCollection services)
{
services. AddControllers();
services. AddEndpointsApiExplorer();
services. AddSwaggerGen();
DiConfiguration.RegisterServices(services);
services. AddScoped<IBookRepository, BookRepository>();
services. AddScoped<IAuthorRepository, AuthorRepository>();
services. AddDbContext();

//services. AddExceptionHandler(options =>
//{
// options. ExceptionHandlingPath = new PathString(“/Error”);
//});

services. AddIdentity<IdentityUser, IdentityRole>(options =>
{
options. SignIn.RequireConfirmedAccount = false;
}). AddEntityFrameworkStores();

services. ConfigureApplicationCookie(options =>
{
options.Cookie.Name = “auth_cookie”;
options. Cookie.SameSite = SameSiteMode.None;
options. Events.OnRedirectToLogin = context =>
{
context. Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};
options. Events.OnRedirectToAccessDenied = context =>
{
context. Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.CompletedTask;
};

});
services. AddScoped();
}
public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifetime
, ApplicationDbContext context, IdentitySeed IdentitySeed)
{
context. Database.EnsureCreated();
app. UseSwagger();
app. UseSwaggerUI();

app. UseHttpsRedirection();
app. UseRouting();
app. UseMiddleware();
app. UseExceptionHandler(“/error”);
app. UseAuthentication();
app. UseAuthorization();
app. UseEndpoints(endpoint =>
{
endpoint. MapControllerRoute(
name: “default”
, pattern: “{controller}/{action=Index}/{id}”);
});

IdentitySeed.Seed(). Wait();
}

which creates following error:

System.InvalidOperationException: "An error occurred when configuring the exception handler middleware. Either the 'ExceptionHandlingPath' or the 'ExceptionHandler' property must be set in 'UseExceptionHandler()'. Alternatively, set one of the aforementioned properties in 'Startup.ConfigureServices' as follows: 'services. AddExceptionHandler(options => { ... });' or configure to generate a 'ProblemDetails' response in 'service. AddProblemDetails()'."

addnig

services. AddExceptionHandler(options =>
{
options. ExceptionHandlingPath = new PathString(“/Error”);
});

Lets the program run as expected, but the middleware isn’t called any more, as every error ends in a 500

According to Copilot

app. UseExceptionHandler(“/error”);

should do the same as the lines in services, but the error remains poping up

my middleware has a method too catch all unknown errors

catch (Exception ex)
{
context. Response.ContentType = “application/problem+json”;
context. Response.StatusCode = StatusCodes.Status500InternalServerError;

var problemdetail = new ProblemDetails()
{
Status = StatusCodes.Status500InternalServerError,
Detail = ex. Message,
Instance = string. Empty,
Title = “Internal Server Error - something went wrongt”,
Type = string. Empty
};

var problemDetailJson = JsonConvert.SerializeObject(problemdetail);
await context. Response.WriteAsync(problemDetailJson);

}

So the question remains, even with a exception middleware, why do i need a ExceptionHandlingPath at all.

And if i need it, where can i add it without interfering in my middleware.


Original Comments

Feedback Bot on 7/18/2024, 10:02 PM:

(private comment, text removed)


Original Solutions

(no solutions)

daCostaeurogard commented 1 month ago

If someone has further questions, please feel free to post them, with unit testing you need the exact error code to be returned, but as i always get 500, i can not proceed

daCostaeurogard commented 1 month ago

I forgot to mention, when i try the same code in Dot Net 6 everything works just fine. and all test run.

daCostaeurogard commented 4 weeks ago

So after much more digging, i found that Net 8 doesn't support exception middleware anymore. the new way is IExceptionHandler

So you need a Class like


using BookStore.Application.Exceptions;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;

public class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger) : IExceptionHandler
{
    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
    {
        var problemDetails = new ProblemDetails();
        problemDetails.Instance = httpContext.Request.Path;
        if (exception is DuplicateException )
        {
            httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
            problemDetails.Title = "Isbn already Exists";
        }
        else if (exception is AuthorNotFoundException )
        {
            httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
            problemDetails.Title = "Author not Found";
        }
        else if (exception is BookNotFoundException )
        {
            httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
            problemDetails.Title = "Book not Found";
        }
        else if (exception.Message.Contains("Validation failed:"))
        {
            httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
            problemDetails.Detail = JsonConvert.SerializeObject(exception.Message);
            problemDetails.Title = "Validation Error";
        }
        else
        {

            httpContext.Response.StatusCode =  StatusCodes.Status500InternalServerError;
            problemDetails.Title = "Internal Server Error - something went wrongt";
        }
        logger.LogError("{ProblemDetailsTitle}", problemDetails.Title);
        problemDetails.Status = httpContext.Response.StatusCode;
        await httpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken).ConfigureAwait(false);
        return true;
    }

}

And if you use it int the startup.cs

            services.AddExceptionHandler<GlobalExceptionHandler>();
            services.AddProblemDetails();

But they also can be called from then program.cs with builder.services....)