admin管理员组

文章数量:1400375

I have my ASP.NET Core 9 Web API app using custom ExceptionHandlerMiddleware Middleware to handle all exceptions as shown below:

public sealed class ExceptionHandlerMiddleware(IWebHostEnvironment env, ILogger<ExceptionHandlerMiddleware> logger) : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (DomainException ex)
        {
            await HandleDomainExceptionAsync(context, ex);
        }
        catch (DbUpdateException ex)
        {
            await HandleDbUpdateExceptionAsync(context, ex);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleDomainExceptionAsync(HttpContext context, DomainException exception)
    {
        logger.LogCritical(exception, exception.Message);
        var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        string result = JsonSerializer.Serialize(Envelope.Error(exception.Message), options);
        context.Response.ContentType = MediaTypeNames.Application.Json;
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return context.Response.WriteAsync(result);
    }

    private Task HandleDbUpdateExceptionAsync(HttpContext context, Exception exception)
    {
        logger.LogCritical(exception, exception.Message);
        var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        string result = JsonSerializer.Serialize(Envelope.Error("Error occurred while saving data. Please try again later."), options);
        context.Response.ContentType = MediaTypeNames.Application.Json;
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return context.Response.WriteAsync(result);
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        logger.LogCritical(exception, exception.Message);
        var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        string result = env.IsLocal() ? JsonSerializer.Serialize(Envelope.Error(exception.Message), options) : JsonSerializer.Serialize(Envelope.Error("An unexpected fault happened. Try again later."), options);
        context.Response.ContentType = MediaTypeNames.Application.Json;
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return context.Response.WriteAsync(result);
    }
}

Now, I came across ProblemDetails and looks like ASP.NET Core 9 has support for it.

All I need to do is to,

Program.cs:

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();

app.Run();

The setup is easy and straight forward. But the problem is whenever I test with exception I get the below response.

{
  "type": ".6.1",
  "title": "An error occurred while processing your request.",
  "status": 500,
  "traceId": "00-a2cfefb5c6ca44a142d9810fef0e5f7d-d7103986bf2c06a4-00"
}

Now I need help in understanding how to show StackTrace in lower environments like Local, Offline, Development. I'm not able to find an option to configure StackTrace based on environment.

I checked the problem details docs and not able to find any information.

I have my ASP.NET Core 9 Web API app using custom ExceptionHandlerMiddleware Middleware to handle all exceptions as shown below:

public sealed class ExceptionHandlerMiddleware(IWebHostEnvironment env, ILogger<ExceptionHandlerMiddleware> logger) : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (DomainException ex)
        {
            await HandleDomainExceptionAsync(context, ex);
        }
        catch (DbUpdateException ex)
        {
            await HandleDbUpdateExceptionAsync(context, ex);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleDomainExceptionAsync(HttpContext context, DomainException exception)
    {
        logger.LogCritical(exception, exception.Message);
        var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        string result = JsonSerializer.Serialize(Envelope.Error(exception.Message), options);
        context.Response.ContentType = MediaTypeNames.Application.Json;
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return context.Response.WriteAsync(result);
    }

    private Task HandleDbUpdateExceptionAsync(HttpContext context, Exception exception)
    {
        logger.LogCritical(exception, exception.Message);
        var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        string result = JsonSerializer.Serialize(Envelope.Error("Error occurred while saving data. Please try again later."), options);
        context.Response.ContentType = MediaTypeNames.Application.Json;
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return context.Response.WriteAsync(result);
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        logger.LogCritical(exception, exception.Message);
        var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
        string result = env.IsLocal() ? JsonSerializer.Serialize(Envelope.Error(exception.Message), options) : JsonSerializer.Serialize(Envelope.Error("An unexpected fault happened. Try again later."), options);
        context.Response.ContentType = MediaTypeNames.Application.Json;
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        return context.Response.WriteAsync(result);
    }
}

Now, I came across ProblemDetails and looks like ASP.NET Core 9 has support for it.

All I need to do is to,

Program.cs:

builder.Services.AddProblemDetails();

var app = builder.Build();

app.UseExceptionHandler();

app.Run();

The setup is easy and straight forward. But the problem is whenever I test with exception I get the below response.

{
  "type": "https://tools.ietf./html/rfc9110#section-15.6.1",
  "title": "An error occurred while processing your request.",
  "status": 500,
  "traceId": "00-a2cfefb5c6ca44a142d9810fef0e5f7d-d7103986bf2c06a4-00"
}

Now I need help in understanding how to show StackTrace in lower environments like Local, Offline, Development. I'm not able to find an option to configure StackTrace based on environment.

I checked the problem details docs and not able to find any information.

Share Improve this question asked Mar 24 at 7:04 fingers10fingers10 8,06912 gold badges62 silver badges108 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 3

First of all, default template for ASP.NET Core project contains such lines:

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
    app.UseDeveloperExceptionPage();
}

which enables developer exception page in development mode. This page contains stack trace for example:

If I would comment/remove app.UseDeveloperExceptionPage(), then i would get this in browser (this is just response body):

So, this is one way to get more details about exceptions.

How to inlucde stack trace directly in problem details, without developer's exception page

You can adjust behavior of problem details with such overload of AddProblemDetails:

builder.Services.AddProblemDetails(options =>
{
    options.CustomizeProblemDetails = context =>
    {
        var hostEnv = context.HttpContext.RequestServices.GetRequiredService<IHostEnvironment>();

        var shouldIncludeStackTrace = hostEnv.IsDevelopment();

        if (context.Exception is null || !shouldIncludeStackTrace)
        {
            return;
        }

        context.ProblemDetails.Detail = context.Exception.StackTrace;
    };
});

With this change, you will get additional stack trace in details property in response, only in development environment:

Last, but not least, you need to adjust condition:

var shouldIncludeStackTrace = hostEnv.IsDevelopment();

in order to inlcude stack trace only in environments, that need it.

References:

  • Customize problem details with CustomizeProblemDetails

本文标签: