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); } }
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); }
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); } }
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); }
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); } }