< Summary

Information
Class: ProjectTemplate.Web.ErrorHandling.ProblemDetailsExtensions
Assembly: ProjectTemplate.Web
File(s): /home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/ErrorHandling/ProblemDetailsExtensions.cs
Line coverage
98%
Covered lines: 69
Uncovered lines: 1
Coverable lines: 70
Total lines: 142
Line coverage: 98.5%
Branch coverage
83%
Covered branches: 30
Total branches: 36
Branch coverage: 83.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
AddApplicationProblemDetails(...)87.5%2424100%
UseProblemDetails(...)50%2291.66%
GetCorrelationId(...)80%1010100%

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Web/ErrorHandling/ProblemDetailsExtensions.cs

#LineLine coverage
 1using System.Diagnostics;
 2using Microsoft.AspNetCore.Diagnostics;
 3using Microsoft.Extensions.Options;
 4using Microsoft.Extensions.Primitives;
 5using ProjectTemplate.Web.Options;
 6
 7namespace ProjectTemplate.Web.ErrorHandling;
 8
 9/// <summary>
 10/// Provides extension methods for configuring standardized problem details and exception handling in ASP.NET Core
 11/// applications.
 12/// </summary>
 13/// <remarks>These extensions integrate custom problem details formatting and exception handling into the
 14/// application's dependency injection and middleware pipelines. They help ensure consistent error responses and
 15/// traceability across environments.</remarks>
 16internal static class ProblemDetailsExtensions
 17{
 18    /// <summary>
 19    /// Adds standardized Problem Details error responses and a custom exception handler to the application's service
 20    /// collection.
 21    /// </summary>
 22    /// <remarks>This method configures Problem Details middleware to include trace and request identifiers in
 23    /// error responses and customizes error details for non-development environments. It also registers a custom
 24    /// exception handler for consistent error formatting.</remarks>
 25    /// <param name="services">The service collection to which Problem Details and the exception handler are added. Cann
 26    /// <param name="webHostEnvironment">The current web hosting environment. Used to determine error detail behavior ba
 27    /// null.</param>
 28    /// <returns>The same service collection instance, enabling method chaining.</returns>
 29    public static IServiceCollection AddApplicationProblemDetails(
 30        this IServiceCollection services,
 31        IWebHostEnvironment webHostEnvironment)
 32    {
 15633        ArgumentNullException.ThrowIfNull(services);
 15634        ArgumentNullException.ThrowIfNull(webHostEnvironment);
 35
 15636        services.AddExceptionHandler<ProblemDetailsExceptionHandler>();
 37
 30638        services.AddProblemDetails(options => options.CustomizeProblemDetails = context =>
 30639            {
 1240                string? originalExceptionPath = context.HttpContext.Features
 1241                    .Get<IExceptionHandlerPathFeature>()?
 1242                    .Path;
 30643
 1244                string? originalStatusCodePath = context.HttpContext.Features
 1245                    .Get<IStatusCodeReExecuteFeature>()?
 1246                    .OriginalPath;
 30647
 1248                context.ProblemDetails.Instance ??= !string.IsNullOrWhiteSpace(originalExceptionPath)
 1249                    ? originalExceptionPath
 1250                    : !string.IsNullOrWhiteSpace(originalStatusCodePath)
 1251                        ? originalStatusCodePath
 1252                        : context.HttpContext.Request.Path;
 30653
 1254                string correlationId = GetCorrelationId(context.HttpContext);
 30655
 1256                Activity? activity = Activity.Current;
 30657
 1258                context.ProblemDetails.Extensions["traceId"] =
 1259                    activity?.TraceId.ToString() ?? context.HttpContext.TraceIdentifier;
 30660
 1261                if (activity is not null)
 30662                {
 663                    context.ProblemDetails.Extensions["spanId"] = activity.SpanId.ToString();
 30664                }
 30665
 1266                context.ProblemDetails.Extensions["requestId"] =
 1267                    context.HttpContext.TraceIdentifier;
 30668
 1269                context.ProblemDetails.Extensions["correlationId"] =
 1270                    correlationId;
 30671
 1272                if (!webHostEnvironment.IsDevelopment() &&
 1273                    context.ProblemDetails.Status >= StatusCodes.Status500InternalServerError)
 30674                {
 275                    context.ProblemDetails.Detail ??=
 276                        "An unexpected error occurred. Contact support with the request ID.";
 30677                }
 31878            });
 79
 15680        return services;
 81    }
 82
 83    /// <summary>
 84    /// Configures standardized error handling and problem details middleware for the application based on the current
 85    /// environment.
 86    /// </summary>
 87    /// <remarks>In the development environment, this method enables the developer exception page. In other
 88    /// environments, it configures a generic exception handler and enforces HTTP Strict Transport Security (HSTS). It
 89    /// also sets up status code pages to return problem details responses when appropriate, or redirects to a custom
 90    /// error page otherwise.</remarks>
 91    /// <param name="app">The <see cref="WebApplication"/> instance to configure. Cannot be null.</param>
 92    /// <returns>The configured <see cref="WebApplication"/> instance.</returns>
 93    public static WebApplication UseProblemDetails(this WebApplication app)
 94    {
 14295        ArgumentNullException.ThrowIfNull(app);
 96
 14297        if (app.Environment.IsDevelopment())
 98        {
 099            app.UseDeveloperExceptionPage();
 100        }
 101        else
 102        {
 142103            app.UseExceptionHandler("/Home/Error/500");
 142104            app.UseHsts();
 105        }
 106
 142107        app.UseWhen(
 142108            ProblemDetailsRequestClassifier.ShouldWriteProblemDetails,
 284109            branch => branch.UseStatusCodePages());
 110
 142111        app.UseWhen(
 102112            context => !ProblemDetailsRequestClassifier.ShouldWriteProblemDetails(context),
 284113            branch => branch.UseStatusCodePagesWithReExecute("/Home/Error/{0}"));
 114
 142115        return app;
 116    }
 117
 118    private static string GetCorrelationId(HttpContext httpContext)
 119    {
 12120        ApplicationRequestLoggingOptions requestLoggingOptions = httpContext.RequestServices
 12121            .GetService<IOptions<ApplicationRequestLoggingOptions>>()?.Value
 12122            ?? new ApplicationRequestLoggingOptions();
 123
 12124        if (httpContext.Request.Headers.TryGetValue(
 12125            requestLoggingOptions.CorrelationHeaderName,
 12126            out StringValues correlationHeaderValues))
 127        {
 4128            string? headerValue = correlationHeaderValues.FirstOrDefault();
 129
 4130            if (!string.IsNullOrWhiteSpace(headerValue))
 131            {
 4132                string cleanValue = headerValue.Trim();
 133
 4134                return cleanValue.Length <= 128
 4135                    ? cleanValue
 4136                    : cleanValue[..128];
 137            }
 138        }
 139
 8140        return httpContext.TraceIdentifier;
 141    }
 142}