Fix #1287 - Port WebAPI parameter binding behavior
This change modifies the default parameter binding behavior for an ApiController to use the WebAPI rules. 'simple types' default to use route data or query string 'complex types' default to use the body (formatters) Adds ModelBindingAttribute to enabled model binding
This commit is contained in:
parent
5b1bcb6079
commit
df8f84b772
|
|
@ -1,9 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.WebApiCompatShim;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
|
||||||
using MvcSample.Web.Models;
|
using MvcSample.Web.Models;
|
||||||
|
|
||||||
namespace MvcSample.Web
|
namespace MvcSample.Web
|
||||||
|
|
@ -61,74 +58,11 @@ namespace MvcSample.Web
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OverloadAttribute : Attribute, IActionConstraint
|
private class OverloadAttribute : Attribute, IActionConstraintFactory
|
||||||
{
|
{
|
||||||
public int Order { get; } = Int32.MaxValue;
|
public IActionConstraint CreateInstance(IServiceProvider services)
|
||||||
|
|
||||||
public bool Accept(ActionConstraintContext context)
|
|
||||||
{
|
{
|
||||||
var candidates = context.Candidates.Select(a => new
|
return new OverloadActionConstraint();
|
||||||
{
|
|
||||||
Action = a,
|
|
||||||
Parameters = GetOverloadableParameters(a.Action),
|
|
||||||
});
|
|
||||||
|
|
||||||
var valueProviderFactory = context.RouteContext.HttpContext.RequestServices
|
|
||||||
.GetRequiredService<ICompositeValueProviderFactory>();
|
|
||||||
|
|
||||||
var factoryContext = new ValueProviderFactoryContext(
|
|
||||||
context.RouteContext.HttpContext,
|
|
||||||
context.RouteContext.RouteData.Values);
|
|
||||||
var valueProvider = valueProviderFactory.GetValueProvider(factoryContext);
|
|
||||||
|
|
||||||
foreach (var group in candidates.GroupBy(c => c.Parameters.Count).OrderByDescending(g => g.Key))
|
|
||||||
{
|
|
||||||
var foundMatch = false;
|
|
||||||
foreach (var candidate in group)
|
|
||||||
{
|
|
||||||
var allFound = true;
|
|
||||||
foreach (var parameter in candidate.Parameters)
|
|
||||||
{
|
|
||||||
if (!(valueProvider.ContainsPrefixAsync(parameter.ParameterBindingInfo.Prefix).Result))
|
|
||||||
{
|
|
||||||
if (candidate.Action.Action == context.CurrentCandidate.Action)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
allFound = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allFound)
|
|
||||||
{
|
|
||||||
foundMatch = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (foundMatch)
|
|
||||||
{
|
|
||||||
return group.Any(c => c.Action.Action == context.CurrentCandidate.Action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<ParameterDescriptor> GetOverloadableParameters(ActionDescriptor action)
|
|
||||||
{
|
|
||||||
if (action.Parameters == null)
|
|
||||||
{
|
|
||||||
return new List<ParameterDescriptor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return action.Parameters.Where(
|
|
||||||
p =>
|
|
||||||
p.ParameterBindingInfo != null &&
|
|
||||||
!p.IsOptional &&
|
|
||||||
ValueProviderResult.CanConvertFromString(p.ParameterBindingInfo.ParameterType))
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Kestrel": "1.0.0-*",
|
"Kestrel": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-*",
|
||||||
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||||
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
{
|
{
|
||||||
|
|
@ -19,6 +20,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
{
|
{
|
||||||
Action = other.Action;
|
Action = other.Action;
|
||||||
Attributes = new List<object>(other.Attributes);
|
Attributes = new List<object>(other.Attributes);
|
||||||
|
BinderMarker = other.BinderMarker;
|
||||||
IsOptional = other.IsOptional;
|
IsOptional = other.IsOptional;
|
||||||
ParameterInfo = other.ParameterInfo;
|
ParameterInfo = other.ParameterInfo;
|
||||||
ParameterName = other.ParameterName;
|
ParameterName = other.ParameterName;
|
||||||
|
|
@ -28,6 +30,8 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
|
|
||||||
public List<object> Attributes { get; private set; }
|
public List<object> Attributes { get; private set; }
|
||||||
|
|
||||||
|
public IBinderMarker BinderMarker { get; set; }
|
||||||
|
|
||||||
public bool IsOptional { get; set; }
|
public bool IsOptional { get; set; }
|
||||||
|
|
||||||
public ParameterInfo ParameterInfo { get; private set; }
|
public ParameterInfo ParameterInfo { get; private set; }
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
var actionBindingContext = await _bindingContextProvider.GetActionBindingContextAsync(actionContext);
|
var actionBindingContext = await _bindingContextProvider.GetActionBindingContextAsync(actionContext);
|
||||||
var metadataProvider = actionBindingContext.MetadataProvider;
|
var metadataProvider = actionBindingContext.MetadataProvider;
|
||||||
var parameters = actionContext.ActionDescriptor.Parameters;
|
|
||||||
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
|
var actionDescriptor = actionContext.ActionDescriptor as ControllerActionDescriptor;
|
||||||
if (actionDescriptor == null)
|
if (actionDescriptor == null)
|
||||||
{
|
{
|
||||||
|
|
@ -37,33 +37,49 @@ namespace Microsoft.AspNet.Mvc
|
||||||
nameof(actionContext));
|
nameof(actionContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionMethodInfo = actionDescriptor.MethodInfo;
|
var parameterMetadata = new List<ModelMetadata>();
|
||||||
var parameterMetadatas = metadataProvider.GetMetadataForParameters(actionMethodInfo);
|
foreach (var parameter in actionDescriptor.Parameters)
|
||||||
|
{
|
||||||
|
var metadata = metadataProvider.GetMetadataForParameter(
|
||||||
|
modelAccessor: null,
|
||||||
|
methodInfo: actionDescriptor.MethodInfo,
|
||||||
|
parameterName: parameter.Name,
|
||||||
|
binderMarker: parameter.BinderMarker);
|
||||||
|
|
||||||
var actionArguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
if (metadata != null)
|
||||||
await PopulateActionArgumentsAsync(parameterMetadatas, actionBindingContext, actionArguments);
|
{
|
||||||
return actionArguments;
|
parameterMetadata.Add(metadata);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task PopulateActionArgumentsAsync(IEnumerable<ModelMetadata> modelMetadatas,
|
var bodyBoundParameterCount = parameterMetadata.Count(
|
||||||
ActionBindingContext actionBindingContext,
|
modelMetadata => modelMetadata.Marker is IBodyBinderMarker);
|
||||||
IDictionary<string, object> invocationInfo)
|
|
||||||
{
|
|
||||||
var bodyBoundParameterCount = modelMetadatas.Count(
|
|
||||||
modelMetadata => modelMetadata.Marker is IBodyBinderMarker);
|
|
||||||
if (bodyBoundParameterCount > 1)
|
if (bodyBoundParameterCount > 1)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Resources.MultipleBodyParametersAreNotAllowed);
|
throw new InvalidOperationException(Resources.MultipleBodyParametersAreNotAllowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var modelMetadata in modelMetadatas)
|
var actionArguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||||
|
foreach (var parameter in parameterMetadata)
|
||||||
{
|
{
|
||||||
var modelBindingContext = GetModelBindingContext(modelMetadata, actionBindingContext);
|
await PopulateArgumentAsync(actionBindingContext, actionArguments, parameter);
|
||||||
|
}
|
||||||
|
|
||||||
if (await actionBindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
return actionArguments;
|
||||||
{
|
}
|
||||||
invocationInfo[modelMetadata.PropertyName] = modelBindingContext.Model;
|
|
||||||
}
|
private async Task PopulateArgumentAsync(
|
||||||
|
ActionBindingContext actionBindingContext,
|
||||||
|
IDictionary<string, object> arguments,
|
||||||
|
ModelMetadata modelMetadata)
|
||||||
|
{
|
||||||
|
|
||||||
|
var parameterType = modelMetadata.ModelType;
|
||||||
|
var modelBindingContext = GetModelBindingContext(modelMetadata, actionBindingContext);
|
||||||
|
|
||||||
|
if (await actionBindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
||||||
|
{
|
||||||
|
arguments[modelMetadata.PropertyName] = modelBindingContext.Model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Microsoft.AspNet.Mvc.ApplicationModel;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.Description;
|
using Microsoft.AspNet.Mvc.Description;
|
||||||
using Microsoft.AspNet.Mvc.Filters;
|
using Microsoft.AspNet.Mvc.Filters;
|
||||||
using Microsoft.AspNet.Mvc.ApplicationModel;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.Routing;
|
using Microsoft.AspNet.Mvc.Routing;
|
||||||
using Microsoft.AspNet.Routing;
|
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
|
@ -191,6 +191,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
var attributes = parameterInfo.GetCustomAttributes(inherit: true).OfType<object>().ToList();
|
var attributes = parameterInfo.GetCustomAttributes(inherit: true).OfType<object>().ToList();
|
||||||
parameterModel.Attributes.AddRange(attributes);
|
parameterModel.Attributes.AddRange(attributes);
|
||||||
|
|
||||||
|
parameterModel.BinderMarker = attributes.OfType<IBinderMarker>().FirstOrDefault();
|
||||||
|
|
||||||
parameterModel.ParameterName = parameterInfo.Name;
|
parameterModel.ParameterName = parameterInfo.Name;
|
||||||
parameterModel.IsOptional = parameterInfo.HasDefaultValue;
|
parameterModel.IsOptional = parameterInfo.HasDefaultValue;
|
||||||
|
|
||||||
|
|
@ -508,8 +510,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
var parameterDescriptor = new ParameterDescriptor()
|
var parameterDescriptor = new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = parameter.BinderMarker,
|
||||||
|
IsOptional = parameter.IsOptional,
|
||||||
Name = parameter.ParameterName,
|
Name = parameter.ParameterName,
|
||||||
IsOptional = parameter.IsOptional
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var isFromBody = parameter.Attributes.OfType<FromBodyAttribute>().Any();
|
var isFromBody = parameter.Attributes.OfType<FromBodyAttribute>().Any();
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
|
@ -16,6 +17,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
public ParameterBindingInfo ParameterBindingInfo { get; set; }
|
public ParameterBindingInfo ParameterBindingInfo { get; set; }
|
||||||
|
|
||||||
|
public IBinderMarker BinderMarker { get; set; }
|
||||||
|
|
||||||
public BodyParameterInfo BodyParameterInfo { get; set; }
|
public BodyParameterInfo BodyParameterInfo { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,24 +46,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return CreateMetadataFromPrototype(prototype, modelAccessor);
|
return CreateMetadataFromPrototype(prototype, modelAccessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ModelMetadata> GetMetadataForParameters([NotNull] MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
var parameters = methodInfo.GetParameters();
|
|
||||||
foreach (var parameter in parameters)
|
|
||||||
{
|
|
||||||
// Name can be null if the methodinfo represents an open delegate.
|
|
||||||
if (!string.IsNullOrEmpty(parameter.Name))
|
|
||||||
{
|
|
||||||
yield return GetMetadataForParameterCore(
|
|
||||||
modelAccessor: null, parameterName: parameter.Name, parameter: parameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModelMetadata GetMetadataForParameter(
|
public ModelMetadata GetMetadataForParameter(
|
||||||
Func<object> modelAccessor,
|
Func<object> modelAccessor,
|
||||||
[NotNull] MethodInfo methodInfo,
|
[NotNull] MethodInfo methodInfo,
|
||||||
[NotNull] string parameterName)
|
[NotNull] string parameterName,
|
||||||
|
IBinderMarker binderMarker)
|
||||||
{
|
{
|
||||||
var parameter = methodInfo.GetParameters().FirstOrDefault(
|
var parameter = methodInfo.GetParameters().FirstOrDefault(
|
||||||
param => StringComparer.Ordinal.Equals(param.Name, parameterName));
|
param => StringComparer.Ordinal.Equals(param.Name, parameterName));
|
||||||
|
|
@ -73,7 +60,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
throw new ArgumentException(message, nameof(parameterName));
|
throw new ArgumentException(message, nameof(parameterName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetMetadataForParameterCore(modelAccessor, parameterName, parameter);
|
return GetMetadataForParameterCore(modelAccessor, parameterName, parameter, binderMarker);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override for creating the prototype metadata (without the accessor)
|
// Override for creating the prototype metadata (without the accessor)
|
||||||
|
|
@ -87,12 +74,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Func<object> modelAccessor);
|
Func<object> modelAccessor);
|
||||||
private ModelMetadata GetMetadataForParameterCore(Func<object> modelAccessor,
|
private ModelMetadata GetMetadataForParameterCore(Func<object> modelAccessor,
|
||||||
string parameterName,
|
string parameterName,
|
||||||
ParameterInfo parameter)
|
ParameterInfo parameter,
|
||||||
|
IBinderMarker binderMarker)
|
||||||
{
|
{
|
||||||
var parameterInfo =
|
var parameterInfo =
|
||||||
CreateParameterInfo(parameter.ParameterType,
|
CreateParameterInfo(parameter.ParameterType,
|
||||||
parameter.GetCustomAttributes(),
|
parameter.GetCustomAttributes(),
|
||||||
parameterName);
|
parameterName,
|
||||||
|
binderMarker);
|
||||||
|
|
||||||
var typeInfo = GetTypeInformation(parameter.ParameterType);
|
var typeInfo = GetTypeInformation(parameter.ParameterType);
|
||||||
UpdateMetadataWithTypeInfo(parameterInfo.Prototype, typeInfo);
|
UpdateMetadataWithTypeInfo(parameterInfo.Prototype, typeInfo);
|
||||||
|
|
@ -244,13 +233,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ParameterInformation CreateParameterInfo(Type parameterType, IEnumerable<Attribute> attributes, string parameterName)
|
private ParameterInformation CreateParameterInfo(
|
||||||
|
Type parameterType,
|
||||||
|
IEnumerable<Attribute> attributes,
|
||||||
|
string parameterName,
|
||||||
|
IBinderMarker binderMarker)
|
||||||
{
|
{
|
||||||
var metadataProtoType = CreateMetadataPrototype(attributes: attributes,
|
var metadataProtoType = CreateMetadataPrototype(attributes: attributes,
|
||||||
containerType: null,
|
containerType: null,
|
||||||
modelType: parameterType,
|
modelType: parameterType,
|
||||||
propertyName: parameterName);
|
propertyName: parameterName);
|
||||||
|
|
||||||
|
if (binderMarker != null)
|
||||||
|
{
|
||||||
|
metadataProtoType.Marker = binderMarker;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameProvider = binderMarker as IModelNameProvider;
|
||||||
|
if (nameProvider != null && nameProvider.Name != null)
|
||||||
|
{
|
||||||
|
metadataProtoType.ModelName = nameProvider.Name;
|
||||||
|
}
|
||||||
|
|
||||||
return new ParameterInformation
|
return new ParameterInformation
|
||||||
{
|
{
|
||||||
Prototype = metadataProtoType
|
Prototype = metadataProtoType
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType);
|
ModelMetadata GetMetadataForType(Func<object> modelAccessor, [NotNull] Type modelType);
|
||||||
|
|
||||||
IEnumerable<ModelMetadata> GetMetadataForParameters([NotNull] MethodInfo methodInfo);
|
ModelMetadata GetMetadataForParameter(
|
||||||
|
Func<object> modelAccessor,
|
||||||
ModelMetadata GetMetadataForParameter(Func<object> modelAccessor, [NotNull] MethodInfo methodInfo, [NotNull] string parameterName);
|
[NotNull] MethodInfo methodInfo,
|
||||||
|
[NotNull] string parameterName,
|
||||||
|
IBinderMarker binderMarker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ namespace System.Web.Http
|
||||||
{
|
{
|
||||||
[UseWebApiRoutes]
|
[UseWebApiRoutes]
|
||||||
[UseWebApiActionConventions]
|
[UseWebApiActionConventions]
|
||||||
|
[UseWebApiParameterConventions]
|
||||||
[UseWebApiOverloading]
|
[UseWebApiOverloading]
|
||||||
public abstract class ApiController : IDisposable
|
public abstract class ApiController : IDisposable
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
{
|
||||||
|
public interface IUseWebApiParameterConventions
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
{
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class UseWebApiParameterConventionsAttribute : Attribute, IUseWebApiParameterConventions
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using System.Web.Http;
|
||||||
|
using Microsoft.AspNet.Mvc.ApplicationModel;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
{
|
||||||
|
public class WebApiParameterConventionsGlobalModelConvention : IGlobalModelConvention
|
||||||
|
{
|
||||||
|
public void Apply(GlobalModel model)
|
||||||
|
{
|
||||||
|
foreach (var controller in model.Controllers)
|
||||||
|
{
|
||||||
|
if (IsConventionApplicable(controller))
|
||||||
|
{
|
||||||
|
Apply(controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsConventionApplicable(ControllerModel controller)
|
||||||
|
{
|
||||||
|
return controller.Attributes.OfType<IUseWebApiParameterConventions>().Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Apply(ControllerModel model)
|
||||||
|
{
|
||||||
|
foreach (var action in model.Actions)
|
||||||
|
{
|
||||||
|
foreach (var parameter in action.Parameters)
|
||||||
|
{
|
||||||
|
if (parameter.BinderMarker != null)
|
||||||
|
{
|
||||||
|
// 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.BinderMarker = new FromUriAttribute();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Complex types are by-default from the body.
|
||||||
|
parameter.BinderMarker = new FromBodyAttribute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNet.Mvc.ApplicationModel;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
|
||||||
{
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
|
||||||
public class FromUriAttribute : Attribute, IParameterModelConvention
|
|
||||||
{
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
public void Apply(ParameterModel model)
|
|
||||||
{
|
|
||||||
if (Name != null)
|
|
||||||
{
|
|
||||||
model.ParameterName = Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
foreach (var parameter in candidate.Parameters)
|
foreach (var parameter in candidate.Parameters)
|
||||||
{
|
{
|
||||||
if (!requestKeys.Contains(parameter.ParameterBindingInfo.Prefix))
|
if (!requestKeys.Contains(parameter.Prefix))
|
||||||
{
|
{
|
||||||
if (candidate.Action.Action == context.CurrentCandidate.Action)
|
if (candidate.Action.Action == context.CurrentCandidate.Action)
|
||||||
{
|
{
|
||||||
|
|
@ -65,7 +65,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ParameterDescriptor> GetOverloadableParameters(ActionSelectorCandidate candidate)
|
private List<OverloadedParameter> GetOverloadableParameters(ActionSelectorCandidate candidate)
|
||||||
{
|
{
|
||||||
if (candidate.Action.Parameters == null)
|
if (candidate.Action.Parameters == null)
|
||||||
{
|
{
|
||||||
|
|
@ -86,13 +86,26 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only consider parameters that are bound from the URL.
|
var parameters = new List<OverloadedParameter>();
|
||||||
return candidate.Action.Parameters.Where(
|
|
||||||
p =>
|
foreach (var parameter in candidate.Action.Parameters)
|
||||||
p.ParameterBindingInfo != null &&
|
{
|
||||||
!p.IsOptional &&
|
// We only consider parameters that are bound from the URL.
|
||||||
ValueProviderResult.CanConvertFromString(p.ParameterBindingInfo.ParameterType))
|
if ((parameter.BinderMarker is IRouteDataMarker || parameter.BinderMarker is IQueryBinderMarker) &&
|
||||||
.ToList();
|
!parameter.IsOptional &&
|
||||||
|
ValueProviderResult.CanConvertFromString(parameter.ParameterBindingInfo.ParameterType))
|
||||||
|
{
|
||||||
|
var prefix = (parameter.BinderMarker as IModelNameProvider).Name ?? parameter.Name;
|
||||||
|
|
||||||
|
parameters.Add(new OverloadedParameter()
|
||||||
|
{
|
||||||
|
ParameterDescriptor = parameter,
|
||||||
|
Prefix = prefix,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ISet<string> GetCombinedKeys(RouteContext routeContext)
|
private static ISet<string> GetCombinedKeys(RouteContext routeContext)
|
||||||
|
|
@ -121,5 +134,12 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class OverloadedParameter
|
||||||
|
{
|
||||||
|
public ParameterDescriptor ParameterDescriptor { get; set; }
|
||||||
|
|
||||||
|
public string Prefix { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace System.Web.Http
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An attribute that specifies that the value can be bound from the query string or route data.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class FromUriAttribute : Attribute, IQueryBinderMarker, IRouteDataMarker, IModelNameProvider
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace System.Web.Http
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An attribute that specifies that the value can be bound by a model binder.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class ModelBinderAttribute : Attribute, IBinderMarker
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -22,6 +22,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
// Add webapi behaviors to controllers with the appropriate attributes
|
// Add webapi behaviors to controllers with the appropriate attributes
|
||||||
options.ApplicationModelConventions.Add(new WebApiActionConventionsGlobalModelConvention());
|
options.ApplicationModelConventions.Add(new WebApiActionConventionsGlobalModelConvention());
|
||||||
|
options.ApplicationModelConventions.Add(new WebApiParameterConventionsGlobalModelConvention());
|
||||||
options.ApplicationModelConventions.Add(new WebApiOverloadingGlobalModelConvention());
|
options.ApplicationModelConventions.Add(new WebApiOverloadingGlobalModelConvention());
|
||||||
options.ApplicationModelConventions.Add(new WebApiRoutesGlobalModelConvention(area: DefaultAreaName));
|
options.ApplicationModelConventions.Add(new WebApiRoutesGlobalModelConvention(area: DefaultAreaName));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
|
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
|
||||||
"Microsoft.AspNet.Mvc.Core": "6.0.0-*",
|
"Microsoft.AspNet.Mvc.Core": "6.0.0-*",
|
||||||
|
"Microsoft.AspNet.Mvc.ModelBinding": "6.0.0-*",
|
||||||
"Microsoft.AspNet.WebApi.Client": "5.2.2"
|
"Microsoft.AspNet.WebApi.Client": "5.2.2"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModel
|
||||||
|
|
||||||
parameter.Action = new ActionModel(typeof(TestController).GetMethod("Edit"));
|
parameter.Action = new ActionModel(typeof(TestController).GetMethod("Edit"));
|
||||||
parameter.Attributes.Add(new FromBodyAttribute());
|
parameter.Attributes.Add(new FromBodyAttribute());
|
||||||
|
parameter.BinderMarker = new FromBodyAttribute();
|
||||||
parameter.IsOptional = true;
|
parameter.IsOptional = true;
|
||||||
parameter.ParameterName = "id";
|
parameter.ParameterName = "id";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
|
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
|
||||||
methodInfo: methodInfo,
|
methodInfo: methodInfo,
|
||||||
parameterName: "foo");
|
parameterName: "foo",
|
||||||
|
binderMarker: null);
|
||||||
|
|
||||||
|
|
||||||
var actionBindingContext = new ActionBindingContext(actionContext,
|
var actionBindingContext = new ActionBindingContext(actionContext,
|
||||||
|
|
@ -93,7 +94,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
|
var modelMetadata = metadataProvider.GetMetadataForParameter(modelAccessor: null,
|
||||||
methodInfo: methodInfo,
|
methodInfo: methodInfo,
|
||||||
parameterName: "foo1");
|
parameterName: "foo1",
|
||||||
|
binderMarker: null);
|
||||||
|
|
||||||
|
|
||||||
var actionBindingContext = new ActionBindingContext(actionContext,
|
var actionBindingContext = new ActionBindingContext(actionContext,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#if ASPNET50
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
@ -377,5 +376,4 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.PaymentRequired, response.StatusCode);
|
Assert.Equal(HttpStatusCode.PaymentRequired, response.StatusCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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 System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Formatting;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web.Http;
|
||||||
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
{
|
||||||
|
public class WebApiCompatShimParameterBindingTest
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _provider = TestHelper.CreateServices(nameof(WebApiCompatShimWebSite));
|
||||||
|
private readonly Action<IApplicationBuilder> _app = new WebApiCompatShimWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http://localhost/api/Blog/Employees/PostByIdDefault/5")]
|
||||||
|
[InlineData("http://localhost/api/Blog/Employees/PostByIdDefault?id=5")]
|
||||||
|
public async Task ApiController_SimpleParameter_Default_ReadsFromUrl(string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("5", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiController_SimpleParameter_Default_DoesNotReadFormData()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url = "http://localhost/api/Blog/Employees/PostByIdDefault";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "id", "5" },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("-1", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http://localhost/api/Blog/Employees/PostByIdModelBinder/5")]
|
||||||
|
[InlineData("http://localhost/api/Blog/Employees/PostByIdModelBinder?id=5")]
|
||||||
|
public async Task ApiController_SimpleParameter_ModelBinder_ReadsFromUrl(string url)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("5", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiController_SimpleParameter_ModelBinder_ReadsFromFormData()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url = "http://localhost/api/Blog/Employees/PostByIdModelBinder";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "id", "5" },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("5", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http://localhost/api/Blog/Employees/PostByIdFromQuery/5", "-1")]
|
||||||
|
[InlineData("http://localhost/api/Blog/Employees/PostByIdFromQuery?id=5", "5")]
|
||||||
|
public async Task ApiController_SimpleParameter_FromQuery_ReadsFromQueryNotRouteData(string url, string expected)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal(expected, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiController_SimpleParameter_FromQuery_DoesNotReadFormData()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url = "http://localhost/api/Blog/Employees/PostByIdFromQuery";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||||
|
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "id", "5" },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("-1", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiController_ComplexParameter_Default_ReadsFromBody()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url = "http://localhost/api/Blog/Employees/PutEmployeeDefault";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Put, url);
|
||||||
|
request.Content = new StringContent(JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
Id = 5,
|
||||||
|
Name = "Test Employee",
|
||||||
|
}));
|
||||||
|
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("{\"Id\":5,\"Name\":\"Test Employee\"}", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiController_ComplexParameter_ModelBinder_ReadsFormAndUrl()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url = "http://localhost/api/Blog/Employees/PutEmployeeModelBinder/5";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Put, url);
|
||||||
|
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "name", "Test Employee" },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("{\"Id\":5,\"Name\":\"Test Employee\"}", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
// name is read from the url - and the rest from the body (formatters)
|
||||||
|
[Fact]
|
||||||
|
public async Task ApiController_TwoParameters_DefaultSources()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_provider, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var url = "http://localhost/api/Blog/Employees/PutEmployeeBothDefault?name=Name_Override";
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Put, url);
|
||||||
|
request.Content = new StringContent(JsonConvert.SerializeObject(new
|
||||||
|
{
|
||||||
|
Id = 5,
|
||||||
|
Name = "Test Employee",
|
||||||
|
}));
|
||||||
|
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("{\"Id\":5,\"Name\":\"Name_Override\"}", content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -927,7 +927,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
private static ModelMetadata GetMetadataForParameter(MethodInfo methodInfo, string parameterName)
|
private static ModelMetadata GetMetadataForParameter(MethodInfo methodInfo, string parameterName)
|
||||||
{
|
{
|
||||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
return metadataProvider.GetMetadataForParameter(null, methodInfo, parameterName);
|
return metadataProvider.GetMetadataForParameter(
|
||||||
|
modelAccessor: null,
|
||||||
|
methodInfo: methodInfo,
|
||||||
|
parameterName: parameterName,
|
||||||
|
binderMarker: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Person
|
private class Person
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
"Property3", "Property4", "IncludedAndExcludedExplicitly1", "ExcludedExplicitly1" };
|
"Property3", "Property4", "IncludedAndExcludedExplicitly1", "ExcludedExplicitly1" };
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var metadata = provider.GetMetadataForParameter(null, methodInfo, "param");
|
var metadata = provider.GetMetadataForParameter(
|
||||||
|
modelAccessor: null,
|
||||||
|
methodInfo: methodInfo,
|
||||||
|
parameterName: "param",
|
||||||
|
binderMarker: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expectedIncludedPropertyNames.ToList(), metadata.IncludedProperties);
|
Assert.Equal(expectedIncludedPropertyNames.ToList(), metadata.IncludedProperties);
|
||||||
|
|
@ -79,7 +83,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var provider = new DataAnnotationsModelMetadataProvider();
|
var provider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var metadata = provider.GetMetadataForParameter(null, methodInfo, "param");
|
var metadata = provider.GetMetadataForParameter(
|
||||||
|
modelAccessor: null,
|
||||||
|
methodInfo: methodInfo,
|
||||||
|
parameterName: "param",
|
||||||
|
binderMarker: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("ParameterPrefix", metadata.ModelName);
|
Assert.Equal("ParameterPrefix", metadata.ModelName);
|
||||||
|
|
@ -108,7 +116,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var provider = new DataAnnotationsModelMetadataProvider();
|
var provider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var metadata = provider.GetMetadataForParameter(null, methodInfo, "param");
|
var metadata = provider.GetMetadataForParameter(
|
||||||
|
modelAccessor: null,
|
||||||
|
methodInfo: methodInfo,
|
||||||
|
parameterName: "param",
|
||||||
|
binderMarker: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("ParameterPrefix", metadata.ModelName);
|
Assert.Equal("ParameterPrefix", metadata.ModelName);
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,87 @@ namespace System.Web.Http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetActions_Parameters_SimpleTypeFromUriByDefault()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
provider.Invoke(context);
|
||||||
|
|
||||||
|
var results = context.Results.Cast<ControllerActionDescriptor>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var controllerType = typeof(TestControllers.EmployeesController).GetTypeInfo();
|
||||||
|
var actions = results
|
||||||
|
.Where(ad => ad.ControllerDescriptor.ControllerTypeInfo == controllerType)
|
||||||
|
.Where(ad => ad.Name == "Get")
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
Assert.NotEmpty(actions);
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
var parameter = Assert.Single(action.Parameters);
|
||||||
|
Assert.IsType<FromUriAttribute>(parameter.BinderMarker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetActions_Parameters_ComplexTypeFromBodyByDefault()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
provider.Invoke(context);
|
||||||
|
|
||||||
|
var results = context.Results.Cast<ControllerActionDescriptor>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var controllerType = typeof(TestControllers.EmployeesController).GetTypeInfo();
|
||||||
|
var actions = results
|
||||||
|
.Where(ad => ad.ControllerDescriptor.ControllerTypeInfo == controllerType)
|
||||||
|
.Where(ad => ad.Name == "Put")
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
Assert.NotEmpty(actions);
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
var parameter = Assert.Single(action.Parameters);
|
||||||
|
Assert.IsType<FromBodyAttribute>(parameter.BinderMarker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetActions_Parameters_BinderMarker()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = CreateProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var context = new ActionDescriptorProviderContext();
|
||||||
|
provider.Invoke(context);
|
||||||
|
|
||||||
|
var results = context.Results.Cast<ControllerActionDescriptor>();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var controllerType = typeof(TestControllers.EmployeesController).GetTypeInfo();
|
||||||
|
var actions = results
|
||||||
|
.Where(ad => ad.ControllerDescriptor.ControllerTypeInfo == controllerType)
|
||||||
|
.Where(ad => ad.Name == "Post")
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
Assert.NotEmpty(actions);
|
||||||
|
foreach (var action in actions)
|
||||||
|
{
|
||||||
|
var parameter = Assert.Single(action.Parameters);
|
||||||
|
Assert.IsType<ModelBinderAttribute>(parameter.BinderMarker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private INestedProviderManager<ActionDescriptorProviderContext> CreateProvider()
|
private INestedProviderManager<ActionDescriptorProviderContext> CreateProvider()
|
||||||
{
|
{
|
||||||
var assemblyProvider = new Mock<IAssemblyProvider>();
|
var assemblyProvider = new Mock<IAssemblyProvider>();
|
||||||
|
|
@ -346,5 +427,27 @@ namespace System.Web.Http.TestControllers
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class EmployeesController : ApiController
|
||||||
|
{
|
||||||
|
public IActionResult Get(int id)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Put(Employee employee)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Post([ModelBinder] Employee employee)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Employee
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Web.Http;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.PipelineCore;
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
|
|
@ -20,6 +21,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -49,11 +51,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -68,7 +72,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
};
|
};
|
||||||
|
|
||||||
context.CurrentCandidate = context.Candidates[0];
|
context.CurrentCandidate = context.Candidates[0];
|
||||||
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17});
|
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
Assert.True(constraint.Accept(context));
|
Assert.True(constraint.Accept(context));
|
||||||
|
|
@ -83,11 +87,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -117,11 +123,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -151,11 +159,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
IsOptional = true,
|
IsOptional = true,
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
|
|
@ -186,11 +196,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -201,11 +213,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity_ordered",
|
Name = "quantity_ordered",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity_ordered", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity_ordered", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -239,6 +253,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -249,11 +264,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -284,11 +301,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
IsOptional = true,
|
IsOptional = true,
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
|
|
@ -300,11 +319,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -335,11 +356,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -350,11 +373,13 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "price",
|
Name = "price",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("price", typeof(decimal)),
|
ParameterBindingInfo = new ParameterBindingInfo("price", typeof(decimal)),
|
||||||
},
|
},
|
||||||
|
|
@ -388,6 +413,7 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
@ -398,11 +424,61 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
Name = "id",
|
Name = "id",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
},
|
},
|
||||||
new ParameterDescriptor()
|
new ParameterDescriptor()
|
||||||
{
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
|
Name = "quantity",
|
||||||
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var constraint = new OverloadActionConstraint();
|
||||||
|
|
||||||
|
var context = new ActionConstraintContext();
|
||||||
|
context.Candidates = new List<ActionSelectorCandidate>()
|
||||||
|
{
|
||||||
|
new ActionSelectorCandidate(action1, new [] { constraint }),
|
||||||
|
new ActionSelectorCandidate(action2, new IActionConstraint[] { }),
|
||||||
|
};
|
||||||
|
|
||||||
|
context.CurrentCandidate = context.Candidates[0];
|
||||||
|
context.RouteContext = CreateRouteContext("?quantity=5", new { id = 17 });
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.True(constraint.Accept(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Accept_AcceptsAction_WithFewerParameters_WhenOtherIsNotOverloaded_FromBodyAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var action1 = new ActionDescriptor();
|
||||||
|
action1.Parameters = new List<ParameterDescriptor>()
|
||||||
|
{
|
||||||
|
new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
|
Name = "id",
|
||||||
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
var action2 = new ActionDescriptor();
|
||||||
|
action2.Parameters = new List<ParameterDescriptor>()
|
||||||
|
{
|
||||||
|
new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
BinderMarker = new FromUriAttribute(),
|
||||||
|
Name = "id",
|
||||||
|
ParameterBindingInfo = new ParameterBindingInfo("id", typeof(int)),
|
||||||
|
},
|
||||||
|
new ParameterDescriptor()
|
||||||
|
{
|
||||||
|
BinderMarker = new FromBodyAttribute(),
|
||||||
Name = "quantity",
|
Name = "quantity",
|
||||||
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
ParameterBindingInfo = new ParameterBindingInfo("quantity", typeof(int)),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Builder
|
||||||
{
|
{
|
||||||
public static Configuration GetTestConfiguration(this IApplicationBuilder app)
|
public static Configuration GetTestConfiguration(this IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
var configurationProvider = app.ApplicationServices.GetRequiredService<ITestConfigurationProvider>();
|
var configurationProvider = app.ApplicationServices.GetService<ITestConfigurationProvider>();
|
||||||
var configuration = configurationProvider == null
|
var configuration = configurationProvider == null
|
||||||
? new Configuration()
|
? new Configuration()
|
||||||
: configurationProvider.Configuration;
|
: configurationProvider.Configuration;
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>24b59501-5f37-4129-96e6-f02ec34c7e2c</ProjectGuid>
|
<ProjectGuid>24b59501-5f37-4129-96e6-f02ec34c7e2c</ProjectGuid>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration">
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration">
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SchemaVersion>2.0</SchemaVersion>
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<DevelopmentServerPort>55571</DevelopmentServerPort>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Web.Http;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace WebApiCompatShimWebSite.Controllers.ParameterBinding
|
||||||
|
{
|
||||||
|
public class EmployeesController : ApiController
|
||||||
|
{
|
||||||
|
public IActionResult PostByIdDefault(int id = -1)
|
||||||
|
{
|
||||||
|
return Ok(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult PostByIdModelBinder([ModelBinder] int id = -1)
|
||||||
|
{
|
||||||
|
return Ok(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult PostByIdFromQuery([FromQuery] int id = -1)
|
||||||
|
{
|
||||||
|
return Ok(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult PutEmployeeDefault(Employee employee)
|
||||||
|
{
|
||||||
|
return Ok(employee);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult PutEmployeeModelBinder([ModelBinder] Employee employee)
|
||||||
|
{
|
||||||
|
return Ok(employee);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult PutEmployeeBothDefault(string name, Employee employee)
|
||||||
|
{
|
||||||
|
employee.Name = name;
|
||||||
|
return Ok(employee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace WebApiCompatShimWebSite
|
||||||
|
{
|
||||||
|
public class Employee
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,5 +8,6 @@
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"aspnet50": { },
|
"aspnet50": { },
|
||||||
"aspnetcore50": { }
|
"aspnetcore50": { }
|
||||||
}
|
},
|
||||||
|
"webroot": "wwwroot"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue