예제 #1
0
    internal void InferParameterBindingSources(ActionModel action)
    {
        for (var i = 0; i < action.Parameters.Count; i++)
        {
            var parameter     = action.Parameters[i];
            var bindingSource = parameter.BindingInfo?.BindingSource;
            if (bindingSource == null)
            {
                bindingSource = InferBindingSourceForParameter(parameter);

                parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
                parameter.BindingInfo.BindingSource = bindingSource;
            }
        }

        var fromBodyParameters = action.Parameters.Where(p => p.BindingInfo !.BindingSource == BindingSource.Body).ToList();

        if (fromBodyParameters.Count > 1)
        {
            var parameters = string.Join(Environment.NewLine, fromBodyParameters.Select(p => p.DisplayName));
            var message    = Resources.FormatApiController_MultipleBodyParametersFound(
                action.DisplayName,
                nameof(FromQueryAttribute),
                nameof(FromRouteAttribute),
                nameof(FromBodyAttribute));

            message += Environment.NewLine + parameters;
            throw new InvalidOperationException(message);
        }
    }
예제 #2
0
    private static RouteInfo GetRouteInfo(
        Dictionary <string, RouteTemplate> templateCache,
        ActionDescriptor action)
    {
        var routeInfo = new RouteInfo()
        {
            ActionDescriptor = action,
        };

        try
        {
            var template = action.AttributeRouteInfo !.Template !;
            if (!templateCache.TryGetValue(template, out var parsedTemplate))
            {
                // Parsing with throw if the template is invalid.
                parsedTemplate = TemplateParser.Parse(template);
                templateCache.Add(template, parsedTemplate);
            }

            routeInfo.RouteTemplate          = parsedTemplate;
            routeInfo.SuppressPathMatching   = action.AttributeRouteInfo.SuppressPathMatching;
            routeInfo.SuppressLinkGeneration = action.AttributeRouteInfo.SuppressLinkGeneration;
        }
        catch (Exception ex)
        {
            routeInfo.ErrorMessage = ex.Message;
            return(routeInfo);
        }

        foreach (var kvp in action.RouteValues)
        {
            foreach (var parameter in routeInfo.RouteTemplate.Parameters)
            {
                if (string.Equals(kvp.Key, parameter.Name, StringComparison.OrdinalIgnoreCase))
                {
                    routeInfo.ErrorMessage = Resources.FormatAttributeRoute_CannotContainParameter(
                        routeInfo.RouteTemplate.TemplateText,
                        kvp.Key,
                        kvp.Value);

                    return(routeInfo);
                }
            }
        }

        routeInfo.Order     = action.AttributeRouteInfo.Order;
        routeInfo.RouteName = action.AttributeRouteInfo.Name;

        return(routeInfo);
    }
예제 #3
0
    public Task RouteAsync(RouteContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (Actions == null)
        {
            var message = Resources.FormatPropertyOfTypeCannotBeNull(
                nameof(Actions),
                nameof(MvcAttributeRouteHandler));
            throw new InvalidOperationException(message);
        }

        var actionDescriptor = _actionSelector.SelectBestCandidate(context, Actions);

        if (actionDescriptor == null)
        {
            _logger.NoActionsMatched(context.RouteData.Values);
            return(Task.CompletedTask);
        }

        foreach (var kvp in actionDescriptor.RouteValues)
        {
            if (!string.IsNullOrEmpty(kvp.Value))
            {
                context.RouteData.Values[kvp.Key] = kvp.Value;
            }
        }

        context.Handler = (c) =>
        {
            var routeData = c.GetRouteData();

            var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);
            var invoker       = _actionInvokerFactory.CreateInvoker(actionContext);
            if (invoker == null)
            {
                throw new InvalidOperationException(
                          Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
                              actionDescriptor.DisplayName));
            }

            return(invoker.InvokeAsync());
        };

        return(Task.CompletedTask);
    }
    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);
        }
    }
예제 #5
0
    private static List <RouteInfo> GetRouteInfos(IReadOnlyList <ActionDescriptor> actions)
    {
        var routeInfos = new List <RouteInfo>();
        var errors     = new List <RouteInfo>();

        // This keeps a cache of 'Template' objects. It's a fairly common case that multiple actions
        // will use the same route template string; thus, the `Template` object can be shared.
        //
        // For a relatively simple route template, the `Template` object will hold about 500 bytes
        // of memory, so sharing is worthwhile.
        var templateCache = new Dictionary <string, RouteTemplate>(StringComparer.OrdinalIgnoreCase);

        var attributeRoutedActions = actions.Where(a => a.AttributeRouteInfo?.Template != null);

        foreach (var action in attributeRoutedActions)
        {
            var routeInfo = GetRouteInfo(templateCache, action);
            if (routeInfo.ErrorMessage == null)
            {
                routeInfos.Add(routeInfo);
            }
            else
            {
                errors.Add(routeInfo);
            }
        }

        if (errors.Count > 0)
        {
            var allErrors = string.Join(
                Environment.NewLine + Environment.NewLine,
                errors.Select(
                    e => Resources.FormatAttributeRoute_IndividualErrorMessage(
                        e.ActionDescriptor.DisplayName,
                        Environment.NewLine,
                        e.ErrorMessage)));

            var message = Resources.FormatAttributeRoute_AggregateErrorMessage(Environment.NewLine, allErrors);
            throw new RouteCreationException(message);
        }

        return(routeInfos);
    }
    private static MediaTypeCollection GetContentTypes(string contentType, string[] additionalContentTypes)
    {
        var completeContentTypes = new List <string>(additionalContentTypes.Length + 1);

        completeContentTypes.Add(contentType);
        completeContentTypes.AddRange(additionalContentTypes);
        MediaTypeCollection contentTypes = new();

        foreach (var type in completeContentTypes)
        {
            var mediaType = new MediaType(type);
            if (mediaType.HasWildcard)
            {
                throw new InvalidOperationException(Resources.FormatGetContentTypes_WildcardsNotSupported(type));
            }

            contentTypes.Add(type);
        }

        return(contentTypes);
    }
예제 #7
0
    public ActionDescriptor?SelectBestCandidate(RouteContext context, IReadOnlyList <ActionDescriptor> candidates)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (candidates == null)
        {
            throw new ArgumentNullException(nameof(candidates));
        }

        var finalMatches = EvaluateActionConstraints(context, candidates);

        if (finalMatches == null || finalMatches.Count == 0)
        {
            return(null);
        }
        else if (finalMatches.Count == 1)
        {
            var selectedAction = finalMatches[0];

            return(selectedAction);
        }
        else
        {
            var actionNames = string.Join(
                Environment.NewLine,
                finalMatches.Select(a => a.DisplayName));

            _logger.AmbiguousActions(actionNames);

            var message = Resources.FormatDefaultActionSelector_AmbiguousActions(
                Environment.NewLine,
                actionNames);

            throw new AmbiguousActionException(message);
        }
    }