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.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Setting the value of any properties on <see cref="ActionModel.ApiExplorer"/> will override any
|
||||
/// values set on the associated <see cref="ControllerModel.ApiExplorer"/>.
|
||||
/// <see cref="ActionModel.ApiExplorer"/> allows configuration of settings for 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>
|
||||
public ApiExplorerModel ApiExplorer { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,24 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
|||
{
|
||||
public ApplicationModel()
|
||||
{
|
||||
ApiExplorer = new ApiExplorerModel();
|
||||
Controllers = new List<ControllerModel>();
|
||||
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<IFilter> Filters { get; private set; }
|
||||
|
|
|
|||
|
|
@ -51,6 +51,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
|
|||
/// <summary>
|
||||
/// Gets or sets the <see cref="ApiExplorerModel"/> for this controller.
|
||||
/// </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 ApplicationModel Application { get; set; }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
@ -55,7 +56,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
actionDescriptor.ControllerName = controller.ControllerName;
|
||||
actionDescriptor.ControllerTypeInfo = controller.ControllerType;
|
||||
|
||||
AddApiExplorerInfo(actionDescriptor, action, controller);
|
||||
AddApiExplorerInfo(actionDescriptor, application, controller, action);
|
||||
AddRouteConstraints(removalConstraints, actionDescriptor, controller, action);
|
||||
|
||||
if (IsAttributeRoutedAction(actionDescriptor))
|
||||
|
|
@ -284,18 +285,40 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
private static void AddApiExplorerInfo(
|
||||
ControllerActionDescriptor actionDescriptor,
|
||||
ActionModel action,
|
||||
ControllerModel controller)
|
||||
ApplicationModel application,
|
||||
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(
|
||||
actionDescriptor.DisplayName));
|
||||
}
|
||||
// ApiExplorer is only supported on attribute routed actions.
|
||||
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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1092,6 +1092,38 @@ namespace Microsoft.AspNet.Mvc.Test
|
|||
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]
|
||||
[InlineData("A", typeof(ApiExplorerEnabledConventionalRoutedController))]
|
||||
[InlineData("A", typeof(ApiExplorerEnabledActionConventionalRoutedController))]
|
||||
|
|
@ -1110,6 +1142,23 @@ namespace Microsoft.AspNet.Mvc.Test
|
|||
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
|
||||
[Fact]
|
||||
public void ApplyConventions_RunsInOrderOfDecreasingScope()
|
||||
|
|
@ -1352,7 +1401,7 @@ namespace Microsoft.AspNet.Mvc.Test
|
|||
|
||||
private ControllerActionDescriptorProvider GetProvider(
|
||||
TypeInfo type,
|
||||
IOptions<MvcOptions> options)
|
||||
IApplicationModelConvention convention)
|
||||
{
|
||||
var modelBuilder = new StaticControllerModelBuilder(type);
|
||||
|
||||
|
|
@ -1361,6 +1410,9 @@ namespace Microsoft.AspNet.Mvc.Test
|
|||
.SetupGet(ap => ap.CandidateAssemblies)
|
||||
.Returns(new Assembly[] { type.Assembly });
|
||||
|
||||
var options = new MockMvcOptionsAccessor();
|
||||
options.Options.ApplicationModelConventions.Add(convention);
|
||||
|
||||
return new ControllerActionDescriptorProvider(
|
||||
assemblyProvider.Object,
|
||||
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