Skip to content

Instantly share code, notes, and snippets.

@Bardin08
Created May 21, 2024 14:52
Show Gist options
  • Save Bardin08/14b742c85725a9eb556b2308306dce65 to your computer and use it in GitHub Desktop.
Save Bardin08/14b742c85725a9eb556b2308306dce65 to your computer and use it in GitHub Desktop.
using ErrorOr;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Options;
using System.Diagnostics;
using Trumpet.Api.Common.Http;
namespace Trumpet.Api.Common.Errors;
public sealed class TrumpetProblemDetailsFactory : ProblemDetailsFactory
{
private readonly Action<ProblemDetailsContext>? _configure;
private readonly ApiBehaviorOptions _options;
public TrumpetProblemDetailsFactory(
IOptions<ApiBehaviorOptions> options,
IOptions<ProblemDetailsOptions>? problemDetailsOptions = null)
{
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
_configure = problemDetailsOptions?.Value?.CustomizeProblemDetails;
}
public override ProblemDetails CreateProblemDetails(
HttpContext httpContext,
int? statusCode = null,
string? title = null,
string? type = null,
string? detail = null,
string? instance = null)
{
statusCode ??= 500;
var problemDetails = new ProblemDetails
{
Status = statusCode,
Title = title,
Type = type,
Detail = detail,
Instance = instance
};
ApplyProblemDetailsDefaults(httpContext, problemDetails, statusCode.Value);
return problemDetails;
}
public override ValidationProblemDetails CreateValidationProblemDetails(
HttpContext httpContext,
ModelStateDictionary modelStateDictionary,
int? statusCode = null,
string? title = null,
string? type = null,
string? detail = null,
string? instance = null)
{
ArgumentNullException.ThrowIfNull(modelStateDictionary);
statusCode ??= 400;
var problemDetails = new ValidationProblemDetails(modelStateDictionary)
{
Status = statusCode,
Type = type,
Detail = detail,
Instance = instance
};
if (title != null)
problemDetails.Title = title;
ApplyProblemDetailsDefaults(httpContext, problemDetails, statusCode.Value);
return problemDetails;
}
private void ApplyProblemDetailsDefaults(HttpContext httpContext, ProblemDetails problemDetails, int statusCode)
{
problemDetails.Status ??= statusCode;
if (_options.ClientErrorMapping.TryGetValue(statusCode, out var clientErrorData))
{
problemDetails.Title ??= clientErrorData.Title;
problemDetails.Type ??= clientErrorData.Link;
}
var traceId = Activity.Current?.Id ?? httpContext?.TraceIdentifier;
if (traceId != null) problemDetails.Extensions["traceId"] = traceId;
if (httpContext?.Items[HttpContextItemKeys.Errors] is List<Error> errors)
problemDetails.Extensions.Add("errorCodes", errors.Select(e => e.Code));
_configure?.Invoke(new ProblemDetailsContext { HttpContext = httpContext!, ProblemDetails = problemDetails });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment