Closed
Description
Background and Motivation
If we want to create an api with multiple result types with using TypedResults, .net core 7 only supports ValidationProblem
for auto generating swagger metadata and ProblemHttpResult
type doesn't generate any swagger metadata (because it doesn't implement IEndpointMetadataProvider
). Maybe we need to declare dedicated problem types like ValidationProblem
for others error status codes, like InternalProbelm
, UnAuthorizedProblem
, NotFoundProblem
...
There is an issue #47623 already for this proposal.
Proposed API
This is an example for UnAuthorized
problem, but it is the same for other error status codes:
namespace Microsoft.AspNetCore.Http.HttpResults;
+ public sealed class UnAuthorizedHttpProblemResult
+ : IResult,
+ IEndpointMetadataProvider,
+ IStatusCodeHttpResult,
+ IContentTypeHttpResult,
+ IValueHttpResult
+{
+ internal UnAuthorizedHttpProblemResult(ProblemDetails problemDetails)
+ {
+ ArgumentNullException.ThrowIfNull(problemDetails);
+ if (problemDetails is { Status: not null and not StatusCodes.Status401Unauthorized })
+ {
+ throw new ArgumentException(
+ $"{nameof(UnAuthorizedHttpProblemResult)} only supports a 401 UnAuthorize response status code.",
+ nameof(problemDetails)
+ );
+ }
+
+ ProblemDetails = problemDetails;
+ ProblemDetailsDefaults.Apply(ProblemDetails, statusCode: StatusCodes.Status401Unauthorized);
+ }
+
+ public ProblemDetails ProblemDetails { get; }
+
+ object? IValueHttpResult.Value => ProblemDetails;
+
+ public string ContentType => "application/problem+json";
+
+ public int StatusCode => StatusCodes.Status401Unauthorized;
+
+ int? IStatusCodeHttpResult.StatusCode => StatusCode;
+
+
+ public Task ExecuteAsync(HttpContext httpContext)
+ {
+ ArgumentNullException.ThrowIfNull(httpContext);
+
+ var loggerFactory = httpContext.RequestServices.GetRequiredService<ILoggerFactory>();
+ var logger = loggerFactory.CreateLogger(typeof(UnAuthorizedHttpProblemResult));
+
+ HttpResultsHelper.Log.WritingResultAsStatusCode(logger, StatusCode);
+ httpContext.Response.StatusCode = StatusCode;
+
+ return HttpResultsHelper.WriteResultAsJsonAsync(httpContext, logger, value: ProblemDetails, ContentType);
+ }
+
+ static void IEndpointMetadataProvider.PopulateMetadata(MethodInfo method, EndpointBuilder builder)
+ {
+ ArgumentNullException.ThrowIfNull(method);
+ ArgumentNullException.ThrowIfNull(builder);
+
+ builder.Metadata.Add(
+ new ProducesResponseTypeMetadata(
+ typeof(ProblemDetails),
+ StatusCodes.Status401Unauthorized,
+ "application/problem+json"
+ )
+ );
+ }
+}
namespace Microsoft.AspNetCore.Http;
/// <summary>
/// A typed factory for <see cref="IResult"/> types in <see cref="Microsoft.AspNetCore.Http.HttpResults"/>.
/// </summary>
public static class TypedResults
{
+ public static UnAuthorizedHttpProblemResult UnAuthorizedProblem(
+ string? title = null,
+ string? detail = null,
+ string? instance = null,
+ string? type = null,
+ IDictionary<string, object?>? extensions = null
+ )
+ {
+ var problemDetails = CreateProblem(title, detail, instance, type, extensions);
+
+ return new UnAuthorizedHttpProblemResult(problemDetails);
+ }
+ private static ProblemDetails CreateProblem(
+ string? title,
+ string? detail,
+ string? instance,
+ string? type,
+ IDictionary<string, object?>? extensions
+ )
+ {
+ var problemDetails = new ProblemDetails
+ {
+ Detail = detail,
+ Instance = instance,
+ Type = type
+ };
+
+ problemDetails.Title = title ?? problemDetails.Title;
+
+ if (extensions is not null)
+ {
+ foreach (var extension in extensions)
+ {
+ problemDetails.Extensions.Add(extension);
+ }
+ }
+
+ return problemDetails;
+ }
}
Usage Examples
endpoints.MapPost("/products",
Results<UnAuthorizedHttpProblemResult, Ok<ProductDto>>(ProductDto product) =>
{
if ( some condition )
return TypedResults.UnauthorizedProblem();
return TypedResults.Ok(product);
});
Alternative Designs
N/A
Risks
None that I am aware of.