[Fixes #4043] Figure out ControllerModel.AttributeRoutes
This commit is contained in:
parent
5423dc8c34
commit
b7cde3e58f
|
|
@ -6,15 +6,12 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
[DebuggerDisplay("Name={ActionName}({Methods()}), Type={Controller.ControllerType.Name}," +
|
||||
" Route: {AttributeRouteModel?.Template}, Filters: {Filters.Count}")]
|
||||
[DebuggerDisplay("{Controller.ControllerType.Name}.{ActionMethod.Name}")]
|
||||
public class ActionModel : ICommonModel, IFilterModel, IApiExplorerModel
|
||||
{
|
||||
public ActionModel(
|
||||
|
|
@ -35,12 +32,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
ApiExplorer = new ApiExplorerModel();
|
||||
Attributes = new List<object>(attributes);
|
||||
ActionConstraints = new List<IActionConstraintMetadata>();
|
||||
Filters = new List<IFilterMetadata>();
|
||||
HttpMethods = new List<string>();
|
||||
Parameters = new List<ParameterModel>();
|
||||
RouteConstraints = new List<IRouteConstraintProvider>();
|
||||
Properties = new Dictionary<object, object>();
|
||||
Selectors = new List<SelectorModel>();
|
||||
}
|
||||
|
||||
public ActionModel(ActionModel other)
|
||||
|
|
@ -57,25 +53,17 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
Controller = other.Controller;
|
||||
|
||||
// These are just metadata, safe to create new collections
|
||||
ActionConstraints = new List<IActionConstraintMetadata>(other.ActionConstraints);
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
Filters = new List<IFilterMetadata>(other.Filters);
|
||||
HttpMethods = new List<string>(other.HttpMethods);
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
|
||||
// Make a deep copy of other 'model' types.
|
||||
ApiExplorer = new ApiExplorerModel(other.ApiExplorer);
|
||||
Parameters = new List<ParameterModel>(other.Parameters.Select(p => new ParameterModel(p)));
|
||||
RouteConstraints = new List<IRouteConstraintProvider>(other.RouteConstraints);
|
||||
|
||||
if (other.AttributeRouteModel != null)
|
||||
{
|
||||
AttributeRouteModel = new AttributeRouteModel(other.AttributeRouteModel);
|
||||
}
|
||||
Selectors = new List<SelectorModel>(other.Selectors.Select(s => new SelectorModel(s)));
|
||||
}
|
||||
|
||||
public IList<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
||||
|
||||
public MethodInfo ActionMethod { get; }
|
||||
|
||||
public string ActionName { get; set; }
|
||||
|
|
@ -86,25 +74,21 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// <remarks>
|
||||
/// <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; }
|
||||
|
||||
public AttributeRouteModel AttributeRouteModel { get; set; }
|
||||
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
public ControllerModel Controller { get; set; }
|
||||
|
||||
public IList<IFilterMetadata> Filters { get; private set; }
|
||||
public IList<IFilterMetadata> Filters { get; }
|
||||
|
||||
public IList<string> HttpMethods { get; private set; }
|
||||
public IList<ParameterModel> Parameters { get; }
|
||||
|
||||
public IList<ParameterModel> Parameters { get; private set; }
|
||||
|
||||
public IList<IRouteConstraintProvider> RouteConstraints { get; private set; }
|
||||
public IList<IRouteConstraintProvider> RouteConstraints { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a set of properties associated with the action.
|
||||
|
|
@ -120,14 +104,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
string ICommonModel.Name => ActionName;
|
||||
|
||||
private string Methods()
|
||||
{
|
||||
if (HttpMethods.Count == 0)
|
||||
{
|
||||
return "All";
|
||||
}
|
||||
|
||||
return string.Join(", ", HttpMethods);
|
||||
}
|
||||
public IList<SelectorModel> Selectors { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,12 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
[DebuggerDisplay("Name={ControllerName}, Type={ControllerType.Name}," +
|
||||
" Routes: {AttributeRoutes.Count}, Filters: {Filters.Count}")]
|
||||
[DebuggerDisplay("Name={ControllerName}, Type={ControllerType.Name}")]
|
||||
public class ControllerModel : ICommonModel, IFilterModel, IApiExplorerModel
|
||||
{
|
||||
public ControllerModel(
|
||||
|
|
@ -35,12 +33,11 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
Actions = new List<ActionModel>();
|
||||
ApiExplorer = new ApiExplorerModel();
|
||||
Attributes = new List<object>(attributes);
|
||||
AttributeRoutes = new List<AttributeRouteModel>();
|
||||
ActionConstraints = new List<IActionConstraintMetadata>();
|
||||
Filters = new List<IFilterMetadata>();
|
||||
RouteConstraints = new List<IRouteConstraintProvider>();
|
||||
Properties = new Dictionary<object, object>();
|
||||
ControllerProperties = new List<PropertyModel>();
|
||||
Filters = new List<IFilterMetadata>();
|
||||
Properties = new Dictionary<object, object>();
|
||||
RouteConstraints = new List<IRouteConstraintProvider>();
|
||||
Selectors = new List<SelectorModel>();
|
||||
}
|
||||
|
||||
public ControllerModel(ControllerModel other)
|
||||
|
|
@ -57,7 +54,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
Application = other.Application;
|
||||
|
||||
// These are just metadata, safe to create new collections
|
||||
ActionConstraints = new List<IActionConstraintMetadata>(other.ActionConstraints);
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
Filters = new List<IFilterMetadata>(other.Filters);
|
||||
RouteConstraints = new List<IRouteConstraintProvider>(other.RouteConstraints);
|
||||
|
|
@ -66,15 +62,12 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
// Make a deep copy of other 'model' types.
|
||||
Actions = new List<ActionModel>(other.Actions.Select(a => new ActionModel(a)));
|
||||
ApiExplorer = new ApiExplorerModel(other.ApiExplorer);
|
||||
AttributeRoutes = new List<AttributeRouteModel>(
|
||||
other.AttributeRoutes.Select(a => new AttributeRouteModel(a)));
|
||||
ControllerProperties =
|
||||
new List<PropertyModel>(other.ControllerProperties.Select(p => new PropertyModel(p)));
|
||||
Selectors = new List<SelectorModel>(other.Selectors.Select(s => new SelectorModel(s)));
|
||||
}
|
||||
|
||||
public IList<IActionConstraintMetadata> ActionConstraints { get; private set; }
|
||||
|
||||
public IList<ActionModel> Actions { get; private set; }
|
||||
public IList<ActionModel> Actions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ApiExplorerModel"/> for this controller.
|
||||
|
|
@ -82,7 +75,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// <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>
|
||||
|
|
@ -90,8 +83,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
public ApplicationModel Application { get; set; }
|
||||
|
||||
public IList<AttributeRouteModel> AttributeRoutes { get; private set; }
|
||||
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => ControllerType;
|
||||
|
|
@ -100,13 +91,13 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
public string ControllerName { get; set; }
|
||||
|
||||
public TypeInfo ControllerType { get; private set; }
|
||||
public TypeInfo ControllerType { get; }
|
||||
|
||||
public IList<PropertyModel> ControllerProperties { get; }
|
||||
|
||||
public IList<IFilterMetadata> Filters { get; private set; }
|
||||
public IList<IFilterMetadata> Filters { get; }
|
||||
|
||||
public IList<IRouteConstraintProvider> RouteConstraints { get; private set; }
|
||||
public IList<IRouteConstraintProvider> RouteConstraints { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a set of properties associated with the controller.
|
||||
|
|
@ -117,5 +108,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// in <see cref="ApplicationModel.Properties"/>.
|
||||
/// </remarks>
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
public IList<SelectorModel> Selectors { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
// 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 Microsoft.AspNetCore.Mvc.ActionConstraints;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
public class SelectorModel
|
||||
{
|
||||
public SelectorModel()
|
||||
{
|
||||
ActionConstraints = new List<IActionConstraintMetadata>();
|
||||
}
|
||||
|
||||
public SelectorModel(SelectorModel other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
ActionConstraints = new List<IActionConstraintMetadata>(other.ActionConstraints);
|
||||
|
||||
if (other.AttributeRouteModel != null)
|
||||
{
|
||||
AttributeRouteModel = new AttributeRouteModel(other.AttributeRouteModel);
|
||||
}
|
||||
}
|
||||
|
||||
public AttributeRouteModel AttributeRouteModel { get; set; }
|
||||
|
||||
public IList<IActionConstraintMetadata> ActionConstraints { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
|||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Core;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Tree;
|
||||
|
||||
|
|
@ -47,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
foreach (var controller in application.Controllers)
|
||||
{
|
||||
// Only add properties which are explictly marked to bind.
|
||||
// Only add properties which are explicitly marked to bind.
|
||||
// The attribute check is required for ModelBinder attribute.
|
||||
var controllerPropertyDescriptors = controller.ControllerProperties
|
||||
.Where(p => p.BindingInfo != null)
|
||||
|
|
@ -128,7 +127,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// Add a route constraint with DenyKey for each constraint in the set to all the
|
||||
// actions that don't have that constraint. For example, if a controller defines
|
||||
// an area constraint, all actions that don't belong to an area must have a route
|
||||
// constraint that prevents them from matching an incomming request.
|
||||
// constraint that prevents them from matching an incoming request.
|
||||
AddRemovalConstraints(actionDescriptor, removalConstraints);
|
||||
}
|
||||
else
|
||||
|
|
@ -185,67 +184,87 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
ControllerModel controller,
|
||||
ActionModel action)
|
||||
{
|
||||
var controllerAttributeRoutes = controller.Selectors
|
||||
.Where(sm => sm.AttributeRouteModel != null)
|
||||
.Select(sm => sm.AttributeRouteModel)
|
||||
.ToList();
|
||||
|
||||
var actionDescriptors = new List<ControllerActionDescriptor>();
|
||||
|
||||
// We check the action to see if the template allows combination behavior
|
||||
// (It doesn't start with / or ~/) so that in the case where we have multiple
|
||||
// [Route] attributes on the controller we don't end up creating multiple
|
||||
if (action.AttributeRouteModel != null &&
|
||||
action.AttributeRouteModel.IsAbsoluteTemplate)
|
||||
foreach (var actionSelectorModel in action.Selectors)
|
||||
{
|
||||
// We're overriding the attribute routes on the controller, so filter out any metadata
|
||||
// from controller level routes.
|
||||
var actionDescriptor = CreateActionDescriptor(
|
||||
action,
|
||||
controllerAttributeRoute: null);
|
||||
var actionAttributeRoute = actionSelectorModel.AttributeRouteModel;
|
||||
|
||||
actionDescriptors.Add(actionDescriptor);
|
||||
|
||||
// If we're using an attribute route on the controller, then filter out any additional
|
||||
// metadata from the 'other' attribute routes.
|
||||
var controllerFilters = controller.Filters
|
||||
.Where(c => !(c is IRouteTemplateProvider));
|
||||
AddActionFilters(actionDescriptor, action.Filters, controllerFilters, application.Filters);
|
||||
|
||||
var controllerConstraints = controller.ActionConstraints
|
||||
.Where(c => !(c is IRouteTemplateProvider));
|
||||
AddActionConstraints(actionDescriptor, action, controllerConstraints);
|
||||
}
|
||||
else if (controller.AttributeRoutes != null &&
|
||||
controller.AttributeRoutes.Count > 0)
|
||||
{
|
||||
// We're using the attribute routes from the controller
|
||||
foreach (var controllerAttributeRoute in controller.AttributeRoutes)
|
||||
// We check the action to see if the template allows combination behavior
|
||||
// (It doesn't start with / or ~/) so that in the case where we have multiple
|
||||
// [Route] attributes on the controller we don't end up creating multiple
|
||||
if (actionAttributeRoute != null && actionAttributeRoute.IsAbsoluteTemplate)
|
||||
{
|
||||
// We're overriding the attribute routes on the controller, so filter out any metadata
|
||||
// from controller level routes.
|
||||
var actionDescriptor = CreateActionDescriptor(
|
||||
action,
|
||||
controllerAttributeRoute);
|
||||
actionAttributeRoute,
|
||||
controllerAttributeRoute: null);
|
||||
|
||||
actionDescriptors.Add(actionDescriptor);
|
||||
|
||||
AddActionFilters(actionDescriptor, action.Filters, controller.Filters, application.Filters);
|
||||
|
||||
// If we're using an attribute route on the controller, then filter out any additional
|
||||
// metadata from the 'other' attribute routes.
|
||||
var controllerFilters = controller.Filters
|
||||
.Where(c => c == controllerAttributeRoute?.Attribute || !(c is IRouteTemplateProvider));
|
||||
AddActionFilters(actionDescriptor, action.Filters, controllerFilters, application.Filters);
|
||||
IList<IActionConstraintMetadata> controllerConstraints = null;
|
||||
if (controller.Selectors.Count > 0)
|
||||
{
|
||||
controllerConstraints = controller.Selectors[0].ActionConstraints
|
||||
.Where(constraint => !(constraint is IRouteTemplateProvider)).ToList();
|
||||
}
|
||||
|
||||
var controllerConstraints = controller.ActionConstraints
|
||||
.Where(c => c == controllerAttributeRoute?.Attribute || !(c is IRouteTemplateProvider));
|
||||
AddActionConstraints(actionDescriptor, action, controllerConstraints);
|
||||
AddActionConstraints(actionDescriptor, action, actionSelectorModel, controllerConstraints);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No attribute routes on the controller
|
||||
var actionDescriptor = CreateActionDescriptor(
|
||||
action,
|
||||
controllerAttributeRoute: null);
|
||||
actionDescriptors.Add(actionDescriptor);
|
||||
else if (controllerAttributeRoutes.Count > 0)
|
||||
{
|
||||
// We're using the attribute routes from the controller
|
||||
foreach (var controllerSelectorModel in controller.Selectors)
|
||||
{
|
||||
var controllerAttributeRoute = controllerSelectorModel.AttributeRouteModel;
|
||||
|
||||
// If there's no attribute route on the controller, then we can use all of the filters/constraints
|
||||
// on the controller.
|
||||
AddActionFilters(actionDescriptor, action.Filters, controller.Filters, application.Filters);
|
||||
AddActionConstraints(actionDescriptor, action, controller.ActionConstraints);
|
||||
var actionDescriptor = CreateActionDescriptor(
|
||||
action,
|
||||
actionAttributeRoute,
|
||||
controllerAttributeRoute);
|
||||
|
||||
actionDescriptors.Add(actionDescriptor);
|
||||
|
||||
AddActionFilters(actionDescriptor, action.Filters, controller.Filters, application.Filters);
|
||||
|
||||
// If we're using an attribute route on the controller, then filter out any additional
|
||||
// metadata from the 'other' attribute routes.
|
||||
var controllerConstraints = controllerSelectorModel.ActionConstraints
|
||||
.Where(c => c == controllerAttributeRoute?.Attribute || !(c is IRouteTemplateProvider));
|
||||
AddActionConstraints(actionDescriptor, action, actionSelectorModel, controllerConstraints);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No attribute routes on the controller
|
||||
var actionDescriptor = CreateActionDescriptor(
|
||||
action,
|
||||
actionAttributeRoute,
|
||||
controllerAttributeRoute: null);
|
||||
actionDescriptors.Add(actionDescriptor);
|
||||
|
||||
IList<IActionConstraintMetadata> controllerConstraints = null;
|
||||
if (controller.Selectors.Count > 0)
|
||||
{
|
||||
controllerConstraints = controller.Selectors[0].ActionConstraints;
|
||||
}
|
||||
|
||||
// If there's no attribute route on the controller, then we use all of the filters/constraints
|
||||
// on the controller regardless.
|
||||
AddActionFilters(actionDescriptor, action.Filters, controller.Filters, application.Filters);
|
||||
AddActionConstraints(actionDescriptor, action, actionSelectorModel, controllerConstraints);
|
||||
}
|
||||
}
|
||||
|
||||
return actionDescriptors;
|
||||
|
|
@ -253,6 +272,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
private static ControllerActionDescriptor CreateActionDescriptor(
|
||||
ActionModel action,
|
||||
AttributeRouteModel actionAttributeRoute,
|
||||
AttributeRouteModel controllerAttributeRoute)
|
||||
{
|
||||
var parameterDescriptors = new List<ParameterDescriptor>();
|
||||
|
|
@ -262,17 +282,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
parameterDescriptors.Add(parameterDescriptor);
|
||||
}
|
||||
|
||||
var attributeRouteInfo = CreateAttributeRouteInfo(
|
||||
action.AttributeRouteModel,
|
||||
controllerAttributeRoute);
|
||||
|
||||
var actionDescriptor = new ControllerActionDescriptor()
|
||||
{
|
||||
Name = action.ActionName,
|
||||
MethodInfo = action.ActionMethod,
|
||||
Parameters = parameterDescriptors,
|
||||
RouteConstraints = new List<RouteDataActionConstraint>(),
|
||||
AttributeRouteInfo = attributeRouteInfo,
|
||||
AttributeRouteInfo = CreateAttributeRouteInfo(actionAttributeRoute, controllerAttributeRoute)
|
||||
};
|
||||
|
||||
actionDescriptor.DisplayName = string.Format(
|
||||
|
|
@ -316,15 +332,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
ControllerModel controller,
|
||||
ActionModel action)
|
||||
{
|
||||
var isVisible =
|
||||
action.ApiExplorer?.IsVisible ??
|
||||
controller.ApiExplorer?.IsVisible ??
|
||||
var isVisible =
|
||||
action.ApiExplorer?.IsVisible ??
|
||||
controller.ApiExplorer?.IsVisible ??
|
||||
application.ApiExplorer?.IsVisible ??
|
||||
false;
|
||||
|
||||
var isVisibleSetOnActionOrController =
|
||||
action.ApiExplorer?.IsVisible ??
|
||||
controller.ApiExplorer?.IsVisible ??
|
||||
var isVisibleSetOnActionOrController =
|
||||
action.ApiExplorer?.IsVisible ??
|
||||
controller.ApiExplorer?.IsVisible ??
|
||||
false;
|
||||
|
||||
// ApiExplorer isn't supported on conventional-routed actions, but we still allow you to configure
|
||||
|
|
@ -417,19 +433,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
private static void AddActionConstraints(
|
||||
ControllerActionDescriptor actionDescriptor,
|
||||
ActionModel action,
|
||||
SelectorModel selectorModel,
|
||||
IEnumerable<IActionConstraintMetadata> controllerConstraints)
|
||||
{
|
||||
var constraints = new List<IActionConstraintMetadata>();
|
||||
|
||||
var httpMethods = action.HttpMethods;
|
||||
if (httpMethods != null && httpMethods.Count > 0)
|
||||
if (selectorModel.ActionConstraints != null)
|
||||
{
|
||||
constraints.Add(new HttpMethodActionConstraint(httpMethods));
|
||||
}
|
||||
|
||||
if (action.ActionConstraints != null)
|
||||
{
|
||||
constraints.AddRange(action.ActionConstraints);
|
||||
constraints.AddRange(selectorModel.ActionConstraints);
|
||||
}
|
||||
|
||||
if (controllerConstraints != null)
|
||||
|
|
@ -449,7 +460,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
ControllerModel controller,
|
||||
ActionModel action)
|
||||
{
|
||||
// Apply all the constraints defined on the action, then controller (for example, [Area])
|
||||
// Apply all the constraints defined on the action, then controller (for example, [Area])
|
||||
// to the actions. Also keep track of all the constraints that require preventing actions
|
||||
// without the constraint to match. For example, actions without an [Area] attribute on their
|
||||
// controller should not match when a value has been given for area when matching a url or
|
||||
|
|
@ -557,7 +568,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
foreach (var constraint in actionDescriptor.RouteConstraints)
|
||||
{
|
||||
// We don't need to do anything with attribute routing for 'catch all' behavior. Order
|
||||
// and predecedence of attribute routes allow this kind of behavior.
|
||||
// and precedence of attribute routes allow this kind of behavior.
|
||||
if (constraint.KeyHandling == RouteKeyHandling.RequireKey ||
|
||||
constraint.KeyHandling == RouteKeyHandling.DenyKey)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Mvc.ActionConstraints;
|
|||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
|
@ -50,46 +49,44 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
foreach (var controllerType in context.ControllerTypes)
|
||||
{
|
||||
var controllerModels = BuildControllerModels(controllerType);
|
||||
if (controllerModels != null)
|
||||
var controllerModel = CreateControllerModel(controllerType);
|
||||
if (controllerModel == null)
|
||||
{
|
||||
foreach (var controllerModel in controllerModels)
|
||||
continue;
|
||||
}
|
||||
|
||||
context.Result.Controllers.Add(controllerModel);
|
||||
controllerModel.Application = context.Result;
|
||||
|
||||
foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType.AsType()))
|
||||
{
|
||||
var propertyInfo = propertyHelper.Property;
|
||||
var propertyModel = CreatePropertyModel(propertyInfo);
|
||||
if (propertyModel != null)
|
||||
{
|
||||
context.Result.Controllers.Add(controllerModel);
|
||||
controllerModel.Application = context.Result;
|
||||
propertyModel.Controller = controllerModel;
|
||||
controllerModel.ControllerProperties.Add(propertyModel);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType.AsType()))
|
||||
foreach (var methodInfo in controllerType.AsType().GetMethods())
|
||||
{
|
||||
var actionModel = CreateActionModel(controllerType, methodInfo);
|
||||
if (actionModel == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
actionModel.Controller = controllerModel;
|
||||
controllerModel.Actions.Add(actionModel);
|
||||
|
||||
foreach (var parameterInfo in actionModel.ActionMethod.GetParameters())
|
||||
{
|
||||
var parameterModel = CreateParameterModel(parameterInfo);
|
||||
if (parameterModel != null)
|
||||
{
|
||||
var propertyInfo = propertyHelper.Property;
|
||||
var propertyModel = CreatePropertyModel(propertyInfo);
|
||||
if (propertyModel != null)
|
||||
{
|
||||
propertyModel.Controller = controllerModel;
|
||||
controllerModel.ControllerProperties.Add(propertyModel);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var methodInfo in controllerType.AsType().GetMethods())
|
||||
{
|
||||
var actionModels = BuildActionModels(controllerType, methodInfo);
|
||||
if (actionModels != null)
|
||||
{
|
||||
foreach (var actionModel in actionModels)
|
||||
{
|
||||
actionModel.Controller = controllerModel;
|
||||
controllerModel.Actions.Add(actionModel);
|
||||
|
||||
foreach (var parameterInfo in actionModel.ActionMethod.GetParameters())
|
||||
{
|
||||
var parameterModel = CreateParameterModel(parameterInfo);
|
||||
if (parameterModel != null)
|
||||
{
|
||||
parameterModel.Action = actionModel;
|
||||
actionModel.Parameters.Add(parameterModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parameterModel.Action = actionModel;
|
||||
actionModel.Parameters.Add(parameterModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -102,25 +99,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// Intentionally empty.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="ControllerModel"/> instances for the given controller <see cref="TypeInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The controller <see cref="TypeInfo"/>.</param>
|
||||
/// <returns>
|
||||
/// A set of <see cref="ControllerModel"/> instances for the given controller <see cref="TypeInfo"/> or
|
||||
/// <c>null</c> if the <paramref name="typeInfo"/> does not represent a controller.
|
||||
/// </returns>
|
||||
protected virtual IEnumerable<ControllerModel> BuildControllerModels(TypeInfo typeInfo)
|
||||
{
|
||||
if (typeInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(typeInfo));
|
||||
}
|
||||
|
||||
var controllerModel = CreateControllerModel(typeInfo);
|
||||
yield return controllerModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ControllerModel"/> for the given <see cref="TypeInfo"/>.
|
||||
/// </summary>
|
||||
|
|
@ -177,20 +155,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
filteredAttributes.AddRange(routeAttributes);
|
||||
|
||||
attributes = filteredAttributes.ToArray();
|
||||
|
||||
var controllerModel = new ControllerModel(typeInfo, attributes);
|
||||
AddRange(
|
||||
controllerModel.AttributeRoutes, routeAttributes.Select(a => new AttributeRouteModel(a)));
|
||||
|
||||
AddRange(controllerModel.Selectors, CreateSelectors(attributes));
|
||||
|
||||
controllerModel.ControllerName =
|
||||
typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ?
|
||||
typeInfo.Name.Substring(0, typeInfo.Name.Length - "Controller".Length) :
|
||||
typeInfo.Name;
|
||||
|
||||
AddRange(controllerModel.ActionConstraints, attributes.OfType<IActionConstraintMetadata>());
|
||||
AddRange(controllerModel.Filters, attributes.OfType<IFilterMetadata>());
|
||||
AddRange(controllerModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
|
||||
|
||||
|
|
@ -250,15 +228,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="ControllerModel"/> instances for the given action <see cref="MethodInfo"/>.
|
||||
/// Creates the <see cref="ActionModel"/> instance for the given action <see cref="MethodInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The controller <see cref="TypeInfo"/>.</param>
|
||||
/// <param name="methodInfo">The action <see cref="MethodInfo"/>.</param>
|
||||
/// <returns>
|
||||
/// A set of <see cref="ActionModel"/> instances for the given action <see cref="MethodInfo"/> or
|
||||
/// An <see cref="ActionModel"/> instance for the given action <see cref="MethodInfo"/> or
|
||||
/// <c>null</c> if the <paramref name="methodInfo"/> does not represent an action.
|
||||
/// </returns>
|
||||
protected virtual IEnumerable<ActionModel> BuildActionModels(
|
||||
protected virtual ActionModel CreateActionModel(
|
||||
TypeInfo typeInfo,
|
||||
MethodInfo methodInfo)
|
||||
{
|
||||
|
|
@ -274,9 +252,44 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
if (!IsAction(typeInfo, methodInfo))
|
||||
{
|
||||
return Enumerable.Empty<ActionModel>();
|
||||
return null;
|
||||
}
|
||||
|
||||
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
|
||||
// is needed to so that the result of ToArray() is object
|
||||
var attributes = methodInfo.GetCustomAttributes(inherit: true).OfType<object>().ToArray();
|
||||
|
||||
var actionModel = new ActionModel(methodInfo, attributes);
|
||||
|
||||
AddRange(actionModel.Filters, attributes.OfType<IFilterMetadata>());
|
||||
|
||||
var actionName = attributes.OfType<ActionNameAttribute>().FirstOrDefault();
|
||||
if (actionName?.Name != null)
|
||||
{
|
||||
actionModel.ActionName = actionName.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
actionModel.ActionName = methodInfo.Name;
|
||||
}
|
||||
|
||||
var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
|
||||
if (apiVisibility != null)
|
||||
{
|
||||
actionModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
|
||||
}
|
||||
|
||||
var apiGroupName = attributes.OfType<IApiDescriptionGroupNameProvider>().FirstOrDefault();
|
||||
if (apiGroupName != null)
|
||||
{
|
||||
actionModel.ApiExplorer.GroupName = apiGroupName.GroupName;
|
||||
}
|
||||
|
||||
AddRange(actionModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
|
||||
|
||||
//TODO: modify comment
|
||||
// Now we need to determine the action selection info (cross-section of routes and constraints)
|
||||
//
|
||||
// For attribute routes on a action, we want want to support 'overriding' routes on a
|
||||
// virtual method, but allow 'overriding'. So we need to walk up the hierarchy looking
|
||||
// for the first definition to define routes.
|
||||
|
|
@ -309,10 +322,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
currentMethodInfo = nextMethodInfo;
|
||||
}
|
||||
|
||||
// CoreCLR returns IEnumerable<Attribute> from GetCustomAttributes - the OfType<object>
|
||||
// is needed to so that the result of ToArray() is object
|
||||
var attributes = methodInfo.GetCustomAttributes(inherit: true).OfType<object>().ToArray();
|
||||
|
||||
// This is fairly complicated so that we maintain referential equality between items in
|
||||
// ActionModel.Attributes and ActionModel.Attributes[*].Attribute.
|
||||
var applicableAttributes = new List<object>();
|
||||
|
|
@ -327,134 +336,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
applicableAttributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
applicableAttributes.AddRange(routeAttributes);
|
||||
AddRange(actionModel.Selectors, CreateSelectors(applicableAttributes));
|
||||
|
||||
attributes = applicableAttributes.ToArray();
|
||||
|
||||
// Route attributes create multiple actions, we want to split the set of
|
||||
// attributes based on these so each action only has the attributes that affect it.
|
||||
//
|
||||
// The set of route attributes are split into those that 'define' a route versus those that are
|
||||
// 'silent'.
|
||||
//
|
||||
// We need to define an action for each attribute that 'defines' a route, and a single action
|
||||
// for all of the ones that don't (if any exist).
|
||||
//
|
||||
// If the attribute that 'defines' a route is NOT an IActionHttpMethodProvider, then we'll include with
|
||||
// it, any IActionHttpMethodProvider that are 'silent' IRouteTemplateProviders. In this case the 'extra'
|
||||
// action for silent route providers isn't needed.
|
||||
//
|
||||
// Ex:
|
||||
// [HttpGet]
|
||||
// [AcceptVerbs("POST", "PUT")]
|
||||
// [HttpPost("Api/Things")]
|
||||
// public void DoThing()
|
||||
//
|
||||
// This will generate 2 actions:
|
||||
// 1. [HttpPost("Api/Things")]
|
||||
// 2. [HttpGet], [AcceptVerbs("POST", "PUT")]
|
||||
//
|
||||
// Note that having a route attribute that doesn't define a route template _might_ be an error. We
|
||||
// don't have enough context to really know at this point so we just pass it on.
|
||||
var routeProviders = new List<object>();
|
||||
|
||||
var createActionForSilentRouteProviders = false;
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
var routeTemplateProvider = attribute as IRouteTemplateProvider;
|
||||
if (routeTemplateProvider != null)
|
||||
{
|
||||
if (IsSilentRouteAttribute(routeTemplateProvider))
|
||||
{
|
||||
createActionForSilentRouteProviders = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
routeProviders.Add(attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var routeProvider in routeProviders)
|
||||
{
|
||||
// If we see an attribute like
|
||||
// [Route(...)]
|
||||
//
|
||||
// Then we want to group any attributes like [HttpGet] with it.
|
||||
//
|
||||
// Basically...
|
||||
//
|
||||
// [HttpGet]
|
||||
// [HttpPost("Products")]
|
||||
// public void Foo() { }
|
||||
//
|
||||
// Is two actions. And...
|
||||
//
|
||||
// [HttpGet]
|
||||
// [Route("Products")]
|
||||
// public void Foo() { }
|
||||
//
|
||||
// Is one action.
|
||||
if (!(routeProvider is IActionHttpMethodProvider))
|
||||
{
|
||||
createActionForSilentRouteProviders = false;
|
||||
}
|
||||
}
|
||||
|
||||
var actionModels = new List<ActionModel>();
|
||||
if (routeProviders.Count == 0 && !createActionForSilentRouteProviders)
|
||||
{
|
||||
actionModels.Add(CreateActionModel(methodInfo, attributes));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Each of these routeProviders are the ones that actually have routing information on them
|
||||
// something like [HttpGet] won't show up here, but [HttpGet("Products")] will.
|
||||
foreach (var routeProvider in routeProviders)
|
||||
{
|
||||
var filteredAttributes = new List<object>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (attribute == routeProvider)
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
else if (routeProviders.Contains(attribute))
|
||||
{
|
||||
// Exclude other route template providers
|
||||
}
|
||||
else if (
|
||||
routeProvider is IActionHttpMethodProvider &&
|
||||
attribute is IActionHttpMethodProvider)
|
||||
{
|
||||
// Exclude other http method providers if this route is an
|
||||
// http method provider.
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
actionModels.Add(CreateActionModel(methodInfo, filteredAttributes));
|
||||
}
|
||||
|
||||
if (createActionForSilentRouteProviders)
|
||||
{
|
||||
var filteredAttributes = new List<object>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (!routeProviders.Contains(attribute))
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
actionModels.Add(CreateActionModel(methodInfo, filteredAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
return actionModels;
|
||||
return actionModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -525,83 +411,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return methodInfo.IsPublic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="ActionModel"/> for the given <see cref="MethodInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">The <see cref="MethodInfo"/>.</param>
|
||||
/// <param name="attributes">The set of attributes to use as metadata.</param>
|
||||
/// <returns>An <see cref="ActionModel"/> for the given <see cref="MethodInfo"/>.</returns>
|
||||
/// <remarks>
|
||||
/// An action-method in code may expand into multiple <see cref="ActionModel"/> instances depending on how
|
||||
/// the action is routed. In the case of multiple routing attributes, this method will invoked be once for
|
||||
/// each action that can be created.
|
||||
///
|
||||
/// If overriding this method, use the provided <paramref name="attributes"/> list to find metadata related to
|
||||
/// the action being created.
|
||||
/// </remarks>
|
||||
protected virtual ActionModel CreateActionModel(
|
||||
MethodInfo methodInfo,
|
||||
IReadOnlyList<object> attributes)
|
||||
{
|
||||
if (methodInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(methodInfo));
|
||||
}
|
||||
|
||||
if (attributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
var actionModel = new ActionModel(methodInfo, attributes);
|
||||
|
||||
AddRange(actionModel.ActionConstraints, attributes.OfType<IActionConstraintMetadata>());
|
||||
AddRange(actionModel.Filters, attributes.OfType<IFilterMetadata>());
|
||||
|
||||
var actionName = attributes.OfType<ActionNameAttribute>().FirstOrDefault();
|
||||
if (actionName?.Name != null)
|
||||
{
|
||||
actionModel.ActionName = actionName.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
actionModel.ActionName = methodInfo.Name;
|
||||
}
|
||||
|
||||
var apiVisibility = attributes.OfType<IApiDescriptionVisibilityProvider>().FirstOrDefault();
|
||||
if (apiVisibility != null)
|
||||
{
|
||||
actionModel.ApiExplorer.IsVisible = !apiVisibility.IgnoreApi;
|
||||
}
|
||||
|
||||
var apiGroupName = attributes.OfType<IApiDescriptionGroupNameProvider>().FirstOrDefault();
|
||||
if (apiGroupName != null)
|
||||
{
|
||||
actionModel.ApiExplorer.GroupName = apiGroupName.GroupName;
|
||||
}
|
||||
|
||||
var httpMethods = attributes.OfType<IActionHttpMethodProvider>();
|
||||
AddRange(actionModel.HttpMethods,
|
||||
httpMethods
|
||||
.Where(a => a.HttpMethods != null)
|
||||
.SelectMany(a => a.HttpMethods)
|
||||
.Distinct());
|
||||
|
||||
AddRange(actionModel.RouteConstraints, attributes.OfType<IRouteConstraintProvider>());
|
||||
|
||||
var routeTemplateProvider =
|
||||
attributes
|
||||
.OfType<IRouteTemplateProvider>()
|
||||
.SingleOrDefault(a => !IsSilentRouteAttribute(a));
|
||||
|
||||
if (routeTemplateProvider != null)
|
||||
{
|
||||
actionModel.AttributeRouteModel = new AttributeRouteModel(routeTemplateProvider);
|
||||
}
|
||||
|
||||
return actionModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ParameterModel"/> for the given <see cref="ParameterInfo"/>.
|
||||
/// </summary>
|
||||
|
|
@ -627,6 +436,160 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return parameterModel;
|
||||
}
|
||||
|
||||
private IList<SelectorModel> CreateSelectors(IList<object> attributes)
|
||||
{
|
||||
// Route attributes create multiple selector models, we want to split the set of
|
||||
// attributes based on these so each selector only has the attributes that affect it.
|
||||
//
|
||||
// The set of route attributes are split into those that 'define' a route versus those that are
|
||||
// 'silent'.
|
||||
//
|
||||
// We need to define a selector for each attribute that 'defines' a route, and a single selector
|
||||
// for all of the ones that don't (if any exist).
|
||||
//
|
||||
// If the attribute that 'defines' a route is NOT an IActionHttpMethodProvider, then we'll include with
|
||||
// it, any IActionHttpMethodProvider that are 'silent' IRouteTemplateProviders. In this case the 'extra'
|
||||
// action for silent route providers isn't needed.
|
||||
//
|
||||
// Ex:
|
||||
// [HttpGet]
|
||||
// [AcceptVerbs("POST", "PUT")]
|
||||
// [HttpPost("Api/Things")]
|
||||
// public void DoThing()
|
||||
//
|
||||
// This will generate 2 selectors:
|
||||
// 1. [HttpPost("Api/Things")]
|
||||
// 2. [HttpGet], [AcceptVerbs("POST", "PUT")]
|
||||
//
|
||||
// Note that having a route attribute that doesn't define a route template _might_ be an error. We
|
||||
// don't have enough context to really know at this point so we just pass it on.
|
||||
var routeProviders = new List<IRouteTemplateProvider>();
|
||||
|
||||
var createSelectorForSilentRouteProviders = false;
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
var routeTemplateProvider = attribute as IRouteTemplateProvider;
|
||||
if (routeTemplateProvider != null)
|
||||
{
|
||||
if (IsSilentRouteAttribute(routeTemplateProvider))
|
||||
{
|
||||
createSelectorForSilentRouteProviders = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
routeProviders.Add(routeTemplateProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var routeProvider in routeProviders)
|
||||
{
|
||||
// If we see an attribute like
|
||||
// [Route(...)]
|
||||
//
|
||||
// Then we want to group any attributes like [HttpGet] with it.
|
||||
//
|
||||
// Basically...
|
||||
//
|
||||
// [HttpGet]
|
||||
// [HttpPost("Products")]
|
||||
// public void Foo() { }
|
||||
//
|
||||
// Is two selectors. And...
|
||||
//
|
||||
// [HttpGet]
|
||||
// [Route("Products")]
|
||||
// public void Foo() { }
|
||||
//
|
||||
// Is one selector.
|
||||
if (!(routeProvider is IActionHttpMethodProvider))
|
||||
{
|
||||
createSelectorForSilentRouteProviders = false;
|
||||
}
|
||||
}
|
||||
|
||||
var selectorModels = new List<SelectorModel>();
|
||||
if (routeProviders.Count == 0 && !createSelectorForSilentRouteProviders)
|
||||
{
|
||||
// Simple case, all attributes apply
|
||||
selectorModels.Add(CreateSelectorModel(route: null, attributes: attributes));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Each of these routeProviders are the ones that actually have routing information on them
|
||||
// something like [HttpGet] won't show up here, but [HttpGet("Products")] will.
|
||||
foreach (var routeProvider in routeProviders)
|
||||
{
|
||||
var filteredAttributes = new List<object>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (attribute == routeProvider)
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
else if (routeProviders.Contains(attribute))
|
||||
{
|
||||
// Exclude other route template providers
|
||||
}
|
||||
else if (
|
||||
routeProvider is IActionHttpMethodProvider &&
|
||||
attribute is IActionHttpMethodProvider)
|
||||
{
|
||||
// Exclude other http method providers if this route is an
|
||||
// http method provider.
|
||||
}
|
||||
else
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
selectorModels.Add(CreateSelectorModel(routeProvider, filteredAttributes));
|
||||
}
|
||||
|
||||
if (createSelectorForSilentRouteProviders)
|
||||
{
|
||||
var filteredAttributes = new List<object>();
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (!routeProviders.Contains(attribute))
|
||||
{
|
||||
filteredAttributes.Add(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
selectorModels.Add(CreateSelectorModel(route: null, attributes: filteredAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
return selectorModels;
|
||||
}
|
||||
|
||||
private static SelectorModel CreateSelectorModel(IRouteTemplateProvider route, IList<object> attributes)
|
||||
{
|
||||
var selectorModel = new SelectorModel();
|
||||
if (route != null)
|
||||
{
|
||||
selectorModel.AttributeRouteModel = new AttributeRouteModel(route);
|
||||
}
|
||||
|
||||
AddRange(selectorModel.ActionConstraints, attributes.OfType<IActionConstraintMetadata>());
|
||||
|
||||
// Simple case, all HTTP method attributes apply
|
||||
var httpMethods = attributes
|
||||
.OfType<IActionHttpMethodProvider>()
|
||||
.SelectMany(a => a.HttpMethods)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
|
||||
if (httpMethods.Length > 0)
|
||||
{
|
||||
selectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(httpMethods));
|
||||
}
|
||||
|
||||
return selectorModel;
|
||||
}
|
||||
|
||||
private bool IsIDisposableMethod(MethodInfo methodInfo, TypeInfo typeInfo)
|
||||
{
|
||||
return
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
|
||||
|
|
@ -64,34 +64,55 @@ namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
|
|||
|
||||
private bool IsActionAttributeRouted(ActionModel action)
|
||||
{
|
||||
if (action.Controller.AttributeRoutes.Count > 0)
|
||||
foreach (var controllerSelectorModel in action.Controller.Selectors)
|
||||
{
|
||||
return true;
|
||||
if (controllerSelectorModel.AttributeRouteModel?.Template != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return action.AttributeRouteModel?.Template != null;
|
||||
foreach (var actionSelectorModel in action.Selectors)
|
||||
{
|
||||
if (actionSelectorModel.AttributeRouteModel?.Template != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetHttpMethodFromConvention(ActionModel action)
|
||||
{
|
||||
if (action.HttpMethods.Count > 0)
|
||||
foreach (var selector in action.Selectors)
|
||||
{
|
||||
// If the HttpMethods are set from attributes, don't override it with the convention
|
||||
return;
|
||||
if (selector.ActionConstraints.OfType<HttpMethodActionConstraint>().Count() > 0)
|
||||
{
|
||||
// If the HttpMethods are set from attributes, don't override it with the convention
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The Method name is used to infer verb constraints. Changing the action name has not impact.
|
||||
// The Method name is used to infer verb constraints. Changing the action name has no impact.
|
||||
foreach (var verb in SupportedHttpMethodConventions)
|
||||
{
|
||||
if (action.ActionMethod.Name.StartsWith(verb, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
action.HttpMethods.Add(verb);
|
||||
foreach (var selector in action.Selectors)
|
||||
{
|
||||
selector.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { verb }));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If no convention matches, then assume POST
|
||||
action.HttpMethods.Add("POST");
|
||||
foreach (var actionSelectorModel in action.Selectors)
|
||||
{
|
||||
actionSelectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { "POST" }));
|
||||
}
|
||||
}
|
||||
|
||||
private class UnnamedActionRouteConstraint : IRouteConstraintProvider
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim
|
|||
|
||||
if (IsConventionApplicable(action.Controller))
|
||||
{
|
||||
action.ActionConstraints.Add(new OverloadActionConstraint());
|
||||
foreach (var actionSelectorModel in action.Selectors)
|
||||
{
|
||||
actionSelectorModel.ActionConstraints.Add(new OverloadActionConstraint());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
public void CopyConstructor_DoesDeepCopyOfOtherModels()
|
||||
{
|
||||
// Arrange
|
||||
var action = new ActionModel(typeof(TestController).GetMethod("Edit"),
|
||||
var action = new ActionModel(typeof(TestController).GetMethod(nameof(TestController.Edit)),
|
||||
new List<object>());
|
||||
|
||||
var parameter = new ParameterModel(action.ActionMethod.GetParameters()[0],
|
||||
|
|
@ -26,7 +26,10 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
action.Parameters.Add(parameter);
|
||||
|
||||
var route = new AttributeRouteModel(new HttpGetAttribute("api/Products"));
|
||||
action.AttributeRouteModel = route;
|
||||
action.Selectors.Add(new SelectorModel()
|
||||
{
|
||||
AttributeRouteModel = route
|
||||
});
|
||||
|
||||
var apiExplorer = action.ApiExplorer;
|
||||
apiExplorer.IsVisible = false;
|
||||
|
|
@ -38,7 +41,10 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
// Assert
|
||||
Assert.NotSame(action, action2.Parameters[0]);
|
||||
Assert.NotSame(apiExplorer, action2.ApiExplorer);
|
||||
Assert.NotSame(route, action2.AttributeRouteModel);
|
||||
Assert.NotSame(action.Selectors, action2.Selectors);
|
||||
Assert.NotNull(action2.Selectors);
|
||||
Assert.Single(action2.Selectors);
|
||||
Assert.NotSame(route, action2.Selectors[0].AttributeRouteModel);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -53,13 +59,14 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
new MyFilterAttribute(),
|
||||
});
|
||||
|
||||
action.ActionConstraints.Add(new HttpMethodActionConstraint(new string[] { "GET" }));
|
||||
var selectorModel = new SelectorModel();
|
||||
selectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new string[] { "GET" }));
|
||||
action.Selectors.Add(selectorModel);
|
||||
action.ActionName = "Edit";
|
||||
|
||||
action.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(),
|
||||
new List<object>());
|
||||
action.Filters.Add(new MyFilterAttribute());
|
||||
action.HttpMethods.Add("GET");
|
||||
action.RouteConstraints.Add(new MyRouteConstraintAttribute());
|
||||
action.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));
|
||||
|
||||
|
|
@ -71,7 +78,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
{
|
||||
// Reflection is used to make sure the test fails when a new property is added.
|
||||
if (property.Name.Equals("ApiExplorer") ||
|
||||
property.Name.Equals("AttributeRouteModel") ||
|
||||
property.Name.Equals("Selectors") ||
|
||||
property.Name.Equals("Parameters"))
|
||||
{
|
||||
// This test excludes other ApplicationModel objects on purpose because we deep copy them.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
action.Controller = controller;
|
||||
|
||||
var route = new AttributeRouteModel(new HttpGetAttribute("api/Products"));
|
||||
controller.AttributeRoutes.Add(route);
|
||||
controller.Selectors.Add(new SelectorModel() { AttributeRouteModel = route });
|
||||
|
||||
var apiExplorer = controller.ApiExplorer;
|
||||
controller.ApiExplorer.GroupName = "group";
|
||||
|
|
@ -39,10 +39,12 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
// Assert
|
||||
Assert.NotSame(action, controller2.Actions[0]);
|
||||
Assert.NotSame(route, controller2.AttributeRoutes[0]);
|
||||
Assert.NotNull(controller2.Selectors);
|
||||
Assert.Single(controller2.Selectors);
|
||||
Assert.NotSame(route, controller2.Selectors[0].AttributeRouteModel);
|
||||
Assert.NotSame(apiExplorer, controller2.ApiExplorer);
|
||||
|
||||
Assert.NotSame(controller.ActionConstraints, controller2.ActionConstraints);
|
||||
Assert.NotSame(controller.Selectors[0].ActionConstraints, controller2.Selectors[0].ActionConstraints);
|
||||
Assert.NotSame(controller.Actions, controller2.Actions);
|
||||
Assert.NotSame(controller.Attributes, controller2.Attributes);
|
||||
Assert.NotSame(controller.Filters, controller2.Filters);
|
||||
|
|
@ -61,7 +63,9 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
new MyFilterAttribute(),
|
||||
});
|
||||
|
||||
controller.ActionConstraints.Add(new HttpMethodActionConstraint(new string[] { "GET" }));
|
||||
var selectorModel = new SelectorModel();
|
||||
selectorModel.ActionConstraints.Add(new HttpMethodActionConstraint(new string[] { "GET" }));
|
||||
controller.Selectors.Add(selectorModel);
|
||||
controller.Application = new ApplicationModel();
|
||||
controller.ControllerName = "cool";
|
||||
controller.Filters.Add(new MyFilterAttribute());
|
||||
|
|
@ -77,7 +81,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
foreach (var property in typeof(ControllerModel).GetProperties())
|
||||
{
|
||||
if (property.Name.Equals("Actions") ||
|
||||
property.Name.Equals("AttributeRoutes") ||
|
||||
property.Name.Equals("Selectors") ||
|
||||
property.Name.Equals("ApiExplorer") ||
|
||||
property.Name.Equals("ControllerProperties"))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var controller = Assert.Single(context.Result.Controllers);
|
||||
var action = Assert.Single(controller.Actions);
|
||||
Assert.Equal("Authorize", action.ActionName);
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
|
||||
var attributeRoutes = action.Selectors.Where(sm => sm.AttributeRouteModel != null);
|
||||
Assert.Empty(attributeRoutes);
|
||||
var authorizeFilters = action.Filters.OfType<AuthorizeFilter>();
|
||||
Assert.Single(authorizeFilters);
|
||||
Assert.Equal(3, authorizeFilters.First().Policy.Requirements.Count);
|
||||
|
|
|
|||
|
|
@ -40,8 +40,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
controller.Application = applicationModel;
|
||||
applicationModel.Controllers.Add(controller);
|
||||
|
||||
var methodInfo = typeof(TestController).GetMethod("SomeAction");
|
||||
var methodInfo = typeof(TestController).GetMethod(nameof(TestController.SomeAction));
|
||||
var actionModel = new ActionModel(methodInfo, new List<object>() { });
|
||||
actionModel.Selectors.Add(new SelectorModel());
|
||||
actionModel.Controller = controller;
|
||||
controller.Actions.Add(actionModel);
|
||||
|
||||
|
|
@ -71,8 +72,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
controller.Application = applicationModel;
|
||||
applicationModel.Controllers.Add(controller);
|
||||
|
||||
var methodInfo = typeof(TestController).GetMethod("SomeAction");
|
||||
var methodInfo = typeof(TestController).GetMethod(nameof(TestController.SomeAction));
|
||||
var actionModel = new ActionModel(methodInfo, new List<object>() { });
|
||||
actionModel.Selectors.Add(new SelectorModel());
|
||||
actionModel.Controller = controller;
|
||||
controller.Actions.Add(actionModel);
|
||||
|
||||
|
|
@ -96,8 +98,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
controller.Properties["test"] = "controller";
|
||||
applicationModel.Controllers.Add(controller);
|
||||
|
||||
var methodInfo = typeof(TestController).GetMethod("SomeAction");
|
||||
var methodInfo = typeof(TestController).GetMethod(nameof(TestController.SomeAction));
|
||||
var actionModel = new ActionModel(methodInfo, new List<object>() { });
|
||||
actionModel.Selectors.Add(new SelectorModel());
|
||||
actionModel.Controller = controller;
|
||||
controller.Actions.Add(actionModel);
|
||||
|
||||
|
|
@ -121,8 +124,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
controller.Properties["test"] = "controller";
|
||||
applicationModel.Controllers.Add(controller);
|
||||
|
||||
var methodInfo = typeof(TestController).GetMethod("SomeAction");
|
||||
var methodInfo = typeof(TestController).GetMethod(nameof(TestController.SomeAction));
|
||||
var actionModel = new ActionModel(methodInfo, new List<object>() { });
|
||||
actionModel.Selectors.Add(new SelectorModel());
|
||||
actionModel.Controller = controller;
|
||||
actionModel.Properties["test"] = "action";
|
||||
controller.Actions.Add(actionModel);
|
||||
|
|
|
|||
|
|
@ -416,13 +416,13 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
var conventional = Assert.Single(model.Controllers,
|
||||
c => c.ControllerName == "ConventionallyRouted");
|
||||
Assert.Empty(conventional.AttributeRoutes);
|
||||
Assert.Empty(conventional.Selectors.Where(sm => sm.AttributeRouteModel != null));
|
||||
Assert.Single(conventional.Actions);
|
||||
|
||||
var attributeRouted = Assert.Single(model.Controllers,
|
||||
c => c.ControllerName == "AttributeRouted");
|
||||
Assert.Single(attributeRouted.Actions);
|
||||
Assert.Single(attributeRouted.AttributeRoutes);
|
||||
Assert.Single(attributeRouted.Selectors.Where(sm => sm.AttributeRouteModel != null));
|
||||
|
||||
var empty = Assert.Single(model.Controllers,
|
||||
c => c.ControllerName == "Empty");
|
||||
|
|
@ -449,10 +449,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
Assert.Equal(2, controller.Actions.Count);
|
||||
|
||||
var getPerson = Assert.Single(controller.Actions, a => a.ActionName == "GetPerson");
|
||||
Assert.Empty(getPerson.HttpMethods);
|
||||
Assert.Empty(getPerson.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
|
||||
var showPeople = Assert.Single(controller.Actions, a => a.ActionName == "ShowPeople");
|
||||
Assert.Empty(showPeople.HttpMethods);
|
||||
Assert.Empty(showPeople.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -486,11 +486,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
// Assert
|
||||
var controller = Assert.Single(model.Controllers);
|
||||
|
||||
var attributeRouteModel = Assert.Single(controller.AttributeRoutes);
|
||||
Assert.Equal("api/Token/[key]/[controller]", attributeRouteModel.Template);
|
||||
var selectorModel = Assert.Single(controller.Selectors.Where(sm => sm.AttributeRouteModel != null));
|
||||
Assert.Equal("api/Token/[key]/[controller]", selectorModel.AttributeRouteModel.Template);
|
||||
|
||||
var action = Assert.Single(controller.Actions);
|
||||
Assert.Equal("stub/[action]", action.AttributeRouteModel.Template);
|
||||
var actionSelectorModel = Assert.Single(action.Selectors.Where(sm => sm.AttributeRouteModel != null));
|
||||
Assert.Equal("stub/[action]", actionSelectorModel.AttributeRouteModel.Template);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -567,7 +568,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var descriptors = provider.GetDescriptors();
|
||||
|
||||
// Assert
|
||||
var actions = descriptors.Where(d => d.Name == "AcceptVerbs");
|
||||
var actions = descriptors.Where(d => d.Name == nameof(MultiRouteAttributesController.AcceptVerbs));
|
||||
Assert.Equal(2, actions.Count());
|
||||
|
||||
foreach (var action in actions)
|
||||
|
|
@ -1300,28 +1301,26 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var model = provider.BuildModel();
|
||||
|
||||
// Assert
|
||||
var actions = Assert.Single(model.Controllers).Actions;
|
||||
Assert.Equal(2, actions.Count());
|
||||
var controllerModel = Assert.Single(model.Controllers);
|
||||
var actionModel = Assert.Single(controllerModel.Actions);
|
||||
Assert.Equal(3, actionModel.Attributes.Count);
|
||||
Assert.Equal(2, actionModel.Attributes.OfType<RouteAndConstraintAttribute>().Count());
|
||||
Assert.Single(actionModel.Attributes.OfType<ConstraintAttribute>());
|
||||
Assert.Equal(2, actionModel.Selectors.Count);
|
||||
|
||||
var action = Assert.Single(actions, a => a.AttributeRouteModel.Template == "R1");
|
||||
var selectorModel = Assert.Single(
|
||||
actionModel.Selectors.Where(sm => sm.AttributeRouteModel?.Template == "R1"));
|
||||
|
||||
Assert.Equal(2, action.Attributes.Count);
|
||||
Assert.Single(action.Attributes, a => a is RouteAndConstraintAttribute);
|
||||
Assert.Single(action.Attributes, a => a is ConstraintAttribute);
|
||||
Assert.Equal(2, selectorModel.ActionConstraints.Count);
|
||||
Assert.Single(selectorModel.ActionConstraints.OfType<RouteAndConstraintAttribute>());
|
||||
Assert.Single(selectorModel.ActionConstraints.OfType<ConstraintAttribute>());
|
||||
|
||||
Assert.Equal(2, action.ActionConstraints.Count);
|
||||
Assert.Single(action.ActionConstraints, a => a is RouteAndConstraintAttribute);
|
||||
Assert.Single(action.ActionConstraints, a => a is ConstraintAttribute);
|
||||
selectorModel = Assert.Single(
|
||||
actionModel.Selectors.Where(sm => sm.AttributeRouteModel?.Template == "R2"));
|
||||
|
||||
action = Assert.Single(actions, a => a.AttributeRouteModel.Template == "R2");
|
||||
|
||||
Assert.Equal(2, action.Attributes.Count);
|
||||
Assert.Single(action.Attributes, a => a is RouteAndConstraintAttribute);
|
||||
Assert.Single(action.Attributes, a => a is ConstraintAttribute);
|
||||
|
||||
Assert.Equal(2, action.ActionConstraints.Count);
|
||||
Assert.Single(action.ActionConstraints, a => a is RouteAndConstraintAttribute);
|
||||
Assert.Single(action.ActionConstraints, a => a is ConstraintAttribute);
|
||||
Assert.Equal(2, selectorModel.ActionConstraints.Count);
|
||||
Assert.Single(selectorModel.ActionConstraints.OfType<RouteAndConstraintAttribute>());
|
||||
Assert.Single(selectorModel.ActionConstraints.OfType<ConstraintAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ using System.Reflection;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -112,13 +111,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var model = builder.CreateControllerModel(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, model.AttributeRoutes.Count);
|
||||
var attributeRoutes = GetAttributeRoutes(model.Selectors);
|
||||
Assert.Equal(2, attributeRoutes.Count);
|
||||
Assert.Equal(2, model.Attributes.Count);
|
||||
|
||||
var route = Assert.Single(model.AttributeRoutes, r => r.Template == "A");
|
||||
var route = Assert.Single(attributeRoutes, r => r.Template == "A");
|
||||
Assert.Contains(route.Attribute, model.Attributes);
|
||||
|
||||
route = Assert.Single(model.AttributeRoutes, r => r.Template == "B");
|
||||
route = Assert.Single(attributeRoutes, r => r.Template == "B");
|
||||
Assert.Contains(route.Attribute, model.Attributes);
|
||||
}
|
||||
|
||||
|
|
@ -133,13 +133,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var model = builder.CreateControllerModel(typeInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, model.AttributeRoutes.Count);
|
||||
var attributeRoutes = GetAttributeRoutes(model.Selectors);
|
||||
Assert.Equal(2, attributeRoutes.Count);
|
||||
Assert.Equal(2, model.Attributes.Count);
|
||||
|
||||
var route = Assert.Single(model.AttributeRoutes, r => r.Template == "C");
|
||||
var route = Assert.Single(attributeRoutes, r => r.Template == "C");
|
||||
Assert.Contains(route.Attribute, model.Attributes);
|
||||
|
||||
route = Assert.Single(model.AttributeRoutes, r => r.Template == "D");
|
||||
route = Assert.Single(attributeRoutes, r => r.Template == "D");
|
||||
Assert.Contains(route.Attribute, model.Attributes);
|
||||
}
|
||||
|
||||
|
|
@ -413,7 +414,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_ConventionallyRoutedAction_WithoutHttpConstraints()
|
||||
public void CreateActionModel_ConventionallyRoutedAction_WithoutHttpConstraints()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -421,18 +422,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(ConventionallyRoutedController.Edit);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.Equal("Edit", action.ActionName);
|
||||
Assert.Empty(action.HttpMethods);
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
Assert.NotNull(action);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
Assert.Empty(action.Attributes);
|
||||
Assert.Single(action.Selectors);
|
||||
Assert.Empty(action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Empty(GetAttributeRoutes(action.Selectors));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_ConventionallyRoutedAction_WithHttpConstraints()
|
||||
public void CreateActionModel_ConventionallyRoutedAction_WithHttpConstraints()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -440,20 +442,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(ConventionallyRoutedController.Update);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.Contains("PUT", action.HttpMethods);
|
||||
Assert.Contains("PATCH", action.HttpMethods);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
var methodConstraint = Assert.Single(
|
||||
action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Contains("PUT", methodConstraint.HttpMethods);
|
||||
Assert.Contains("PATCH", methodConstraint.HttpMethods);
|
||||
|
||||
Assert.Equal("Update", action.ActionName);
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
Assert.Empty(GetAttributeRoutes(action.Selectors));
|
||||
Assert.IsType<CustomHttpMethodsAttribute>(Assert.Single(action.Attributes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_ConventionallyRoutedActionWithHttpConstraints_AndInvalidRouteTemplateProvider()
|
||||
public void CreateActionModel_ConventionallyRoutedActionWithHttpConstraints_AndInvalidRouteTemplateProvider()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -461,21 +466,24 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(ConventionallyRoutedController.Delete);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.Contains("DELETE", action.HttpMethods);
|
||||
Assert.Contains("HEAD", action.HttpMethods);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
var methodConstraint = Assert.Single(
|
||||
action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Contains("DELETE", methodConstraint.HttpMethods);
|
||||
Assert.Contains("HEAD", methodConstraint.HttpMethods);
|
||||
|
||||
Assert.Equal("Delete", action.ActionName);
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
Assert.Empty(GetAttributeRoutes(action.Selectors));
|
||||
Assert.Single(action.Attributes.OfType<HttpDeleteAttribute>());
|
||||
Assert.Single(action.Attributes.OfType<HttpHeadAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_ConventionallyRoutedAction_WithMultipleHttpConstraints()
|
||||
public void CreateActionModel_ConventionallyRoutedAction_WithMultipleHttpConstraints()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -483,19 +491,22 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(ConventionallyRoutedController.Details);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.Contains("GET", action.HttpMethods);
|
||||
Assert.Contains("POST", action.HttpMethods);
|
||||
Assert.Contains("HEAD", action.HttpMethods);
|
||||
Assert.Equal("Details", action.ActionName);
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
var methodConstraint = Assert.Single(
|
||||
action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Contains("GET", methodConstraint.HttpMethods);
|
||||
Assert.Contains("POST", methodConstraint.HttpMethods);
|
||||
Assert.Contains("HEAD", methodConstraint.HttpMethods);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
Assert.Empty(GetAttributeRoutes(action.Selectors));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_ConventionallyRoutedAction_WithMultipleOverlappingHttpConstraints()
|
||||
public void CreateActionModel_ConventionallyRoutedAction_WithMultipleOverlappingHttpConstraints()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -503,19 +514,22 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(ConventionallyRoutedController.List);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.Contains("GET", action.HttpMethods);
|
||||
Assert.Contains("PUT", action.HttpMethods);
|
||||
Assert.Contains("POST", action.HttpMethods);
|
||||
Assert.Equal("List", action.ActionName);
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
var methodConstraint = Assert.Single(
|
||||
action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Contains("GET", methodConstraint.HttpMethods);
|
||||
Assert.Contains("PUT", methodConstraint.HttpMethods);
|
||||
Assert.Contains("POST", methodConstraint.HttpMethods);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
Assert.Empty(GetAttributeRoutes(action.Selectors));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_AttributeRouteOnAction()
|
||||
public void CreateActionModel_AttributeRouteOnAction()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -523,24 +537,27 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(NoRouteAttributeOnControllerController.Edit);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
var methodConstraint = Assert.Single(
|
||||
action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
|
||||
Assert.Equal("Edit", action.ActionName);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
|
||||
var httpMethod = Assert.Single(action.HttpMethods);
|
||||
var httpMethod = Assert.Single(methodConstraint.HttpMethods);
|
||||
Assert.Equal("HEAD", httpMethod);
|
||||
|
||||
Assert.NotNull(action.AttributeRouteModel);
|
||||
Assert.Equal("Change", action.AttributeRouteModel.Template);
|
||||
var attributeRoute = Assert.Single(GetAttributeRoutes(action.Selectors));
|
||||
Assert.Equal("Change", attributeRoute.Template);
|
||||
|
||||
Assert.IsType<HttpHeadAttribute>(Assert.Single(action.Attributes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_AttributeRouteOnAction_RouteAttribute()
|
||||
public void CreateActionModel_AttributeRouteOnAction_RouteAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -548,23 +565,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(NoRouteAttributeOnControllerController.Update);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
Assert.Empty(action.Selectors[0].ActionConstraints);
|
||||
|
||||
Assert.Equal("Update", action.ActionName);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
|
||||
Assert.Empty(action.HttpMethods);
|
||||
|
||||
Assert.NotNull(action.AttributeRouteModel);
|
||||
Assert.Equal("Update", action.AttributeRouteModel.Template);
|
||||
var attributeRoute = Assert.Single(GetAttributeRoutes(action.Selectors));
|
||||
Assert.Equal("Update", attributeRoute.Template);
|
||||
|
||||
Assert.IsType<RouteAttribute>(Assert.Single(action.Attributes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_AttributeRouteOnAction_AcceptVerbsAttributeWithTemplate()
|
||||
public void CreateActionModel_AttributeRouteOnAction_AcceptVerbsAttributeWithTemplate()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -572,23 +589,28 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(NoRouteAttributeOnControllerController.List);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
var methodConstraint = Assert.Single(
|
||||
action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
|
||||
Assert.Equal("List", action.ActionName);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
|
||||
Assert.Equal(new[] { "GET", "HEAD" }, action.HttpMethods.OrderBy(m => m, StringComparer.Ordinal));
|
||||
Assert.Equal(
|
||||
new[] { "GET", "HEAD" },
|
||||
methodConstraint.HttpMethods.OrderBy(m => m, StringComparer.Ordinal));
|
||||
|
||||
Assert.NotNull(action.AttributeRouteModel);
|
||||
Assert.Equal("ListAll", action.AttributeRouteModel.Template);
|
||||
var attributeRoute = Assert.Single(GetAttributeRoutes(action.Selectors));
|
||||
Assert.Equal("ListAll", attributeRoute.Template);
|
||||
|
||||
Assert.IsType<AcceptVerbsAttribute>(Assert.Single(action.Attributes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_AttributeRouteOnAction_CreatesOneActionInforPerRouteTemplate()
|
||||
public void CreateActionModel_AttributeRouteOnAction_CreatesOneActionInforPerRouteTemplate()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -596,30 +618,35 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(NoRouteAttributeOnControllerController.Index);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, actions.Count());
|
||||
Assert.NotNull(action);
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
Assert.NotNull(action.Attributes);
|
||||
Assert.Equal(2, action.Attributes.Count);
|
||||
Assert.Single(action.Attributes.OfType<HttpGetAttribute>());
|
||||
Assert.Single(action.Attributes.OfType<HttpPostAttribute>());
|
||||
Assert.Equal(2, action.Selectors.Count);
|
||||
|
||||
foreach (var action in actions)
|
||||
foreach (var actionSelectorModel in action.Selectors)
|
||||
{
|
||||
Assert.Equal("Index", action.ActionName);
|
||||
Assert.NotNull(action.AttributeRouteModel);
|
||||
Assert.NotNull(actionSelectorModel.AttributeRouteModel);
|
||||
}
|
||||
|
||||
var list = Assert.Single(actions, ai => ai.AttributeRouteModel.Template.Equals("List"));
|
||||
var listMethod = Assert.Single(list.HttpMethods);
|
||||
var selectorModel = Assert.Single(action.Selectors, ai => ai.AttributeRouteModel?.Template == "List");
|
||||
var methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
var listMethod = Assert.Single(methodConstraint.HttpMethods);
|
||||
Assert.Equal("POST", listMethod);
|
||||
Assert.IsType<HttpPostAttribute>(Assert.Single(list.Attributes));
|
||||
|
||||
var all = Assert.Single(actions, ai => ai.AttributeRouteModel.Template.Equals("All"));
|
||||
var allMethod = Assert.Single(all.HttpMethods);
|
||||
var all = Assert.Single(action.Selectors, ai => ai.AttributeRouteModel?.Template == "All");
|
||||
methodConstraint = Assert.Single(all.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
var allMethod = Assert.Single(methodConstraint.HttpMethods);
|
||||
Assert.Equal("GET", allMethod);
|
||||
Assert.IsType<HttpGetAttribute>(Assert.Single(all.Attributes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_NoRouteOnController_AllowsConventionallyRoutedActions_OnTheSameController()
|
||||
public void CreateActionModel_NoRouteOnController_AllowsConventionallyRoutedActions_OnTheSameController()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -627,77 +654,74 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(NoRouteAttributeOnControllerController.Remove);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
|
||||
Assert.Equal("Remove", action.ActionName);
|
||||
|
||||
Assert.Empty(action.HttpMethods);
|
||||
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
Assert.NotNull(action);
|
||||
|
||||
Assert.Equal(actionName, action.ActionName);
|
||||
Assert.Empty(action.Attributes);
|
||||
Assert.Single(action.Selectors);
|
||||
Assert.Empty(action.Selectors[0].ActionConstraints);
|
||||
Assert.Null(action.Selectors[0].AttributeRouteModel);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(SingleRouteAttributeController))]
|
||||
[InlineData(typeof(MultipleRouteAttributeController))]
|
||||
public void BuildActionModels_RouteAttributeOnController_CreatesAttributeRoute_ForNonAttributedActions(Type controller)
|
||||
public void CreateActionModel_RouteAttributeOnController_CreatesAttributeRoute_ForNonAttributedActions(Type controller)
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
var typeInfo = controller.GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod("Delete"));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod("Delete"));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.NotNull(action);
|
||||
|
||||
Assert.Equal("Delete", action.ActionName);
|
||||
|
||||
Assert.Empty(action.HttpMethods);
|
||||
|
||||
Assert.Null(action.AttributeRouteModel);
|
||||
|
||||
Assert.Single(action.Selectors);
|
||||
Assert.Empty(action.Selectors[0].ActionConstraints);
|
||||
Assert.Empty(GetAttributeRoutes(action.Selectors));
|
||||
Assert.Empty(action.Attributes);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(typeof(SingleRouteAttributeController))]
|
||||
[InlineData(typeof(MultipleRouteAttributeController))]
|
||||
public void BuildActionModels_RouteOnController_CreatesOneActionInforPerRouteTemplateOnAction(Type controller)
|
||||
public void CreateActionModel_RouteOnController_CreatesOneActionInforPerRouteTemplateOnAction(Type controller)
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
var typeInfo = controller.GetTypeInfo();
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod("Index"));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod("Index"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, actions.Count());
|
||||
Assert.NotNull(action.Attributes);
|
||||
Assert.Equal(2, action.Attributes.Count);
|
||||
Assert.Equal(2, action.Selectors.Count);
|
||||
Assert.Equal("Index", action.ActionName);
|
||||
|
||||
foreach (var action in actions)
|
||||
foreach (var selectorModel in action.Selectors)
|
||||
{
|
||||
Assert.Equal("Index", action.ActionName);
|
||||
|
||||
var httpMethod = Assert.Single(action.HttpMethods);
|
||||
var methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
var httpMethod = Assert.Single(methodConstraint.HttpMethods);
|
||||
Assert.Equal("GET", httpMethod);
|
||||
|
||||
Assert.NotNull(action.AttributeRouteModel.Template);
|
||||
|
||||
Assert.IsType<HttpGetAttribute>(Assert.Single(action.Attributes));
|
||||
Assert.NotNull(selectorModel.AttributeRouteModel.Template);
|
||||
}
|
||||
|
||||
Assert.Single(actions, ai => ai.AttributeRouteModel.Template.Equals("List"));
|
||||
Assert.Single(actions, ai => ai.AttributeRouteModel.Template.Equals("All"));
|
||||
Assert.Single(action.Selectors, ai => ai.AttributeRouteModel.Template.Equals("List"));
|
||||
Assert.Single(action.Selectors, ai => ai.AttributeRouteModel.Template.Equals("All"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_MixedHttpVerbsAndRoutes_EmptyVerbWithRoute()
|
||||
public void CreateActionModel_MixedHttpVerbsAndRoutes_EmptyVerbWithRoute()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -705,16 +729,20 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.VerbAndRoute);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
var action = Assert.Single(actions);
|
||||
Assert.Equal<string>(new string[] { "GET" }, action.HttpMethods);
|
||||
Assert.Equal("Products", action.AttributeRouteModel.Template);
|
||||
Assert.NotNull(action);
|
||||
Assert.Single(action.Selectors);
|
||||
var methodConstraint = Assert.Single(
|
||||
action.Selectors[0].ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal<string>(new string[] { "GET" }, methodConstraint.HttpMethods);
|
||||
var attributeRoute = Assert.Single(GetAttributeRoutes(action.Selectors));
|
||||
Assert.Equal("Products", attributeRoute.Template);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_MixedHttpVerbsAndRoutes_MultipleEmptyVerbsWithMultipleRoutes()
|
||||
public void CreateActionModel_MixedHttpVerbsAndRoutes_MultipleEmptyVerbsWithMultipleRoutes()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -722,21 +750,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.MultipleVerbsAndRoutes);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var actions = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, actions.Count());
|
||||
Assert.Equal(2, actions.Selectors.Count);
|
||||
|
||||
// OrderBy is used because the order of the results may very depending on the platform / client.
|
||||
var action = Assert.Single(actions, a => a.AttributeRouteModel.Template == "Products");
|
||||
Assert.Equal(new[] { "GET", "POST" }, action.HttpMethods.OrderBy(key => key, StringComparer.Ordinal));
|
||||
var selectorModel = Assert.Single(actions.Selectors, a => a.AttributeRouteModel.Template == "Products");
|
||||
var methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal(new[] { "GET", "POST" }, methodConstraint.HttpMethods.OrderBy(key => key, StringComparer.Ordinal));
|
||||
|
||||
action = Assert.Single(actions, a => a.AttributeRouteModel.Template == "v2/Products");
|
||||
Assert.Equal(new[] { "GET", "POST" }, action.HttpMethods.OrderBy(key => key, StringComparer.Ordinal));
|
||||
selectorModel = Assert.Single(actions.Selectors, a => a.AttributeRouteModel.Template == "v2/Products");
|
||||
methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal(new[] { "GET", "POST" }, methodConstraint.HttpMethods.OrderBy(key => key, StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbsWithMultipleRoutes()
|
||||
public void CreateActionModel_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbsWithMultipleRoutes()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -744,23 +774,26 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.MultipleVerbsWithAnyWithoutTemplateAndRoutes);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, actions.Count());
|
||||
Assert.Equal(3, action.Selectors.Count);
|
||||
|
||||
var action = Assert.Single(actions, a => a.AttributeRouteModel.Template == "Products");
|
||||
Assert.Equal<string>(new string[] { "GET" }, action.HttpMethods);
|
||||
var selectorModel = Assert.Single(action.Selectors, s => s.AttributeRouteModel.Template == "Products");
|
||||
var methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal<string>(new string[] { "GET" }, methodConstraint.HttpMethods);
|
||||
|
||||
action = Assert.Single(actions, a => a.AttributeRouteModel.Template == "v2/Products");
|
||||
Assert.Equal<string>(new string[] { "GET" }, action.HttpMethods);
|
||||
selectorModel = Assert.Single(action.Selectors, s => s.AttributeRouteModel.Template == "v2/Products");
|
||||
methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal<string>(new string[] { "GET" }, methodConstraint.HttpMethods);
|
||||
|
||||
action = Assert.Single(actions, a => a.AttributeRouteModel.Template == "Products/Buy");
|
||||
Assert.Equal<string>(new string[] { "POST" }, action.HttpMethods);
|
||||
selectorModel = Assert.Single(action.Selectors, s => s.AttributeRouteModel.Template == "Products/Buy");
|
||||
methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal<string>(new string[] { "POST" }, methodConstraint.HttpMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs()
|
||||
public void CreateActionModel_MixedHttpVerbsAndRoutes_MultipleEmptyAndNonEmptyVerbs()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -768,20 +801,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(MixedHttpVerbsAndRouteAttributeController.Invalid);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, actions.Count());
|
||||
Assert.NotNull(action);
|
||||
Assert.Equal(2, action.Selectors.Count);
|
||||
|
||||
var action = Assert.Single(actions, a => a.AttributeRouteModel?.Template == "Products");
|
||||
Assert.Equal<string>(new string[] { "POST" }, action.HttpMethods);
|
||||
var selectorModel = Assert.Single(action.Selectors, s => s.AttributeRouteModel?.Template == "Products");
|
||||
var methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal<string>(new string[] { "POST" }, methodConstraint.HttpMethods);
|
||||
|
||||
action = Assert.Single(actions, a => a.AttributeRouteModel?.Template == null);
|
||||
Assert.Equal<string>(new string[] { "GET" }, action.HttpMethods);
|
||||
selectorModel = Assert.Single(action.Selectors, s => s.AttributeRouteModel?.Template == null);
|
||||
methodConstraint = Assert.Single(selectorModel.ActionConstraints.OfType<HttpMethodActionConstraint>());
|
||||
Assert.Equal<string>(new string[] { "GET" }, methodConstraint.HttpMethods);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_InheritedAttributeRoutes()
|
||||
public void CreateActionModel_InheritedAttributeRoutes()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -789,22 +825,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(DerivedClassInheritsAttributeRoutesController.Edit);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var actions = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, actions.Count());
|
||||
Assert.Equal(2, actions.Attributes.Count);
|
||||
Assert.Equal(2, actions.Selectors.Count);
|
||||
|
||||
var action = Assert.Single(actions, a => a.AttributeRouteModel?.Template == "A");
|
||||
Assert.Equal(1, action.Attributes.Count);
|
||||
Assert.Contains(action.AttributeRouteModel.Attribute, action.Attributes);
|
||||
var selectorModel = Assert.Single(actions.Selectors, a => a.AttributeRouteModel?.Template == "A");
|
||||
Assert.Contains(selectorModel.AttributeRouteModel.Attribute, actions.Attributes);
|
||||
|
||||
action = Assert.Single(actions, a => a.AttributeRouteModel?.Template == "B");
|
||||
Assert.Equal(1, action.Attributes.Count);
|
||||
Assert.Contains(action.AttributeRouteModel.Attribute, action.Attributes);
|
||||
selectorModel = Assert.Single(actions.Selectors, a => a.AttributeRouteModel?.Template == "B");
|
||||
Assert.Contains(selectorModel.AttributeRouteModel.Attribute, actions.Attributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildActionModels_InheritedAttributeRoutesOverridden()
|
||||
public void CreateActionModel_InheritedAttributeRoutesOverridden()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new TestApplicationModelProvider();
|
||||
|
|
@ -812,18 +847,25 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
var actionName = nameof(DerivedClassOverridesAttributeRoutesController.Edit);
|
||||
|
||||
// Act
|
||||
var actions = builder.BuildActionModels(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
var action = builder.CreateActionModel(typeInfo, typeInfo.AsType().GetMethod(actionName));
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, actions.Count());
|
||||
Assert.Equal(4, action.Attributes.Count);
|
||||
Assert.Equal(2, action.Selectors.Count);
|
||||
|
||||
var action = Assert.Single(actions, a => a.AttributeRouteModel?.Template == "C");
|
||||
Assert.Equal(1, action.Attributes.Count);
|
||||
Assert.Contains(action.AttributeRouteModel.Attribute, action.Attributes);
|
||||
var selectorModel = Assert.Single(action.Selectors, a => a.AttributeRouteModel?.Template == "C");
|
||||
Assert.Contains(selectorModel.AttributeRouteModel.Attribute, action.Attributes);
|
||||
|
||||
action = Assert.Single(actions, a => a.AttributeRouteModel?.Template == "D");
|
||||
Assert.Equal(1, action.Attributes.Count);
|
||||
Assert.Contains(action.AttributeRouteModel.Attribute, action.Attributes);
|
||||
selectorModel = Assert.Single(action.Selectors, a => a.AttributeRouteModel?.Template == "D");
|
||||
Assert.Contains(selectorModel.AttributeRouteModel.Attribute, action.Attributes);
|
||||
}
|
||||
|
||||
private IList<AttributeRouteModel> GetAttributeRoutes(IList<SelectorModel> selectors)
|
||||
{
|
||||
return selectors
|
||||
.Where(sm => sm.AttributeRouteModel != null)
|
||||
.Select(sm => sm.AttributeRouteModel)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private class BaseClassWithAttributeRoutesController
|
||||
|
|
@ -1223,31 +1265,21 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
|
||||
public MvcOptions Options { get; }
|
||||
|
||||
public new IEnumerable<ControllerModel> BuildControllerModels(TypeInfo typeInfo)
|
||||
{
|
||||
return base.BuildControllerModels(typeInfo);
|
||||
}
|
||||
|
||||
public new ControllerModel CreateControllerModel(TypeInfo typeInfo)
|
||||
{
|
||||
return base.CreateControllerModel(typeInfo);
|
||||
}
|
||||
|
||||
public new ActionModel CreateActionModel(TypeInfo typeInfo, MethodInfo methodInfo)
|
||||
{
|
||||
return base.CreateActionModel(typeInfo, methodInfo);
|
||||
}
|
||||
|
||||
public new PropertyModel CreatePropertyModel(PropertyInfo propertyInfo)
|
||||
{
|
||||
return base.CreatePropertyModel(propertyInfo);
|
||||
}
|
||||
|
||||
public new IEnumerable<ActionModel> BuildActionModels(TypeInfo typeInfo, MethodInfo methodInfo)
|
||||
{
|
||||
return base.BuildActionModels(typeInfo, methodInfo);
|
||||
}
|
||||
|
||||
public new ActionModel CreateActionModel(MethodInfo methodInfo, IReadOnlyList<object> attributes)
|
||||
{
|
||||
return base.CreateActionModel(methodInfo, attributes);
|
||||
}
|
||||
|
||||
public new bool IsAction(TypeInfo typeInfo, MethodInfo methodInfo)
|
||||
{
|
||||
return base.IsAction(typeInfo, methodInfo);
|
||||
|
|
|
|||
Loading…
Reference in New Issue