Add ApiExplorer details to ApplicationModel
This change allows you to set global defaults for ApiExplorer on the ApplicationModel. Additionally, we're more lenient about configuring ApiExplorer = on with conventional routing. If you turn on ApiExplorer at the application level, we'll just skip over all conventionally routed controllers instead of throwing.
This commit is contained in:
parent
8399dc5f4e
commit
ee419e2442
|
|
@ -58,8 +58,11 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
/// Gets or sets the <see cref="ApiExplorerModel"/> for this action.
|
/// Gets or sets the <see cref="ApiExplorerModel"/> for this action.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Setting the value of any properties on <see cref="ActionModel.ApiExplorer"/> will override any
|
/// <see cref="ActionModel.ApiExplorer"/> allows configuration of settings for ApiExplorer
|
||||||
/// values set on the associated <see cref="ControllerModel.ApiExplorer"/>.
|
/// which apply to the action.
|
||||||
|
///
|
||||||
|
/// Settings applied by <see cref="ActionModel.ApiExplorer"/> override settings from
|
||||||
|
/// <see cref="ApplicationModel.ApiExplorer"/> and <see cref="ControllerModel.ApiExplorer"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public ApiExplorerModel ApiExplorer { get; set; }
|
public ApiExplorerModel ApiExplorer { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,24 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
{
|
{
|
||||||
public ApplicationModel()
|
public ApplicationModel()
|
||||||
{
|
{
|
||||||
|
ApiExplorer = new ApiExplorerModel();
|
||||||
Controllers = new List<ControllerModel>();
|
Controllers = new List<ControllerModel>();
|
||||||
Filters = new List<IFilter>();
|
Filters = new List<IFilter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ApiExplorerModel"/> for the application.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="ApplicationModel.ApiExplorer"/> allows configuration of default settings
|
||||||
|
/// for ApiExplorer that apply to all actions unless overridden by
|
||||||
|
/// <see cref="ControllerModel.ApiExplorer"/> or <see cref="ActionModel.ApiExplorer"/>.
|
||||||
|
///
|
||||||
|
/// If using <see cref="ApplicationModel.ApiExplorer"/> to set <see cref="ApiExplorerModel.IsVisible"/> to
|
||||||
|
/// <c>true</c>, this setting will only be honored for actions which use attribute routing.
|
||||||
|
/// </remarks>
|
||||||
|
public ApiExplorerModel ApiExplorer { get; set; }
|
||||||
|
|
||||||
public IList<ControllerModel> Controllers { get; private set; }
|
public IList<ControllerModel> Controllers { get; private set; }
|
||||||
|
|
||||||
public IList<IFilter> Filters { get; private set; }
|
public IList<IFilter> Filters { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="ApiExplorerModel"/> for this controller.
|
/// Gets or sets the <see cref="ApiExplorerModel"/> for this controller.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <see cref="ControllerModel.ApiExplorer"/> allows configuration of settings for ApiExplorer
|
||||||
|
/// which apply to all actions in the controller unless overridden by <see cref="ActionModel.ApiExplorer"/>.
|
||||||
|
///
|
||||||
|
/// Settings applied by <see cref="ControllerModel.ApiExplorer"/> override settings from
|
||||||
|
/// <see cref="ApplicationModel.ApiExplorer"/>.
|
||||||
|
/// </remarks>
|
||||||
public ApiExplorerModel ApiExplorer { get; set; }
|
public ApiExplorerModel ApiExplorer { get; set; }
|
||||||
|
|
||||||
public ApplicationModel Application { get; set; }
|
public ApplicationModel Application { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
@ -55,7 +56,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
actionDescriptor.ControllerName = controller.ControllerName;
|
actionDescriptor.ControllerName = controller.ControllerName;
|
||||||
actionDescriptor.ControllerTypeInfo = controller.ControllerType;
|
actionDescriptor.ControllerTypeInfo = controller.ControllerType;
|
||||||
|
|
||||||
AddApiExplorerInfo(actionDescriptor, action, controller);
|
AddApiExplorerInfo(actionDescriptor, application, controller, action);
|
||||||
AddRouteConstraints(removalConstraints, actionDescriptor, controller, action);
|
AddRouteConstraints(removalConstraints, actionDescriptor, controller, action);
|
||||||
|
|
||||||
if (IsAttributeRoutedAction(actionDescriptor))
|
if (IsAttributeRoutedAction(actionDescriptor))
|
||||||
|
|
@ -284,18 +285,40 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
private static void AddApiExplorerInfo(
|
private static void AddApiExplorerInfo(
|
||||||
ControllerActionDescriptor actionDescriptor,
|
ControllerActionDescriptor actionDescriptor,
|
||||||
ActionModel action,
|
ApplicationModel application,
|
||||||
ControllerModel controller)
|
ControllerModel controller,
|
||||||
|
ActionModel action)
|
||||||
{
|
{
|
||||||
var apiExplorerIsVisible = action.ApiExplorer?.IsVisible ?? controller.ApiExplorer?.IsVisible ?? false;
|
|
||||||
if (apiExplorerIsVisible)
|
var isVisible =
|
||||||
|
action.ApiExplorer?.IsVisible ??
|
||||||
|
controller.ApiExplorer?.IsVisible ??
|
||||||
|
application.ApiExplorer?.IsVisible ??
|
||||||
|
false;
|
||||||
|
|
||||||
|
var isVisibleSetOnActionOrController =
|
||||||
|
action.ApiExplorer?.IsVisible ??
|
||||||
|
controller.ApiExplorer?.IsVisible ??
|
||||||
|
false;
|
||||||
|
|
||||||
|
// ApiExplorer isn't supported on conventional-routed actions, but we still allow you to configure
|
||||||
|
// it at the application level when you have a mix of controller types. We'll just skip over enabling
|
||||||
|
// ApiExplorer for conventional-routed controllers when this happens.
|
||||||
|
var isVisibleSetOnApplication = application.ApiExplorer?.IsVisible ?? false;
|
||||||
|
|
||||||
|
if (isVisibleSetOnActionOrController && !IsAttributeRoutedAction(actionDescriptor))
|
||||||
{
|
{
|
||||||
if (!IsAttributeRoutedAction(actionDescriptor))
|
// ApiExplorer is only supported on attribute routed actions.
|
||||||
{
|
throw new InvalidOperationException(Resources.FormatApiExplorer_UnsupportedAction(
|
||||||
// ApiExplorer is only supported on attribute routed actions.
|
actionDescriptor.DisplayName));
|
||||||
throw new InvalidOperationException(Resources.FormatApiExplorer_UnsupportedAction(
|
}
|
||||||
actionDescriptor.DisplayName));
|
else if (isVisibleSetOnApplication && !IsAttributeRoutedAction(actionDescriptor))
|
||||||
}
|
{
|
||||||
|
// This is the case where we're going to be lenient, just ignore it.
|
||||||
|
}
|
||||||
|
else if (isVisible)
|
||||||
|
{
|
||||||
|
Debug.Assert(IsAttributeRoutedAction(actionDescriptor));
|
||||||
|
|
||||||
var apiExplorerActionData = new ApiDescriptionActionData()
|
var apiExplorerActionData = new ApiDescriptionActionData()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1092,6 +1092,38 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
Assert.Equal("Store", action.GetProperty<ApiDescriptionActionData>().GroupName);
|
Assert.Equal("Store", action.GetProperty<ApiDescriptionActionData>().GroupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ApiExplorer_IsVisibleOnApplication_CanOverrideOnController()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var convention = new ApiExplorerIsVisibleConvention(isVisible: true);
|
||||||
|
var provider = GetProvider(typeof(ApiExplorerExplicitlyNotVisibleController).GetTypeInfo(), convention);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actions = provider.GetDescriptors();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var action = Assert.Single(actions);
|
||||||
|
Assert.Null(action.GetProperty<ApiDescriptionActionData>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ApiExplorer_IsVisibleOnApplication_CanOverrideOnAction()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var convention = new ApiExplorerIsVisibleConvention(isVisible: true);
|
||||||
|
var provider = GetProvider(
|
||||||
|
typeof(ApiExplorerExplicitlyNotVisibleOnActionController).GetTypeInfo(),
|
||||||
|
convention);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actions = provider.GetDescriptors();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var action = Assert.Single(actions);
|
||||||
|
Assert.Null(action.GetProperty<ApiDescriptionActionData>());
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("A", typeof(ApiExplorerEnabledConventionalRoutedController))]
|
[InlineData("A", typeof(ApiExplorerEnabledConventionalRoutedController))]
|
||||||
[InlineData("A", typeof(ApiExplorerEnabledActionConventionalRoutedController))]
|
[InlineData("A", typeof(ApiExplorerEnabledActionConventionalRoutedController))]
|
||||||
|
|
@ -1110,6 +1142,23 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
Assert.Equal(expected, ex.Message);
|
Assert.Equal(expected, ex.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ApiExplorer_SkipsConventionalRoutedController_WhenConfiguredOnApplication()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var convention = new ApiExplorerIsVisibleConvention(isVisible: true);
|
||||||
|
var provider = GetProvider(
|
||||||
|
typeof(ConventionallyRoutedController).GetTypeInfo(),
|
||||||
|
convention);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actions = provider.GetDescriptors();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var action = Assert.Single(actions);
|
||||||
|
Assert.Null(action.GetProperty<ApiDescriptionActionData>());
|
||||||
|
}
|
||||||
|
|
||||||
// Verifies the sequence of conventions running
|
// Verifies the sequence of conventions running
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ApplyConventions_RunsInOrderOfDecreasingScope()
|
public void ApplyConventions_RunsInOrderOfDecreasingScope()
|
||||||
|
|
@ -1352,7 +1401,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
|
|
||||||
private ControllerActionDescriptorProvider GetProvider(
|
private ControllerActionDescriptorProvider GetProvider(
|
||||||
TypeInfo type,
|
TypeInfo type,
|
||||||
IOptions<MvcOptions> options)
|
IApplicationModelConvention convention)
|
||||||
{
|
{
|
||||||
var modelBuilder = new StaticControllerModelBuilder(type);
|
var modelBuilder = new StaticControllerModelBuilder(type);
|
||||||
|
|
||||||
|
|
@ -1361,6 +1410,9 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
.SetupGet(ap => ap.CandidateAssemblies)
|
.SetupGet(ap => ap.CandidateAssemblies)
|
||||||
.Returns(new Assembly[] { type.Assembly });
|
.Returns(new Assembly[] { type.Assembly });
|
||||||
|
|
||||||
|
var options = new MockMvcOptionsAccessor();
|
||||||
|
options.Options.ApplicationModelConventions.Add(convention);
|
||||||
|
|
||||||
return new ControllerActionDescriptorProvider(
|
return new ControllerActionDescriptorProvider(
|
||||||
assemblyProvider.Object,
|
assemblyProvider.Object,
|
||||||
modelBuilder,
|
modelBuilder,
|
||||||
|
|
@ -1894,5 +1946,20 @@ namespace Microsoft.AspNet.Mvc.Test
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ApiExplorerIsVisibleConvention : IApplicationModelConvention
|
||||||
|
{
|
||||||
|
private bool _isVisible;
|
||||||
|
|
||||||
|
public ApiExplorerIsVisibleConvention(bool isVisible)
|
||||||
|
{
|
||||||
|
_isVisible = isVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply([NotNull] ApplicationModel application)
|
||||||
|
{
|
||||||
|
application.ApiExplorer.IsVisible = _isVisible;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue