Skip to content

Commit fefe276

Browse files
authored
Improve Results.Problem and Results.ValidationProblem APIs (#36856) (#36930)
1 parent 3589956 commit fefe276

File tree

4 files changed

+66
-14
lines changed

4 files changed

+66
-14
lines changed

src/Http/Http.Results/src/PublicAPI.Unshipped.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ static Microsoft.AspNetCore.Http.Results.LocalRedirect(string! localUrl, bool pe
2020
static Microsoft.AspNetCore.Http.Results.NoContent() -> Microsoft.AspNetCore.Http.IResult!
2121
static Microsoft.AspNetCore.Http.Results.NotFound(object? value = null) -> Microsoft.AspNetCore.Http.IResult!
2222
static Microsoft.AspNetCore.Http.Results.Ok(object? value = null) -> Microsoft.AspNetCore.Http.IResult!
23-
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!
23+
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<string!, object?>? extensions = null) -> Microsoft.AspNetCore.Http.IResult!
24+
static Microsoft.AspNetCore.Http.Results.Problem(Microsoft.AspNetCore.Mvc.ProblemDetails! problemDetails) -> Microsoft.AspNetCore.Http.IResult!
2425
static Microsoft.AspNetCore.Http.Results.Redirect(string! url, bool permanent = false, bool preserveMethod = false) -> Microsoft.AspNetCore.Http.IResult!
2526
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!
2627
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,6 @@ static Microsoft.AspNetCore.Http.Results.Stream(System.IO.Stream! stream, string
3031
static Microsoft.AspNetCore.Http.Results.Text(string! content, string? contentType = null, System.Text.Encoding? contentEncoding = null) -> Microsoft.AspNetCore.Http.IResult!
3132
static Microsoft.AspNetCore.Http.Results.Unauthorized() -> Microsoft.AspNetCore.Http.IResult!
3233
static Microsoft.AspNetCore.Http.Results.UnprocessableEntity(object? error = null) -> Microsoft.AspNetCore.Http.IResult!
33-
static Microsoft.AspNetCore.Http.Results.ValidationProblem(System.Collections.Generic.IDictionary<string!, string![]!>! errors, string? detail = null, string? instance = null, int? statusCode = null, string? title = null, string? type = null) -> Microsoft.AspNetCore.Http.IResult!
34+
static Microsoft.AspNetCore.Http.Results.ValidationProblem(System.Collections.Generic.IDictionary<string!, string![]!>! errors, string? detail = null, string? instance = null, int? statusCode = null, string? title = null, string? type = null, System.Collections.Generic.IDictionary<string!, object?>? extensions = null) -> Microsoft.AspNetCore.Http.IResult!
3435
static Microsoft.AspNetCore.Http.Results.Extensions.get -> Microsoft.AspNetCore.Http.IResultExtensions!
35-
Microsoft.AspNetCore.Http.IResultExtensions
36+
Microsoft.AspNetCore.Http.IResultExtensions

src/Http/Http.Results/src/Results.cs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -471,23 +471,46 @@ public static IResult UnprocessableEntity(object? error = null)
471471
/// <param name="instance">The value for <see cref="ProblemDetails.Instance" />.</param>
472472
/// <param name="title">The value for <see cref="ProblemDetails.Title" />.</param>
473473
/// <param name="type">The value for <see cref="ProblemDetails.Type" />.</param>
474+
/// <param name="extensions">The value for <see cref="ProblemDetails.Extensions" />.</param>
474475
/// <returns>The created <see cref="IResult"/> for the response.</returns>
475476
public static IResult Problem(
476477
string? detail = null,
477478
string? instance = null,
478479
int? statusCode = null,
479480
string? title = null,
480-
string? type = null)
481+
string? type = null,
482+
IDictionary<string, object?>? extensions = null)
481483
{
482484
var problemDetails = new ProblemDetails
483485
{
484486
Detail = detail,
485487
Instance = instance,
486488
Status = statusCode,
487489
Title = title,
488-
Type = type
490+
Type = type,
489491
};
490492

493+
if (extensions is not null)
494+
{
495+
foreach (var extension in extensions)
496+
{
497+
problemDetails.Extensions.Add(extension);
498+
}
499+
}
500+
501+
return new ObjectResult(problemDetails)
502+
{
503+
ContentType = "application/problem+json",
504+
};
505+
}
506+
507+
/// <summary>
508+
/// Produces a <see cref="ProblemDetails"/> response.
509+
/// </summary>
510+
/// <param name="problemDetails">The <see cref="ProblemDetails"/> object to produce a response from.</param>
511+
/// <returns>The created <see cref="IResult"/> for the response.</returns>
512+
public static IResult Problem(ProblemDetails problemDetails)
513+
{
491514
return new ObjectResult(problemDetails)
492515
{
493516
ContentType = "application/problem+json",
@@ -502,25 +525,36 @@ public static IResult Problem(
502525
/// <param name="detail">The value for <see cref="ProblemDetails.Detail" />.</param>
503526
/// <param name="instance">The value for <see cref="ProblemDetails.Instance" />.</param>
504527
/// <param name="statusCode">The status code.</param>
505-
/// <param name="title">The value for <see cref="ProblemDetails.Title" />.</param>
528+
/// <param name="title">The value for <see cref="ProblemDetails.Title" />. Defaults to "One or more validation errors occurred."</param>
506529
/// <param name="type">The value for <see cref="ProblemDetails.Type" />.</param>
530+
/// <param name="extensions">The value for <see cref="ProblemDetails.Extensions" />.</param>
507531
/// <returns>The created <see cref="IResult"/> for the response.</returns>
508532
public static IResult ValidationProblem(
509533
IDictionary<string, string[]> errors,
510534
string? detail = null,
511535
string? instance = null,
512536
int? statusCode = null,
513537
string? title = null,
514-
string? type = null)
538+
string? type = null,
539+
IDictionary<string, object?>? extensions = null)
515540
{
516541
var problemDetails = new HttpValidationProblemDetails(errors)
517542
{
518543
Detail = detail,
519544
Instance = instance,
520-
Title = title,
521545
Type = type,
522546
Status = statusCode,
523547
};
548+
549+
problemDetails.Title = title ?? problemDetails.Title;
550+
551+
if (extensions is not null)
552+
{
553+
foreach (var extension in extensions)
554+
{
555+
problemDetails.Extensions.Add(extension);
556+
}
557+
}
524558

525559
return new ObjectResult(problemDetails)
526560
{

src/Http/samples/MinimalSample/MinimalSample.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<Reference Include="Microsoft.AspNetCore" />
99
<Reference Include="Microsoft.AspNetCore.Diagnostics" />
1010
<Reference Include="Microsoft.AspNetCore.Hosting" />
11+
<Reference Include="Microsoft.AspNetCore.Http" />
12+
<Reference Include="Microsoft.AspNetCore.Http.Results" />
1113
<!-- Mvc.Core is referenced only for its attributes -->
1214
<Reference Include="Microsoft.AspNetCore.Mvc.Core" />
1315
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using Microsoft.AspNetCore.Builder;
6-
using Microsoft.Extensions.Hosting;
4+
using Microsoft.AspNetCore.Mvc;
75

86
var app = WebApplication.Create(args);
97

@@ -13,12 +11,29 @@
1311
}
1412

1513
string Plaintext() => "Hello, World!";
16-
app.MapGet("/plaintext", (Func<string>)Plaintext);
14+
app.MapGet("/plaintext", Plaintext);
15+
1716

1817
object Json() => new { message = "Hello, World!" };
19-
app.MapGet("/json", (Func<object>)Json);
18+
app.MapGet("/json", Json);
2019

2120
string SayHello(string name) => $"Hello, {name}!";
22-
app.MapGet("/hello/{name}", (Func<string, string>)SayHello);
21+
app.MapGet("/hello/{name}", SayHello);
22+
23+
var extensions = new Dictionary<string, object>() { { "traceId", "traceId123" } };
24+
25+
app.MapGet("/problem", () =>
26+
Results.Problem(statusCode: 500, extensions: extensions));
27+
28+
app.MapGet("/problem-object", () =>
29+
Results.Problem(new ProblemDetails() { Status = 500, Extensions = { { "traceId", "traceId123"} } }));
30+
31+
var errors = new Dictionary<string, string[]>();
32+
33+
app.MapGet("/validation-problem", () =>
34+
Results.ValidationProblem(errors, statusCode: 400, extensions: extensions));
35+
36+
app.MapGet("/validation-problem-object", () =>
37+
Results.Problem(new HttpValidationProblemDetails(errors) { Status = 400, Extensions = { { "traceId", "traceId123"}}}));
2338

2439
app.Run();

0 commit comments

Comments
 (0)