From dc173b4b807304fe17199fff75f713e879e3cd25 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 22 Sep 2021 13:24:13 -0700 Subject: [PATCH 1/4] Improve Results.Problem and Results.ValidationProblem APIs --- .../Http.Results/src/PublicAPI.Unshipped.txt | 8 +-- src/Http/Http.Results/src/Results.cs | 52 +++++++++++++++++-- .../MinimalSample/MinimalSample.csproj | 2 + src/Http/samples/MinimalSample/Program.cs | 27 +++++++--- 4 files changed, 77 insertions(+), 12 deletions(-) diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index 96ce109da59d..a259a49bd409 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -20,7 +20,8 @@ static Microsoft.AspNetCore.Http.Results.LocalRedirect(string! localUrl, bool pe static Microsoft.AspNetCore.Http.Results.NoContent() -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.NotFound(object? value = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Ok(object? value = null) -> Microsoft.AspNetCore.Http.IResult! -static Microsoft.AspNetCore.Http.Results.Problem(string? detail = null, string? instance = null, int? statusCode = null, string? title = null, string? type = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.Problem(string? detail = null, string? instance = null, int? statusCode = null, string? title = null, string? type = null, System.Collections.Generic.IDictionary? extensions = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.Problem(Microsoft.AspNetCore.Mvc.ProblemDetails! problemDetails) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Redirect(string! url, bool permanent = false, bool preserveMethod = false) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.RedirectToRoute(string? routeName = null, object? routeValues = null, bool permanent = false, bool preserveMethod = false, string? fragment = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.SignIn(System.Security.Claims.ClaimsPrincipal! principal, Microsoft.AspNetCore.Authentication.AuthenticationProperties? properties = null, string? authenticationScheme = null) -> Microsoft.AspNetCore.Http.IResult! @@ -30,6 +31,7 @@ static Microsoft.AspNetCore.Http.Results.Stream(System.IO.Stream! stream, string static Microsoft.AspNetCore.Http.Results.Text(string! content, string? contentType = null, System.Text.Encoding? contentEncoding = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Unauthorized() -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.UnprocessableEntity(object? error = null) -> Microsoft.AspNetCore.Http.IResult! -static Microsoft.AspNetCore.Http.Results.ValidationProblem(System.Collections.Generic.IDictionary! errors, string? detail = null, string? instance = null, int? statusCode = null, string? title = null, string? type = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.ValidationProblem(System.Collections.Generic.IDictionary! errors, string? detail = null, string? instance = null, int? statusCode = null, string? title = null, string? type = null, System.Collections.Generic.IDictionary? extensions = null) -> Microsoft.AspNetCore.Http.IResult! +static Microsoft.AspNetCore.Http.Results.ValidationProblem(Microsoft.AspNetCore.Http.HttpValidationProblemDetails! problemDetails) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Extensions.get -> Microsoft.AspNetCore.Http.IResultExtensions! -Microsoft.AspNetCore.Http.IResultExtensions \ No newline at end of file +Microsoft.AspNetCore.Http.IResultExtensions diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index 15d4937e3a24..1d72916c5dd2 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -471,13 +471,15 @@ public static IResult UnprocessableEntity(object? error = null) /// The value for . /// The value for . /// The value for . + /// The value for . /// The created for the response. public static IResult Problem( string? detail = null, string? instance = null, int? statusCode = null, string? title = null, - string? type = null) + string? type = null, + IDictionary? extensions = null) { var problemDetails = new ProblemDetails { @@ -488,6 +490,27 @@ public static IResult Problem( Type = type }; + if (extensions is not null) + { + foreach (var extension in extensions) + { + problemDetails.Extensions.Add(extension); + } + } + + return new ObjectResult(problemDetails) + { + ContentType = "application/problem+json", + }; + } + + /// + /// Produces a response. + /// + /// The object to produce a response from. + /// The created for the response. + public static IResult Problem(ProblemDetails problemDetails) + { return new ObjectResult(problemDetails) { ContentType = "application/problem+json", @@ -504,6 +527,7 @@ public static IResult Problem( /// The status code. /// The value for . /// The value for . + /// The value for . /// The created for the response. public static IResult ValidationProblem( IDictionary errors, @@ -511,7 +535,8 @@ public static IResult ValidationProblem( string? instance = null, int? statusCode = null, string? title = null, - string? type = null) + string? type = null, + IDictionary? extensions = null) { var problemDetails = new HttpValidationProblemDetails(errors) { @@ -519,9 +544,30 @@ public static IResult ValidationProblem( Instance = instance, Title = title, Type = type, - Status = statusCode, + Status = statusCode }; + if (extensions is not null) + { + foreach (var extension in extensions) + { + problemDetails.Extensions.Add(extension); + } + } + + return new ObjectResult(problemDetails) + { + ContentType = "application/problem+json", + }; + } + + /// + /// Produces a response. + /// + /// The object to produce a response from. + /// The created for the response. + public static IResult ValidationProblem(HttpValidationProblemDetails problemDetails) + { return new ObjectResult(problemDetails) { ContentType = "application/problem+json", diff --git a/src/Http/samples/MinimalSample/MinimalSample.csproj b/src/Http/samples/MinimalSample/MinimalSample.csproj index 6b59d1446b9b..eea90b96520b 100644 --- a/src/Http/samples/MinimalSample/MinimalSample.csproj +++ b/src/Http/samples/MinimalSample/MinimalSample.csproj @@ -8,6 +8,8 @@ + + diff --git a/src/Http/samples/MinimalSample/Program.cs b/src/Http/samples/MinimalSample/Program.cs index 5441e671a8f9..c120a0973976 100644 --- a/src/Http/samples/MinimalSample/Program.cs +++ b/src/Http/samples/MinimalSample/Program.cs @@ -1,9 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Hosting; +using Microsoft.AspNetCore.Mvc; var app = WebApplication.Create(args); @@ -13,12 +11,29 @@ } string Plaintext() => "Hello, World!"; -app.MapGet("/plaintext", (Func)Plaintext); +app.MapGet("/plaintext", Plaintext); + object Json() => new { message = "Hello, World!" }; -app.MapGet("/json", (Func)Json); +app.MapGet("/json", Json); string SayHello(string name) => $"Hello, {name}!"; -app.MapGet("/hello/{name}", (Func)SayHello); +app.MapGet("/hello/{name}", SayHello); + +var extensions = new Dictionary() { { "traceId", "traceId123" } }; + +app.MapGet("/problem", () => + Results.Problem(statusCode: 500, extensions: extensions)); + +app.MapGet("/problem-object", () => + Results.Problem(new ProblemDetails() { Status = 500, Extensions = { { "traceId", "traceId123"} } })); + +var errors = new Dictionary(); + +app.MapGet("/validation-problem", () => + Results.ValidationProblem(errors, statusCode: 400, extensions: extensions)); + +app.MapGet("/validation-problem-object", () => + Results.ValidationProblem(new HttpValidationProblemDetails(errors) { Status = 400, Extensions = { { "traceId", "traceId123"}}})); app.Run(); From fa132cf68cd74d46a1f28ee1851d6fba6675815d Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 22 Sep 2021 15:50:16 -0700 Subject: [PATCH 2/4] Address feedback from review --- .../Http.Results/src/PublicAPI.Unshipped.txt | 1 - src/Http/Http.Results/src/Results.cs | 17 ++--------------- src/Http/samples/MinimalSample/Program.cs | 2 +- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt index a259a49bd409..7eff16008f12 100644 --- a/src/Http/Http.Results/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Results/src/PublicAPI.Unshipped.txt @@ -32,6 +32,5 @@ static Microsoft.AspNetCore.Http.Results.Text(string! content, string? contentTy static Microsoft.AspNetCore.Http.Results.Unauthorized() -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.UnprocessableEntity(object? error = null) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.ValidationProblem(System.Collections.Generic.IDictionary! errors, string? detail = null, string? instance = null, int? statusCode = null, string? title = null, string? type = null, System.Collections.Generic.IDictionary? extensions = null) -> Microsoft.AspNetCore.Http.IResult! -static Microsoft.AspNetCore.Http.Results.ValidationProblem(Microsoft.AspNetCore.Http.HttpValidationProblemDetails! problemDetails) -> Microsoft.AspNetCore.Http.IResult! static Microsoft.AspNetCore.Http.Results.Extensions.get -> Microsoft.AspNetCore.Http.IResultExtensions! Microsoft.AspNetCore.Http.IResultExtensions diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index 1d72916c5dd2..6d945879e777 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -487,7 +487,7 @@ public static IResult Problem( Instance = instance, Status = statusCode, Title = title, - Type = type + Type = type, }; if (extensions is not null) @@ -544,7 +544,7 @@ public static IResult ValidationProblem( Instance = instance, Title = title, Type = type, - Status = statusCode + Status = statusCode, }; if (extensions is not null) @@ -561,19 +561,6 @@ public static IResult ValidationProblem( }; } - /// - /// Produces a response. - /// - /// The object to produce a response from. - /// The created for the response. - public static IResult ValidationProblem(HttpValidationProblemDetails problemDetails) - { - return new ObjectResult(problemDetails) - { - ContentType = "application/problem+json", - }; - } - /// /// Produces a response. /// diff --git a/src/Http/samples/MinimalSample/Program.cs b/src/Http/samples/MinimalSample/Program.cs index c120a0973976..12b78a782467 100644 --- a/src/Http/samples/MinimalSample/Program.cs +++ b/src/Http/samples/MinimalSample/Program.cs @@ -34,6 +34,6 @@ Results.ValidationProblem(errors, statusCode: 400, extensions: extensions)); app.MapGet("/validation-problem-object", () => - Results.ValidationProblem(new HttpValidationProblemDetails(errors) { Status = 400, Extensions = { { "traceId", "traceId123"}}})); + Results.Problem(new HttpValidationProblemDetails(errors) { Status = 400, Extensions = { { "traceId", "traceId123"}}})); app.Run(); From f8f45c7ff6fef830de4ecf9ab0b8c39b48fd797d Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 22 Sep 2021 17:18:05 -0700 Subject: [PATCH 3/4] Set default title for ValidationProblem --- src/Http/Http.Results/src/Results.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index 6d945879e777..4ca258d474b4 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -525,7 +525,7 @@ public static IResult Problem(ProblemDetails problemDetails) /// The value for . /// The value for . /// The status code. - /// The value for . + /// The value for . Defaults to "One or more validation errors occurred." /// The value for . /// The value for . /// The created for the response. @@ -542,7 +542,7 @@ public static IResult ValidationProblem( { Detail = detail, Instance = instance, - Title = title, + Title = title ?? "One or more validation errors occurred.", Type = type, Status = statusCode, }; From cdc024bfdc1b30e21be209145b1e33d99f297b03 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 22 Sep 2021 17:44:53 -0700 Subject: [PATCH 4/4] Update src/Http/Http.Results/src/Results.cs Co-authored-by: Stephen Halter --- src/Http/Http.Results/src/Results.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Http/Http.Results/src/Results.cs b/src/Http/Http.Results/src/Results.cs index 4ca258d474b4..aa69b20ea4d9 100644 --- a/src/Http/Http.Results/src/Results.cs +++ b/src/Http/Http.Results/src/Results.cs @@ -542,10 +542,11 @@ public static IResult ValidationProblem( { Detail = detail, Instance = instance, - Title = title ?? "One or more validation errors occurred.", Type = type, Status = statusCode, }; + + problemDetails.Title = title ?? problemDetails.Title; if (extensions is not null) {