private void Apply(ControllerModel controller) { foreach (var action in controller.Actions) { foreach (var parameter in action.Parameters) { if (parameter.BinderMetadata is IBinderMetadata) { // This has a binding behavior configured, just leave it alone. } else if (ValueProviderResult.CanConvertFromString(parameter.ParameterInfo.ParameterType)) { // Simple types are by-default from the URI. parameter.BinderMetadata = new FromUriAttribute(); } else { // Complex types are by-default from the body. parameter.BinderMetadata = new FromBodyAttribute(); } // If the parameter has a default value, we want to consider it as optional parameter by default. var optionalMetadata = parameter.BinderMetadata as FromUriAttribute; if (parameter.ParameterInfo.HasDefaultValue && optionalMetadata != null) { optionalMetadata.IsOptional = true; } } } }
private List <OverloadedParameter> GetOverloadableParameters(ActionSelectorCandidate candidate) { if (candidate.Action.Parameters == null) { return(null); } var isOverloaded = false; foreach (var constraint in candidate.Constraints) { if (constraint is OverloadActionConstraint) { isOverloaded = true; } } if (!isOverloaded) { return(null); } var parameters = new List <OverloadedParameter>(); foreach (var parameter in candidate.Action.Parameters) { // We only consider parameters that are bound from the URL. if ((parameter.BinderMetadata is IRouteDataValueProviderMetadata || parameter.BinderMetadata is IQueryValueProviderMetadata) && ValueProviderResult.CanConvertFromString(parameter.ParameterType)) { var optionalMetadata = parameter.BinderMetadata as IOptionalBinderMetadata; if (optionalMetadata == null || optionalMetadata.IsOptional) { // Optional parameters are ignored in overloading. If a parameter doesn't specify that it's // required then treat it as optional (MVC default). WebAPI parameters will all by-default // specify themselves as required unless they have a default value. continue; } var nameProvider = parameter.BinderMetadata as IModelNameProvider; var prefix = nameProvider?.Name ?? parameter.Name; parameters.Add(new OverloadedParameter() { ParameterDescriptor = parameter, Prefix = prefix, }); } } return(parameters); }
private List <OverloadedParameter> GetOverloadableParameters(ActionSelectorCandidate candidate) { if (candidate.Action.Parameters == null) { return(null); } var isOverloaded = false; foreach (var constraint in candidate.Constraints) { if (constraint is OverloadActionConstraint) { isOverloaded = true; } } if (!isOverloaded) { return(null); } var parameters = new List <OverloadedParameter>(); foreach (var parameter in candidate.Action.Parameters) { // We only consider parameters that are bound from the URL. if ((parameter.BinderMetadata is IRouteDataValueProviderMetadata || parameter.BinderMetadata is IQueryValueProviderMetadata) && !parameter.IsOptional && ValueProviderResult.CanConvertFromString(parameter.ParameterType)) { var nameProvider = parameter.BinderMetadata as IModelNameProvider; var prefix = nameProvider?.Name ?? parameter.Name; parameters.Add(new OverloadedParameter() { ParameterDescriptor = parameter, Prefix = prefix, }); } } return(parameters); }
public void Apply(ActionModel action) { if (IsConventionApplicable(action.Controller)) { var optionalParameters = new HashSet <string>(); var uriBindingSource = (new FromUriAttribute()).BindingSource; foreach (var parameter in action.Parameters) { // Some IBindingSourceMetadata attributes like ModelBinder attribute return null // as their binding source. Special case to ensure we do not ignore them. if (parameter.BindingInfo?.BindingSource != null || parameter.Attributes.OfType <IBindingSourceMetadata>().Any()) { // This has a binding behavior configured, just leave it alone. } else if (ValueProviderResult.CanConvertFromString(parameter.ParameterInfo.ParameterType)) { // Simple types are by-default from the URI. parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo(); parameter.BindingInfo.BindingSource = uriBindingSource; } else { // Complex types are by-default from the body. parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo(); parameter.BindingInfo.BindingSource = BindingSource.Body; } // For all non IOptionalBinderMetadata, which are not URL source (like FromQuery etc.) do not // participate in overload selection and hence are added to the hashset so that they can be // ignored in OverloadActionConstraint. var optionalMetadata = parameter.Attributes.OfType <IOptionalBinderMetadata>().SingleOrDefault(); if (parameter.ParameterInfo.HasDefaultValue && parameter.BindingInfo.BindingSource == uriBindingSource || optionalMetadata != null && optionalMetadata.IsOptional || optionalMetadata == null && parameter.BindingInfo.BindingSource != uriBindingSource) { optionalParameters.Add(parameter.ParameterName); } } action.Properties.Add("OptionalParameters", optionalParameters); } }
public void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { foreach (var action in controller.Actions) { foreach (var parameter in action.Parameters) { if (parameter.BinderMetadata is IBinderMetadata || ValueProviderResult.CanConvertFromString(parameter.ParameterInfo.ParameterType)) { // behavior configured or simple type so do nothing } else { // Complex types are by-default from the body. parameter.BinderMetadata = new FromBodyAttribute(); } } } } }
private void Apply(ControllerModel model) { foreach (var action in model.Actions) { foreach (var parameter in action.Parameters) { if (parameter.BinderMetadata is IBinderMetadata) { // This has a binding behavior configured, just leave it alone. } else if (ValueProviderResult.CanConvertFromString(parameter.ParameterInfo.ParameterType)) { // Simple types are by-default from the URI. parameter.BinderMetadata = new FromUriAttribute(); } else { // Complex types are by-default from the body. parameter.BinderMetadata = new FromBodyAttribute(); } } } }
public void Apply(ApplicationModel application) { foreach (var controller in application.Controllers) { foreach (var action in controller.Actions) { foreach (var parameter in action.Parameters) { if (parameter.BindingInfo?.BindingSource != null || parameter.Attributes.OfType <IBindingSourceMetadata>().Any() || ValueProviderResult.CanConvertFromString(parameter.ParameterInfo.ParameterType)) { // behavior configured or simple type so do nothing } else { // Complex types are by-default from the body. parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo(); parameter.BindingInfo.BindingSource = BindingSource.Body; } } } } }
private List <OverloadedParameter> GetOverloadableParameters(ActionSelectorCandidate candidate) { if (candidate.Action.Parameters == null) { return(null); } var isOverloaded = false; foreach (var constraint in candidate.Constraints) { if (constraint is OverloadActionConstraint) { isOverloaded = true; } } if (!isOverloaded) { return(null); } var parameters = new List <OverloadedParameter>(); object optionalParametersObject; candidate.Action.Properties.TryGetValue("OptionalParameters", out optionalParametersObject); var optionalParameters = (HashSet <string>)optionalParametersObject; foreach (var parameter in candidate.Action.Parameters) { // We only consider parameters that are marked as bound from the URL. var source = parameter.BindingInfo?.BindingSource; if (source == null) { continue; } if ((source.CanAcceptDataFrom(BindingSource.Path) || source.CanAcceptDataFrom(BindingSource.Query)) && ValueProviderResult.CanConvertFromString(parameter.ParameterType)) { if (optionalParameters != null) { var isOptional = optionalParameters.Contains(parameter.Name); if (isOptional) { // Optional parameters are ignored in overloading. If a parameter doesn't specify that it's // required then treat it as optional (MVC default). WebAPI parameters will all by-default // specify themselves as required unless they have a default value. continue; } } var prefix = parameter.BindingInfo?.BinderModelName ?? parameter.Name; parameters.Add(new OverloadedParameter() { ParameterDescriptor = parameter, Prefix = prefix, }); } } return(parameters); }
protected virtual async Task <ActionDescriptor> SelectBestCandidate( RouteContext context, List <ActionDescriptor> candidates) { var applicableCandiates = new List <ActionDescriptorCandidate>(); foreach (var action in candidates) { var isApplicable = true; var candidate = new ActionDescriptorCandidate() { Action = action, }; var actionContext = new ActionContext(context, action); var actionBindingContext = await _bindingProvider.GetActionBindingContextAsync(actionContext); foreach (var parameter in action.Parameters.Where(p => p.ParameterBindingInfo != null)) { if (!ValueProviderResult.CanConvertFromString(parameter.ParameterBindingInfo.ParameterType)) { continue; } if (await actionBindingContext.ValueProvider.ContainsPrefixAsync( parameter.ParameterBindingInfo.Prefix)) { candidate.FoundParameters++; if (parameter.IsOptional) { candidate.FoundOptionalParameters++; } } else if (!parameter.IsOptional) { isApplicable = false; break; } } if (isApplicable) { applicableCandiates.Add(candidate); } } if (applicableCandiates.Count == 0) { return(null); } var mostParametersSatisfied = applicableCandiates .GroupBy(c => c.FoundParameters) .OrderByDescending(g => g.Key) .First(); var fewestOptionalParameters = mostParametersSatisfied .GroupBy(c => c.FoundOptionalParameters) .OrderBy(g => g.Key).First() .ToArray(); if (fewestOptionalParameters.Length > 1) { throw new InvalidOperationException("The actions are ambiguious."); } return(fewestOptionalParameters[0].Action); }