zoukankan      html  css  js  c++  java
  • asp.net core mvc 3.1 源码分析(五)

    创建完ApplicationModel后,调用ControllerActionDescriptorBuilder类的Build方法创建对应的ControllerActionDescriptor

    internal static class ControllerActionDescriptorBuilder
        {
            public static IList<ControllerActionDescriptor> Build(ApplicationModel application)
            {
                return ApplicationModelFactory.Flatten(application, CreateActionDescriptor);
            }
    
            private static ControllerActionDescriptor CreateActionDescriptor(
                ApplicationModel application,
                ControllerModel controller,
                ActionModel action,
                SelectorModel selector)
            {
                var actionDescriptor = new ControllerActionDescriptor
                {
                    ActionName = action.ActionName,
                    MethodInfo = action.ActionMethod,
                };
    
                actionDescriptor.ControllerName = controller.ControllerName;
                actionDescriptor.ControllerTypeInfo = controller.ControllerType;
                AddControllerPropertyDescriptors(actionDescriptor, controller);
    
                AddActionConstraints(actionDescriptor, selector);
                AddEndpointMetadata(actionDescriptor, selector);
                AddAttributeRoute(actionDescriptor, selector);
                AddParameterDescriptors(actionDescriptor, action);
                AddActionFilters(actionDescriptor, action.Filters, controller.Filters, application.Filters);
                AddApiExplorerInfo(actionDescriptor, application, controller, action);
                AddRouteValues(actionDescriptor, controller, action);
                AddProperties(actionDescriptor, action, controller, application);
    
                return actionDescriptor;
            }
    
            private static void AddControllerPropertyDescriptors(ActionDescriptor actionDescriptor, ControllerModel controller)
            {
                actionDescriptor.BoundProperties = controller.ControllerProperties
                    .Where(p => p.BindingInfo != null)
                    .Select(CreateParameterDescriptor)
                    .ToList();
            }
    
            private static void AddParameterDescriptors(ActionDescriptor actionDescriptor, ActionModel action)
            {
                var parameterDescriptors = new List<ParameterDescriptor>();
                foreach (var parameter in action.Parameters)
                {
                    var parameterDescriptor = CreateParameterDescriptor(parameter);
                    parameterDescriptors.Add(parameterDescriptor);
                }
    
                actionDescriptor.Parameters = parameterDescriptors;
            }
            
            private static ParameterDescriptor CreateParameterDescriptor(ParameterModel parameterModel)
            {
                var parameterDescriptor = new ControllerParameterDescriptor()
                {
                    Name = parameterModel.ParameterName,
                    ParameterType = parameterModel.ParameterInfo.ParameterType,
                    BindingInfo = parameterModel.BindingInfo,
                    ParameterInfo = parameterModel.ParameterInfo,
                };
    
                return parameterDescriptor;
            }
    
            private static ParameterDescriptor CreateParameterDescriptor(PropertyModel propertyModel)
            {
                var parameterDescriptor = new ControllerBoundPropertyDescriptor()
                {
                    BindingInfo = propertyModel.BindingInfo,
                    Name = propertyModel.PropertyName,
                    ParameterType = propertyModel.PropertyInfo.PropertyType,
                    PropertyInfo = propertyModel.PropertyInfo,
                };
    
                return parameterDescriptor;
            }
    
            private static void AddApiExplorerInfo(
                ControllerActionDescriptor actionDescriptor,
                ApplicationModel application,
                ControllerModel controller,
                ActionModel action)
            {
                var isVisible =
                    action.ApiExplorer?.IsVisible ??
                    controller.ApiExplorer?.IsVisible ??
                    application.ApiExplorer?.IsVisible ??
                    false;
    
                var isVisibleSetOnActionOrController =
                    action.ApiExplorer?.IsVisible ??
                    controller.ApiExplorer?.IsVisible ??
                    false;
    
                // ApiExplorer isn't supported on conventional-routed actions, but we still allow you to configure
                // it at the application level when you have a mix of controller types. We'll just skip over enabling
                // ApiExplorer for conventional-routed controllers when this happens.
                var isVisibleSetOnApplication = application.ApiExplorer?.IsVisible ?? false;
    
                if (isVisibleSetOnActionOrController && !IsAttributeRouted(actionDescriptor))
                {
                    // ApiExplorer is only supported on attribute routed actions.
                    throw new InvalidOperationException(Resources.FormatApiExplorer_UnsupportedAction(
                        actionDescriptor.DisplayName));
                }
                else if (isVisibleSetOnApplication && !IsAttributeRouted(actionDescriptor))
                {
                    // This is the case where we're going to be lenient, just ignore it.
                }
                else if (isVisible)
                {
                    Debug.Assert(IsAttributeRouted(actionDescriptor));
    
                    var apiExplorerActionData = new ApiDescriptionActionData()
                    {
                        GroupName = action.ApiExplorer?.GroupName ?? controller.ApiExplorer?.GroupName,
                    };
    
                    actionDescriptor.SetProperty(apiExplorerActionData);
                }
            }
    
            private static void AddProperties(
                ControllerActionDescriptor actionDescriptor,
                ActionModel action,
                ControllerModel controller,
                ApplicationModel application)
            {
                foreach (var item in application.Properties)
                {
                    actionDescriptor.Properties[item.Key] = item.Value;
                }
    
                foreach (var item in controller.Properties)
                {
                    actionDescriptor.Properties[item.Key] = item.Value;
                }
    
                foreach (var item in action.Properties)
                {
                    actionDescriptor.Properties[item.Key] = item.Value;
                }
            }
    
            private static void AddActionFilters(
                ControllerActionDescriptor actionDescriptor,
                IEnumerable<IFilterMetadata> actionFilters,
                IEnumerable<IFilterMetadata> controllerFilters,
                IEnumerable<IFilterMetadata> globalFilters)
            {
                actionDescriptor.FilterDescriptors =
                    actionFilters.Select(f => new FilterDescriptor(f, FilterScope.Action))
                    .Concat(controllerFilters.Select(f => new FilterDescriptor(f, FilterScope.Controller)))
                    .Concat(globalFilters.Select(f => new FilterDescriptor(f, FilterScope.Global)))
                    .OrderBy(d => d, FilterDescriptorOrderComparer.Comparer)
                    .ToList();
            }
    
            private static void AddActionConstraints(ControllerActionDescriptor actionDescriptor, SelectorModel selectorModel)
            {
                if (selectorModel.ActionConstraints?.Count > 0)
                {
                    actionDescriptor.ActionConstraints = new List<IActionConstraintMetadata>(selectorModel.ActionConstraints);
                }
            }
    
            private static void AddEndpointMetadata(ControllerActionDescriptor actionDescriptor, SelectorModel selectorModel)
            {
                if (selectorModel.EndpointMetadata?.Count > 0)
                {
                    actionDescriptor.EndpointMetadata = new List<object>(selectorModel.EndpointMetadata);
                }
            }
    
            private static void AddAttributeRoute(ControllerActionDescriptor actionDescriptor, SelectorModel selectorModel)
            {
                if (selectorModel.AttributeRouteModel != null)
                {
                    actionDescriptor.AttributeRouteInfo = new AttributeRouteInfo
                    {
                        Template = selectorModel.AttributeRouteModel.Template,
                        Order = selectorModel.AttributeRouteModel.Order ?? 0,
                        Name = selectorModel.AttributeRouteModel.Name,
                        SuppressLinkGeneration = selectorModel.AttributeRouteModel.SuppressLinkGeneration,
                        SuppressPathMatching = selectorModel.AttributeRouteModel.SuppressPathMatching,
                    };
                }
            }
    
            public static void AddRouteValues(
                ControllerActionDescriptor actionDescriptor,
                ControllerModel controller,
                ActionModel action)
            {
                // 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
                // generating a link.
                foreach (var kvp in action.RouteValues)
                {
                    // Skip duplicates
                    if (!actionDescriptor.RouteValues.ContainsKey(kvp.Key))
                    {
                        actionDescriptor.RouteValues.Add(kvp.Key, kvp.Value);
                    }
                }
    
                foreach (var kvp in controller.RouteValues)
                {
                    // Skip duplicates - this also means that a value on the action will take precedence
                    if (!actionDescriptor.RouteValues.ContainsKey(kvp.Key))
                    {
                        actionDescriptor.RouteValues.Add(kvp.Key, kvp.Value);
                    }
                }
    
                // Lastly add the 'default' values
                if (!actionDescriptor.RouteValues.ContainsKey("action"))
                {
                    actionDescriptor.RouteValues.Add("action", action.ActionName ?? string.Empty);
                }
    
                if (!actionDescriptor.RouteValues.ContainsKey("controller"))
                {
                    actionDescriptor.RouteValues.Add("controller", controller.ControllerName);
                }
            }
    
            private static bool IsAttributeRouted(ActionDescriptor actionDescriptor)
            {
                return actionDescriptor.AttributeRouteInfo != null;
            }
        }
    public static List<TResult> Flatten<TResult>(
                ApplicationModel application,
                Func<ApplicationModel, ControllerModel, ActionModel, SelectorModel, TResult> flattener)
            {
                var results = new List<TResult>();
                var errors = new Dictionary<MethodInfo, IList<string>>();
    
                var actionsByMethod = new Dictionary<MethodInfo, List<(ActionModel, SelectorModel)>>();
                var actionsByRouteName = new Dictionary<string, List<(ActionModel, SelectorModel)>>(StringComparer.OrdinalIgnoreCase);
    
                var routeTemplateErrors = new List<string>();
    
                foreach (var controller in application.Controllers)
                {
                    foreach (var action in controller.Actions)
                    {
                        foreach (var selector in ActionAttributeRouteModel.FlattenSelectors(action))
                        {
                            // PostProcess attribute routes so we can observe any errors.
                            ReplaceAttributeRouteTokens(controller, action, selector, routeTemplateErrors);
    
                            // Add to the data structures we use to find errors.
                            AddActionToMethodInfoMap(actionsByMethod, action, selector);
                            AddActionToRouteNameMap(actionsByRouteName, action, selector);
    
                            var result = flattener(application, controller, action, selector);
                            Debug.Assert(result != null);
    
                            results.Add(result);
                        }
                    }
                }
    
                var attributeRoutingConfigurationErrors = new Dictionary<MethodInfo, string>();
                foreach (var (method, actions) in actionsByMethod)
                {
                    ValidateActionGroupConfiguration(
                        method,
                        actions,
                        attributeRoutingConfigurationErrors);
                }
    
                if (attributeRoutingConfigurationErrors.Any())
                {
                    var message = CreateAttributeRoutingAggregateErrorMessage(attributeRoutingConfigurationErrors.Values);
    
                    throw new InvalidOperationException(message);
                }
    
                var namedRoutedErrors = ValidateNamedAttributeRoutedActions(actionsByRouteName);
                if (namedRoutedErrors.Any())
                {
                    var message = CreateAttributeRoutingAggregateErrorMessage(namedRoutedErrors);
                    throw new InvalidOperationException(message);
                }
    
                if (routeTemplateErrors.Any())
                {
                    var message = CreateAttributeRoutingAggregateErrorMessage(routeTemplateErrors);
                    throw new InvalidOperationException(message);
                }
    
    
                return results;
            }
    internal static class ActionAttributeRouteModel
        {
            public static IEnumerable<SelectorModel> FlattenSelectors(ActionModel actionModel)
            {
                // Loop through all attribute routes defined on the controller. 
                // These perform a cross-product with all of the action-level attribute routes.
                var controllerSelectors = actionModel.Controller.Selectors
                    .Where(sm => sm.AttributeRouteModel != null)
                    .ToList();
    
                // We also include metadata and action constraints from the controller
                // even when there are no routes, or when an action overrides the route template.
                SelectorModel additionalSelector = null;
                if (actionModel.Controller.Selectors.Count > 0)
                {
                    // This logic seems arbitrary but there's a good reason for it.
                    // 
                    // When we build the controller level selectors, any metadata or action constraints
                    // that aren't IRouteTemplateProvider will be included in all selectors. So we 
                    // pick any selector and then grab all of the stuff that isn't IRouteTemplateProvider
                    // then we've found all of the items that aren't routes.
                    //
                    // This is fragile wrt application model customizing the data - but no one has
                    // run into an issue with this and its pretty esoteric.
                    additionalSelector = new SelectorModel(actionModel.Controller.Selectors.First());
                    additionalSelector.AttributeRouteModel = null;
    
                    for (var i = additionalSelector.ActionConstraints.Count - 1; i >= 0; i--)
                    {
                        if (additionalSelector.ActionConstraints[i] is IRouteTemplateProvider)
                        {
                            additionalSelector.ActionConstraints.RemoveAt(i);
                        }
                    }
    
                    for (var i = additionalSelector.EndpointMetadata.Count - 1; i >= 0; i--)
                    {
                        if (additionalSelector.EndpointMetadata[i] is IRouteTemplateProvider)
                        {
                            additionalSelector.EndpointMetadata.RemoveAt(i);
                        }
                    }
                }
    
                var actionConstraints = new List<IActionConstraintMetadata>();
    
                foreach (var actionSelector in actionModel.Selectors)
                {
                    var actionRouteModel = actionSelector.AttributeRouteModel;
    
                    // 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 (actionRouteModel != null && actionRouteModel.IsAbsoluteTemplate)
                    {
                        // We're overriding the routes from the controller, but any *unbound* constraints
                        // still apply.
                        var selector = new SelectorModel(actionSelector);
    
                        selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(
                            left: null,
                            right: actionRouteModel);
    
                        AddActionConstraints(selector, additionalSelector?.ActionConstraints);
                        AddEndpointMetadata(selector, additionalSelector?.EndpointMetadata);
    
                        yield return selector;
                    }
                    else if (controllerSelectors.Count > 0)
                    {
                        for (var i = 0; i < controllerSelectors.Count; i++)
                        {
                            var controllerSelector = controllerSelectors[i];
    
                            // We're using the attribute routes from the controller
                            var selector = new SelectorModel(actionSelector);
    
                            selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(
                                controllerSelector.AttributeRouteModel,
                                actionRouteModel);
    
                            AddActionConstraints(selector, controllerSelector.ActionConstraints);
                            AddEndpointMetadata(selector, controllerSelector.EndpointMetadata);
    
                            // No need to include the additional selector here because it would duplicate
                            // data in controllerSelector.
    
                            yield return selector;
                        }
                    }
                    else
                    {
                        // There are no routes on the controller, but any *unbound* constraints
                        // still apply.
                        var selector = new SelectorModel(actionSelector);
    
                        selector.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(
                            left: null,
                            right: actionRouteModel);
    
                        AddActionConstraints(selector, additionalSelector?.ActionConstraints);
                        AddEndpointMetadata(selector, additionalSelector?.EndpointMetadata);
    
                        yield return selector;
                    }
                }
            }
    
            private static void AddActionConstraints(SelectorModel selector, IList<IActionConstraintMetadata> actionConstraints)
            {
                if (actionConstraints != null)
                {
                    for (var i = 0; i < actionConstraints.Count;i++)
                    {
                        selector.ActionConstraints.Add(actionConstraints[i]);
                    }
                }
            }
    
            private static void AddEndpointMetadata(SelectorModel selector, IList<object> controllerMetadata)
            {
                if (controllerMetadata != null)
                {
                    // It is criticial to get the order in which metadata appears in endpoint metadata correct. More significant metadata
                    // must appear later in the sequence. In this case, the values in `controllerMetadata` should have their order
                    // preserved, but appear earlier than the entries in `selector.EndpointMetadata`.
                    for (var i = 0; i < controllerMetadata.Count; i++)
                    {
                        selector.EndpointMetadata.Insert(i, controllerMetadata[i]);
                    }
                }
            }
    
            public static IEnumerable<(AttributeRouteModel route, SelectorModel actionSelector, SelectorModel controllerSelector)> GetAttributeRoutes(ActionModel actionModel)
            {
                var controllerAttributeRoutes = actionModel.Controller.Selectors
                    .Where(sm => sm.AttributeRouteModel != null)
                    .Select(sm => sm.AttributeRouteModel)
                    .ToList();
    
                foreach (var actionSelectorModel in actionModel.Selectors)
                {
                    var actionRouteModel = actionSelectorModel.AttributeRouteModel;
    
                    // 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 (actionRouteModel != null && actionRouteModel.IsAbsoluteTemplate)
                    {
                        var route = AttributeRouteModel.CombineAttributeRouteModel(
                            left: null,
                            right: actionRouteModel);
    
                        yield return (route, actionSelectorModel, null);
                    }
                    else if (controllerAttributeRoutes.Count > 0)
                    {
                        for (var i = 0; i < actionModel.Controller.Selectors.Count; i++)
                        {
                            // We're using the attribute routes from the controller
                            var controllerSelector = actionModel.Controller.Selectors[i];
    
                            var route = AttributeRouteModel.CombineAttributeRouteModel(
                                controllerSelector.AttributeRouteModel,
                                actionRouteModel);
    
                            yield return (route, actionSelectorModel, controllerSelector);
                        }
                    }
    
                    else
                    {
                        var route = AttributeRouteModel.CombineAttributeRouteModel(
                            left: null,
                            right: actionRouteModel);
    
                        yield return (route, actionSelectorModel, null);
                    }
                }
            }
        }
  • 相关阅读:
    KMP算法的理解和代码实现
    关于线程死锁
    PAT1018
    PAT1059
    PAT1009
    PAT1006
    PAT1005
    PAT1004
    PAT1002
    PAT
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12642807.html
Copyright © 2011-2022 走看看