Add 'default response' to API Response Type
Also some cleanup and unit tests.
This commit is contained in:
parent
ece5d6a690
commit
7ba167fcd8
|
|
@ -39,5 +39,15 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
/// Gets or sets the HTTP response status code.
|
/// Gets or sets the HTTP response status code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int StatusCode { get; set; }
|
public int StatusCode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the response type represents a default response.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If an <see cref="ApiDescription"/> has a default response, then the <see cref="StatusCode"/> property should be ignored. This response
|
||||||
|
/// will be used when a more specific response format does not apply. The common use of a default response is to specify the format
|
||||||
|
/// for communicating error conditions.
|
||||||
|
/// </remarks>
|
||||||
|
public bool IsDefaultResponse { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
|
{
|
||||||
|
public class ApiBehaviorApiDescriptionProvider : IApiDescriptionProvider
|
||||||
|
{
|
||||||
|
private readonly IModelMetadataProvider _modelMetadaProvider;
|
||||||
|
|
||||||
|
public ApiBehaviorApiDescriptionProvider(IModelMetadataProvider modelMetadataProvider)
|
||||||
|
{
|
||||||
|
_modelMetadaProvider = modelMetadataProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// The order is set to execute after the default provider.
|
||||||
|
/// </remarks>
|
||||||
|
public int Order => -1000 + 10;
|
||||||
|
|
||||||
|
public void OnProvidersExecuted(ApiDescriptionProviderContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnProvidersExecuting(ApiDescriptionProviderContext context)
|
||||||
|
{
|
||||||
|
foreach (var description in context.Results)
|
||||||
|
{
|
||||||
|
if (!AppliesTo(description))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var responseType in CreateProblemResponseTypes(description))
|
||||||
|
{
|
||||||
|
description.SupportedResponseTypes.Add(responseType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AppliesTo(ApiDescription description)
|
||||||
|
{
|
||||||
|
return description.ActionDescriptor.FilterDescriptors.Any(f => f.Filter is IApiBehaviorMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the parameter is named "id" (e.g. int id) or ends in Id (e.g. personId)
|
||||||
|
public bool IsIdParameter(ParameterDescriptor parameter)
|
||||||
|
{
|
||||||
|
if (parameter.Name == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.Equals("id", parameter.Name, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're looking for a name ending with Id, but preceded by a lower case letter. This should match
|
||||||
|
// the normal PascalCase naming conventions.
|
||||||
|
if (parameter.Name.Length >= 3 &&
|
||||||
|
parameter.Name.EndsWith("Id", StringComparison.Ordinal) &&
|
||||||
|
char.IsLower(parameter.Name, parameter.Name.Length - 3))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<ApiResponseType> CreateProblemResponseTypes(ApiDescription description)
|
||||||
|
{
|
||||||
|
if (description.ActionDescriptor.Parameters.Any() || description.ActionDescriptor.BoundProperties.Any())
|
||||||
|
{
|
||||||
|
// For validation errors.
|
||||||
|
yield return CreateProblemResponse(StatusCodes.Status400BadRequest);
|
||||||
|
|
||||||
|
if (description.ActionDescriptor.Parameters.Any(p => IsIdParameter(p)))
|
||||||
|
{
|
||||||
|
yield return CreateProblemResponse(StatusCodes.Status404NotFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return CreateProblemResponse(statusCode: 0, isDefaultResponse: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ApiResponseType CreateProblemResponse(int statusCode, bool isDefaultResponse = false)
|
||||||
|
{
|
||||||
|
return new ApiResponseType
|
||||||
|
{
|
||||||
|
ApiResponseFormats = new List<ApiResponseFormat>
|
||||||
|
{
|
||||||
|
new ApiResponseFormat
|
||||||
|
{
|
||||||
|
MediaType = "application/problem+json",
|
||||||
|
},
|
||||||
|
new ApiResponseFormat
|
||||||
|
{
|
||||||
|
MediaType = "application/problem+xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
IsDefaultResponse = isDefaultResponse,
|
||||||
|
ModelMetadata = _modelMetadaProvider.GetMetadataForType(typeof(ProblemDetails)),
|
||||||
|
StatusCode = statusCode,
|
||||||
|
Type = typeof(ProblemDetails),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
|
||||||
{
|
|
||||||
public class ApiControllerApiDescriptionProvider : IApiDescriptionProvider
|
|
||||||
{
|
|
||||||
private readonly IModelMetadataProvider _modelMetadaProvider;
|
|
||||||
|
|
||||||
public ApiControllerApiDescriptionProvider(IModelMetadataProvider modelMetadataProvider)
|
|
||||||
{
|
|
||||||
_modelMetadaProvider = modelMetadataProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <remarks>
|
|
||||||
/// The order is set to execute after the <see cref="DefaultApiDescriptionProvider"/>.
|
|
||||||
/// </remarks>
|
|
||||||
public int Order => -1000 + 10;
|
|
||||||
|
|
||||||
public void OnProvidersExecuted(ApiDescriptionProviderContext context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnProvidersExecuting(ApiDescriptionProviderContext context)
|
|
||||||
{
|
|
||||||
foreach (var apiDescription in context.Results)
|
|
||||||
{
|
|
||||||
if (!apiDescription.ActionDescriptor.FilterDescriptors.Any(f => f.Filter is IApiBehaviorMetadata))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameters = apiDescription.ActionDescriptor.Parameters.Concat(apiDescription.ActionDescriptor.BoundProperties);
|
|
||||||
if (parameters.Any())
|
|
||||||
{
|
|
||||||
apiDescription.SupportedResponseTypes.Add(CreateProblemResponse(StatusCodes.Status400BadRequest));
|
|
||||||
|
|
||||||
if (parameters.Any(p => p.Name.EndsWith("id", StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
apiDescription.SupportedResponseTypes.Add(CreateProblemResponse(StatusCodes.Status404NotFound));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't have a good way to signal a "default" response type. We'll use 0 to indicate this until we come up
|
|
||||||
// with something better.
|
|
||||||
apiDescription.SupportedResponseTypes.Add(CreateProblemResponse(statusCode: 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ApiResponseType CreateProblemResponse(int statusCode)
|
|
||||||
{
|
|
||||||
return new ApiResponseType
|
|
||||||
{
|
|
||||||
ApiResponseFormats = new List<ApiResponseFormat>
|
|
||||||
{
|
|
||||||
new ApiResponseFormat
|
|
||||||
{
|
|
||||||
MediaType = "application/problem+json",
|
|
||||||
},
|
|
||||||
new ApiResponseFormat
|
|
||||||
{
|
|
||||||
MediaType = "application/problem+xml",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ModelMetadata = _modelMetadaProvider.GetMetadataForType(typeof(ProblemDetails)),
|
|
||||||
StatusCode = statusCode,
|
|
||||||
Type = typeof(ProblemDetails),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
|
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IApiDescriptionProvider, ApiControllerApiDescriptionProvider>());
|
ServiceDescriptor.Transient<IApiDescriptionProvider, ApiBehaviorApiDescriptionProvider>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
|
ServiceDescriptor.Transient<IApplicationModelProvider, DefaultApplicationModelProvider>());
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IApplicationModelProvider, ApiControllerApplicationModelProvider>());
|
ServiceDescriptor.Transient<IApplicationModelProvider, ApiBehaviorApplicationModelProvider>());
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());
|
ServiceDescriptor.Transient<IActionDescriptorProvider, ControllerActionDescriptorProvider>());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
public class ApiControllerApplicationModelProvider : IApplicationModelProvider
|
public class ApiBehaviorApplicationModelProvider : IApplicationModelProvider
|
||||||
{
|
{
|
||||||
private readonly ApiBehaviorOptions _apiBehaviorOptions;
|
private readonly ApiBehaviorOptions _apiBehaviorOptions;
|
||||||
private readonly ModelStateInvalidFilter _modelStateInvalidFilter;
|
private readonly ModelStateInvalidFilter _modelStateInvalidFilter;
|
||||||
|
|
||||||
public ApiControllerApplicationModelProvider(IOptions<ApiBehaviorOptions> apiBehaviorOptions, ILoggerFactory loggerFactory)
|
public ApiBehaviorApplicationModelProvider(IOptions<ApiBehaviorOptions> apiBehaviorOptions, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_apiBehaviorOptions = apiBehaviorOptions.Value;
|
_apiBehaviorOptions = apiBehaviorOptions.Value;
|
||||||
if (_apiBehaviorOptions.EnableModelStateInvalidFilter && _apiBehaviorOptions.InvalidModelStateResponseFactory == null)
|
if (_apiBehaviorOptions.EnableModelStateInvalidFilter && _apiBehaviorOptions.InvalidModelStateResponseFactory == null)
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.ApiExplorer
|
||||||
|
{
|
||||||
|
public class ApiBehaviorApiDescriptionProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AppliesTo_ActionWithoutApiBehavior_ReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = new ActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new List<FilterDescriptor>(),
|
||||||
|
};
|
||||||
|
var description = new ApiDescription()
|
||||||
|
{
|
||||||
|
ActionDescriptor = action,
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = new ApiBehaviorApiDescriptionProvider(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.AppliesTo(description);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AppliesTo_ActionWithApiBehavior_ReturnsTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = new ActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new List<FilterDescriptor>()
|
||||||
|
{
|
||||||
|
new FilterDescriptor(Mock.Of<IApiBehaviorMetadata>(), FilterScope.Global),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var description = new ApiDescription()
|
||||||
|
{
|
||||||
|
ActionDescriptor = action,
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = new ApiBehaviorApiDescriptionProvider(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.AppliesTo(description);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("id")]
|
||||||
|
[InlineData("personId")]
|
||||||
|
[InlineData("üId")]
|
||||||
|
public void IsIdParameter_ParameterNameMatchesConvention_ReturnsTrue(string name)
|
||||||
|
{
|
||||||
|
var parameter = new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = new ApiBehaviorApiDescriptionProvider(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.IsIdParameter(parameter);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null)]
|
||||||
|
[InlineData("")]
|
||||||
|
[InlineData("i")]
|
||||||
|
[InlineData("Id")]
|
||||||
|
[InlineData("iD")]
|
||||||
|
[InlineData("persoNId")]
|
||||||
|
[InlineData("personid")]
|
||||||
|
[InlineData("ü Id")]
|
||||||
|
[InlineData("ÜId")]
|
||||||
|
public void IsIdParameter_ParameterNameDoesNotMatchConvention_ReturnsFalse(string name)
|
||||||
|
{
|
||||||
|
var parameter = new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = new ApiBehaviorApiDescriptionProvider(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = provider.IsIdParameter(parameter);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateProblemResponseTypes_NoParameters_IncludesDefaultResponse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = new ActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new List<FilterDescriptor>()
|
||||||
|
{
|
||||||
|
new FilterDescriptor(Mock.Of<IApiBehaviorMetadata>(), FilterScope.Global),
|
||||||
|
},
|
||||||
|
BoundProperties = new List<ParameterDescriptor>(),
|
||||||
|
Parameters = new List<ParameterDescriptor>(),
|
||||||
|
};
|
||||||
|
var description = new ApiDescription()
|
||||||
|
{
|
||||||
|
ActionDescriptor = action,
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = new ApiBehaviorApiDescriptionProvider(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = provider.CreateProblemResponseTypes(description);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
results.OrderBy(r => r.StatusCode),
|
||||||
|
r =>
|
||||||
|
{
|
||||||
|
Assert.Equal(typeof(ProblemDetails), r.Type);
|
||||||
|
Assert.Equal(0, r.StatusCode);
|
||||||
|
Assert.True(r.IsDefaultResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateProblemResponseTypes_WithBoundProperty_Includes400Response()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = new ActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new List<FilterDescriptor>()
|
||||||
|
{
|
||||||
|
new FilterDescriptor(Mock.Of<IApiBehaviorMetadata>(), FilterScope.Global),
|
||||||
|
},
|
||||||
|
BoundProperties = new List<ParameterDescriptor>()
|
||||||
|
{
|
||||||
|
new ParameterDescriptor()
|
||||||
|
},
|
||||||
|
Parameters = new List<ParameterDescriptor>(),
|
||||||
|
};
|
||||||
|
var description = new ApiDescription()
|
||||||
|
{
|
||||||
|
ActionDescriptor = action,
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = new ApiBehaviorApiDescriptionProvider(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = provider.CreateProblemResponseTypes(description);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
results.OrderBy(r => r.StatusCode),
|
||||||
|
r =>
|
||||||
|
{
|
||||||
|
Assert.Equal(typeof(ProblemDetails), r.Type);
|
||||||
|
Assert.Equal(0, r.StatusCode);
|
||||||
|
Assert.True(r.IsDefaultResponse);
|
||||||
|
},
|
||||||
|
r =>
|
||||||
|
{
|
||||||
|
Assert.Equal(typeof(ProblemDetails), r.Type);
|
||||||
|
Assert.Equal(400, r.StatusCode);
|
||||||
|
Assert.False(r.IsDefaultResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateProblemResponseTypes_WithIdParameter_Includes404Response()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action = new ActionDescriptor()
|
||||||
|
{
|
||||||
|
FilterDescriptors = new List<FilterDescriptor>()
|
||||||
|
{
|
||||||
|
new FilterDescriptor(Mock.Of<IApiBehaviorMetadata>(), FilterScope.Global),
|
||||||
|
},
|
||||||
|
BoundProperties = new List<ParameterDescriptor>()
|
||||||
|
{
|
||||||
|
},
|
||||||
|
Parameters = new List<ParameterDescriptor>()
|
||||||
|
{
|
||||||
|
new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
Name = "customerId",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var description = new ApiDescription()
|
||||||
|
{
|
||||||
|
ActionDescriptor = action,
|
||||||
|
};
|
||||||
|
|
||||||
|
var provider = new ApiBehaviorApiDescriptionProvider(new EmptyModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = provider.CreateProblemResponseTypes(description);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
results.OrderBy(r => r.StatusCode),
|
||||||
|
r =>
|
||||||
|
{
|
||||||
|
Assert.Equal(typeof(ProblemDetails), r.Type);
|
||||||
|
Assert.Equal(0, r.StatusCode);
|
||||||
|
Assert.True(r.IsDefaultResponse);
|
||||||
|
},
|
||||||
|
r =>
|
||||||
|
{
|
||||||
|
Assert.Equal(typeof(ProblemDetails), r.Type);
|
||||||
|
Assert.Equal(400, r.StatusCode);
|
||||||
|
Assert.False(r.IsDefaultResponse);
|
||||||
|
},
|
||||||
|
r =>
|
||||||
|
{
|
||||||
|
Assert.Equal(typeof(ProblemDetails), r.Type);
|
||||||
|
Assert.Equal(404, r.StatusCode);
|
||||||
|
Assert.False(r.IsDefaultResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -295,7 +295,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
new Type[]
|
new Type[]
|
||||||
{
|
{
|
||||||
typeof(DefaultApplicationModelProvider),
|
typeof(DefaultApplicationModelProvider),
|
||||||
typeof(ApiControllerApplicationModelProvider),
|
typeof(ApiBehaviorApplicationModelProvider),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
public class ApiControllerApplicationModelProviderTest
|
public class ApiBehaviorApplicationModelProviderTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnProvidersExecuting_AddsModelStateInvalidFilter_IfTypeIsAnnotatedWithAttribute()
|
public void OnProvidersExecuting_AddsModelStateInvalidFilter_IfTypeIsAnnotatedWithAttribute()
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
InvalidModelStateResponseFactory = _ => null,
|
InvalidModelStateResponseFactory = _ => null,
|
||||||
});
|
});
|
||||||
|
|
||||||
var provider = new ApiControllerApplicationModelProvider(options, NullLoggerFactory.Instance);
|
var provider = new ApiBehaviorApplicationModelProvider(options, NullLoggerFactory.Instance);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
EnableModelStateInvalidFilter = false,
|
EnableModelStateInvalidFilter = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
var provider = new ApiControllerApplicationModelProvider(options, NullLoggerFactory.Instance);
|
var provider = new ApiBehaviorApplicationModelProvider(options, NullLoggerFactory.Instance);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
InvalidModelStateResponseFactory = _ => null,
|
InvalidModelStateResponseFactory = _ => null,
|
||||||
});
|
});
|
||||||
|
|
||||||
var provider = new ApiControllerApplicationModelProvider(options, NullLoggerFactory.Instance);
|
var provider = new ApiBehaviorApplicationModelProvider(options, NullLoggerFactory.Instance);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
EnableModelStateInvalidFilter = false,
|
EnableModelStateInvalidFilter = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
var provider = new ApiControllerApplicationModelProvider(options, NullLoggerFactory.Instance);
|
var provider = new ApiBehaviorApplicationModelProvider(options, NullLoggerFactory.Instance);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
provider.OnProvidersExecuting(context);
|
provider.OnProvidersExecuting(context);
|
||||||
|
|
@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
InvalidModelStateResponseFactory = _ => null,
|
InvalidModelStateResponseFactory = _ => null,
|
||||||
});
|
});
|
||||||
|
|
||||||
var provider = new ApiControllerApplicationModelProvider(options, NullLoggerFactory.Instance);
|
var provider = new ApiBehaviorApplicationModelProvider(options, NullLoggerFactory.Instance);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
var ex = Assert.Throws<InvalidOperationException>(() => provider.OnProvidersExecuting(context));
|
||||||
|
|
@ -1079,6 +1079,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
response =>
|
response =>
|
||||||
{
|
{
|
||||||
Assert.Equal(0, response.StatusCode);
|
Assert.Equal(0, response.StatusCode);
|
||||||
|
Assert.True(response.IsDefaultResponse);
|
||||||
AssertProblemDetails(response);
|
AssertProblemDetails(response);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1097,12 +1098,14 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
response =>
|
response =>
|
||||||
{
|
{
|
||||||
Assert.Equal(0, response.StatusCode);
|
Assert.Equal(0, response.StatusCode);
|
||||||
|
Assert.True(response.IsDefaultResponse);
|
||||||
AssertProblemDetails(response);
|
AssertProblemDetails(response);
|
||||||
},
|
},
|
||||||
response => Assert.Equal(200, response.StatusCode),
|
response => Assert.Equal(200, response.StatusCode),
|
||||||
response =>
|
response =>
|
||||||
{
|
{
|
||||||
Assert.Equal(400, response.StatusCode);
|
Assert.Equal(400, response.StatusCode);
|
||||||
|
Assert.False(response.IsDefaultResponse);
|
||||||
AssertProblemDetails(response);
|
AssertProblemDetails(response);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1123,17 +1126,20 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
response =>
|
response =>
|
||||||
{
|
{
|
||||||
Assert.Equal(0, response.StatusCode);
|
Assert.Equal(0, response.StatusCode);
|
||||||
|
Assert.True(response.IsDefaultResponse);
|
||||||
AssertProblemDetails(response);
|
AssertProblemDetails(response);
|
||||||
},
|
},
|
||||||
response => Assert.Equal(200, response.StatusCode),
|
response => Assert.Equal(200, response.StatusCode),
|
||||||
response =>
|
response =>
|
||||||
{
|
{
|
||||||
Assert.Equal(400, response.StatusCode);
|
Assert.Equal(400, response.StatusCode);
|
||||||
|
Assert.False(response.IsDefaultResponse);
|
||||||
AssertProblemDetails(response);
|
AssertProblemDetails(response);
|
||||||
},
|
},
|
||||||
response =>
|
response =>
|
||||||
{
|
{
|
||||||
Assert.Equal(404, response.StatusCode);
|
Assert.Equal(404, response.StatusCode);
|
||||||
|
Assert.False(response.IsDefaultResponse);
|
||||||
AssertProblemDetails(response);
|
AssertProblemDetails(response);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -1199,6 +1205,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
public string ResponseType { get; set; }
|
public string ResponseType { get; set; }
|
||||||
|
|
||||||
public int StatusCode { get; set; }
|
public int StatusCode { get; set; }
|
||||||
|
|
||||||
|
public bool IsDefaultResponse { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ApiExplorerResponseFormat
|
private class ApiExplorerResponseFormat
|
||||||
|
|
|
||||||
|
|
@ -417,7 +417,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
typeof(CorsApplicationModelProvider),
|
typeof(CorsApplicationModelProvider),
|
||||||
typeof(AuthorizationApplicationModelProvider),
|
typeof(AuthorizationApplicationModelProvider),
|
||||||
typeof(TempDataApplicationModelProvider),
|
typeof(TempDataApplicationModelProvider),
|
||||||
typeof(ApiControllerApplicationModelProvider),
|
typeof(ApiBehaviorApplicationModelProvider),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -425,7 +425,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
new Type[]
|
new Type[]
|
||||||
{
|
{
|
||||||
typeof(DefaultApiDescriptionProvider),
|
typeof(DefaultApiDescriptionProvider),
|
||||||
typeof(ApiControllerApiDescriptionProvider),
|
typeof(ApiBehaviorApiDescriptionProvider),
|
||||||
typeof(JsonPatchOperationsArrayProvider),
|
typeof(JsonPatchOperationsArrayProvider),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ namespace ApiExplorerWebSite
|
||||||
var responseType = new ApiExplorerResponseType()
|
var responseType = new ApiExplorerResponseType()
|
||||||
{
|
{
|
||||||
StatusCode = response.StatusCode,
|
StatusCode = response.StatusCode,
|
||||||
ResponseType = response.Type?.FullName
|
ResponseType = response.Type?.FullName,
|
||||||
|
IsDefaultResponse = response.IsDefaultResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach(var responseFormat in response.ApiResponseFormats)
|
foreach(var responseFormat in response.ApiResponseFormats)
|
||||||
|
|
@ -152,6 +153,8 @@ namespace ApiExplorerWebSite
|
||||||
public string ResponseType { get; set; }
|
public string ResponseType { get; set; }
|
||||||
|
|
||||||
public int StatusCode { get; set; }
|
public int StatusCode { get; set; }
|
||||||
|
|
||||||
|
public bool IsDefaultResponse { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ApiExplorerResponseFormat
|
private class ApiExplorerResponseFormat
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue