| File: Infrastructure\DefaultApiProblemDetailsWriter.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Mvc.Infrastructure; internal sealed class DefaultApiProblemDetailsWriter : IProblemDetailsWriter { private readonly OutputFormatterSelector _formatterSelector; private readonly IHttpResponseStreamWriterFactory _writerFactory; private readonly ProblemDetailsFactory _problemDetailsFactory; private readonly ApiBehaviorOptions _apiBehaviorOptions; private static readonly MediaTypeCollection _problemContentTypes = new() { "application/problem+json", "application/problem+xml" }; public DefaultApiProblemDetailsWriter( OutputFormatterSelector formatterSelector, IHttpResponseStreamWriterFactory writerFactory, ProblemDetailsFactory problemDetailsFactory, IOptions<ApiBehaviorOptions> apiBehaviorOptions) { _formatterSelector = formatterSelector; _writerFactory = writerFactory; _problemDetailsFactory = problemDetailsFactory; _apiBehaviorOptions = apiBehaviorOptions.Value; } public bool CanWrite(ProblemDetailsContext context) { var controllerAttribute = context.AdditionalMetadata?.GetMetadata<ControllerAttribute>() ?? context.HttpContext.GetEndpoint()?.Metadata.GetMetadata<ControllerAttribute>(); return controllerAttribute != null; } public ValueTask WriteAsync(ProblemDetailsContext context) { var apiControllerAttribute = context.AdditionalMetadata?.GetMetadata<IApiBehaviorMetadata>() ?? context.HttpContext.GetEndpoint()?.Metadata.GetMetadata<IApiBehaviorMetadata>(); if (apiControllerAttribute is null || _apiBehaviorOptions.SuppressMapClientErrors) { // In this case we don't want to write return ValueTask.CompletedTask; } // Recreating the problem details to get all customizations // from the factory var problemDetails = _problemDetailsFactory.CreateProblemDetails( context.HttpContext, context.ProblemDetails.Status ?? context.HttpContext.Response.StatusCode, context.ProblemDetails.Title, context.ProblemDetails.Type, context.ProblemDetails.Detail, context.ProblemDetails.Instance); if (context.ProblemDetails?.Extensions is not null) { foreach (var extension in context.ProblemDetails.Extensions) { problemDetails.Extensions[extension.Key] = extension.Value; } } var formatterContext = new OutputFormatterWriteContext( context.HttpContext, _writerFactory.CreateWriter, typeof(ProblemDetails), problemDetails); var selectedFormatter = _formatterSelector.SelectFormatter( formatterContext, Array.Empty<IOutputFormatter>(), _problemContentTypes); if (selectedFormatter == null) { return ValueTask.CompletedTask; } return new ValueTask(selectedFormatter.WriteAsync(formatterContext)); } }