-
Notifications
You must be signed in to change notification settings - Fork 703
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Back port error object support. Related to #1019
- Loading branch information
1 parent
c462195
commit e5a84b0
Showing
10 changed files
with
277 additions
and
9 deletions.
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/InteropFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
|
||
// Ignore Spelling: Interop | ||
namespace Asp.Versioning.Http; | ||
|
||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
public class InteropFixture : MinimalApiFixture | ||
{ | ||
protected override void OnConfigureServices( IServiceCollection services ) | ||
{ | ||
services.AddSingleton<IProblemDetailsFactory, ErrorObjectFactory>(); | ||
base.OnConfigureServices( services ); | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
src/AspNetCore/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Http/MinimalApiFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
...Mvc.Acceptance.Tests/Http/given a versioned minimal API/when error objects are enabled.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
|
||
namespace given_a_versioned_minimal_API; | ||
|
||
using Asp.Versioning; | ||
using Asp.Versioning.Http; | ||
|
||
public class when_error_objects_are_enabled : AcceptanceTest, IClassFixture<InteropFixture> | ||
{ | ||
[Fact] | ||
public async Task then_the_response_should_not_be_problem_details() | ||
{ | ||
// arrange | ||
var example = new | ||
{ | ||
error = new | ||
{ | ||
code = default( string ), | ||
message = default( string ), | ||
target = default( string ), | ||
innerError = new | ||
{ | ||
message = default( string ), | ||
}, | ||
}, | ||
}; | ||
|
||
// act | ||
var response = await GetAsync( "api/values?api-version=3.0" ); | ||
var error = await response.Content.ReadAsExampleAsync( example ); | ||
|
||
// assert | ||
response.Content.Headers.ContentType.MediaType.Should().Be( "application/json" ); | ||
error.Should().BeEquivalentTo( | ||
new | ||
{ | ||
error = new | ||
{ | ||
code = "UnsupportedApiVersion", | ||
message = "Unsupported API version", | ||
innerError = new | ||
{ | ||
message = "The HTTP resource that matches the request URI " + | ||
"'http://localhost/api/values' does not support " + | ||
"the API version '3.0'.", | ||
}, | ||
}, | ||
} ); | ||
} | ||
|
||
public when_error_objects_are_enabled( InteropFixture fixture ) : base( fixture ) { } | ||
} |
15 changes: 15 additions & 0 deletions
15
...Core/Acceptance/Asp.Versioning.Mvc.Acceptance.Tests/Mvc/UsingAttributes/InteropFixture.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
|
||
// Ignore Spelling: Interop | ||
namespace Asp.Versioning.Mvc.UsingAttributes; | ||
|
||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
public class InteropFixture : BasicFixture | ||
{ | ||
protected override void OnConfigureServices( IServiceCollection services ) | ||
{ | ||
services.AddSingleton<IProblemDetailsFactory, ErrorObjectFactory>(); | ||
base.OnConfigureServices( services ); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
....Tests/Mvc/UsingAttributes/given a versioned Controller/when error objects are enabled.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
|
||
namespace given_a_versioned_Controller; | ||
|
||
using Asp.Versioning; | ||
using Asp.Versioning.Mvc.UsingAttributes; | ||
|
||
public class when_error_objects_are_enabled : AcceptanceTest, IClassFixture<InteropFixture> | ||
{ | ||
[Fact] | ||
public async Task then_the_response_should_not_be_problem_details() | ||
{ | ||
// arrange | ||
var example = new | ||
{ | ||
error = new | ||
{ | ||
code = default( string ), | ||
message = default( string ), | ||
target = default( string ), | ||
innerError = new | ||
{ | ||
message = default( string ), | ||
}, | ||
}, | ||
}; | ||
|
||
// act | ||
var response = await GetAsync( "api/values?api-version=3.0" ); | ||
var error = await response.Content.ReadAsExampleAsync( example ); | ||
|
||
// assert | ||
response.Content.Headers.ContentType.MediaType.Should().Be( "application/json" ); | ||
error.Should().BeEquivalentTo( | ||
new | ||
{ | ||
error = new | ||
{ | ||
code = "UnsupportedApiVersion", | ||
message = "Unsupported API version", | ||
innerError = new | ||
{ | ||
message = "The HTTP resource that matches the request URI " + | ||
"'http://localhost/api/values' does not support " + | ||
"the API version '3.0'.", | ||
}, | ||
}, | ||
} ); | ||
} | ||
|
||
public when_error_objects_are_enabled( InteropFixture fixture ) : base( fixture ) { } | ||
} |
4 changes: 2 additions & 2 deletions
4
src/AspNetCore/WebApi/src/Asp.Versioning.Http/Asp.Versioning.Http.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
src/AspNetCore/WebApi/src/Asp.Versioning.Http/ErrorObjectFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
|
||
namespace Asp.Versioning; | ||
|
||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
/// <summary> | ||
/// Represents a factory that creates problem details formatted for error objects in responses. | ||
/// </summary> | ||
/// <remarks>This enables backward compatibility by converting <see cref="ProblemDetails"/> into Error Objects that | ||
/// conform to the <a ref="https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses">Error Responses</a> | ||
/// in the Microsoft REST API Guidelines and | ||
/// <a ref="https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#_Toc38457793">OData Error Responses</a>.</remarks> | ||
[CLSCompliant( false )] | ||
public sealed class ErrorObjectFactory : IProblemDetailsFactory | ||
{ | ||
private readonly IProblemDetailsFactory inner; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ErrorObjectFactory"/> class. | ||
/// </summary> | ||
public ErrorObjectFactory() : this( new DefaultProblemDetailsFactory() ) { } | ||
|
||
private ErrorObjectFactory( IProblemDetailsFactory inner ) => this.inner = inner; | ||
|
||
/// <summary> | ||
/// Creates and returns a new factory that decorates another factory. | ||
/// </summary> | ||
/// <param name="decorated">The inner, decorated factory instance.</param> | ||
/// <returns>A new <see cref="ErrorObjectFactory"/>.</returns> | ||
public static ErrorObjectFactory Decorate( IProblemDetailsFactory decorated ) => new( decorated ); | ||
|
||
/// <inheritdoc /> | ||
public ProblemDetails CreateProblemDetails( | ||
HttpRequest request, | ||
int? statusCode = null, | ||
string? title = null, | ||
string? type = null, | ||
string? detail = null, | ||
string? instance = null ) | ||
{ | ||
var problemDetails = inner.CreateProblemDetails( | ||
request ?? throw new ArgumentNullException( nameof( request ) ), | ||
statusCode, | ||
title, | ||
type, | ||
detail, | ||
instance ); | ||
|
||
if ( IsSupported( problemDetails ) ) | ||
{ | ||
var response = request.HttpContext.Response; | ||
response.OnStarting( ChangeContentType, response ); | ||
return ToErrorObject( problemDetails ); | ||
} | ||
|
||
return problemDetails; | ||
} | ||
|
||
private static bool IsSupported( ProblemDetails problemDetails ) | ||
{ | ||
var type = problemDetails.Type; | ||
|
||
return type == ProblemDetailsDefaults.Unsupported.Type || | ||
type == ProblemDetailsDefaults.Unspecified.Type || | ||
type == ProblemDetailsDefaults.Invalid.Type || | ||
type == ProblemDetailsDefaults.Ambiguous.Type; | ||
} | ||
|
||
private static ProblemDetails ToErrorObject( ProblemDetails problemDetails ) | ||
{ | ||
var error = new Dictionary<string, object>( capacity: 4 ); | ||
var errorObject = new ProblemDetails() | ||
{ | ||
Extensions = | ||
{ | ||
[nameof( error )] = error, | ||
}, | ||
}; | ||
|
||
if ( !string.IsNullOrEmpty( problemDetails.Title ) ) | ||
{ | ||
error["message"] = problemDetails.Title; | ||
} | ||
|
||
if ( problemDetails.Extensions.TryGetValue( "code", out var value ) && value is string code ) | ||
{ | ||
error["code"] = code; | ||
} | ||
|
||
if ( !string.IsNullOrEmpty( problemDetails.Instance ) ) | ||
{ | ||
error["target"] = problemDetails.Instance; | ||
} | ||
|
||
if ( !string.IsNullOrEmpty( problemDetails.Detail ) ) | ||
{ | ||
error["innerError"] = new Dictionary<string, object>( capacity: 1 ) | ||
{ | ||
["message"] = problemDetails.Detail, | ||
}; | ||
} | ||
|
||
return errorObject; | ||
} | ||
|
||
private static Task ChangeContentType( object state ) | ||
{ | ||
var response = (HttpResponse) state; | ||
response.ContentType = "application/json"; | ||
return Task.CompletedTask; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
| ||
Add backward compatibility for error objects |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters