Fix for #1722 - FromHeader does not respect default value
This change adds support for our three-valued logic to the default value handling part of the MutableObjectModelBinder. The issue is that we want to look up a default value when a 'greedy' model binder returns true but doesn't find a value. We also don't want to call the property setter unless there is: 1). A value from model binding OR 2). A default value
This commit is contained in:
parent
692a07240c
commit
51e7812e7e
|
|
@ -88,7 +88,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
var parameterType = parameter.ModelType;
|
var parameterType = parameter.ModelType;
|
||||||
var modelBindingContext = GetModelBindingContext(parameter, actionContext, operationBindingContext);
|
var modelBindingContext = GetModelBindingContext(parameter, actionContext, operationBindingContext);
|
||||||
if (await bindingContext.ModelBinder.BindModelAsync(modelBindingContext))
|
if (await bindingContext.ModelBinder.BindModelAsync(modelBindingContext) &&
|
||||||
|
modelBindingContext.IsModelSet)
|
||||||
{
|
{
|
||||||
arguments[parameter.PropertyName] = modelBindingContext.Model;
|
arguments[parameter.PropertyName] = modelBindingContext.Model;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
propertyMetadata);
|
propertyMetadata);
|
||||||
|
|
||||||
// bind and propagate the values
|
// bind and propagate the values
|
||||||
// If we can't bind, then leave the result missing (don't add a null).
|
// If we can't bind then leave the result missing (don't add a null).
|
||||||
if (await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
|
if (await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(propertyBindingContext))
|
||||||
{
|
{
|
||||||
var result = new ComplexModelDtoResult(propertyBindingContext.Model,
|
var result = ComplexModelDtoResult.FromBindingContext(propertyBindingContext);
|
||||||
propertyBindingContext.ValidationNode);
|
|
||||||
dto.Results[propertyMetadata] = result;
|
dto.Results[propertyMetadata] = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,25 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public sealed class ComplexModelDtoResult
|
public sealed class ComplexModelDtoResult
|
||||||
{
|
{
|
||||||
public ComplexModelDtoResult(object model,
|
public static ComplexModelDtoResult FromBindingContext([NotNull] ModelBindingContext context)
|
||||||
[NotNull] ModelValidationNode validationNode)
|
{
|
||||||
|
return new ComplexModelDtoResult(context.Model, context.IsModelSet, context.ValidationNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComplexModelDtoResult(
|
||||||
|
object model,
|
||||||
|
bool isModelBound,
|
||||||
|
[NotNull] ModelValidationNode validationNode)
|
||||||
{
|
{
|
||||||
Model = model;
|
Model = model;
|
||||||
|
IsModelBound = isModelBound;
|
||||||
ValidationNode = validationNode;
|
ValidationNode = validationNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Model { get; private set; }
|
public bool IsModelBound { get; }
|
||||||
|
|
||||||
public ModelValidationNode ValidationNode { get; private set; }
|
public object Model { get; set; }
|
||||||
|
|
||||||
|
public ModelValidationNode ValidationNode { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -291,10 +291,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static object GetPropertyDefaultValue(PropertyInfo propertyInfo)
|
private static bool TryGetPropertyDefaultValue(PropertyInfo propertyInfo, out object value)
|
||||||
{
|
{
|
||||||
var attr = propertyInfo.GetCustomAttribute<DefaultValueAttribute>();
|
var attribute = propertyInfo.GetCustomAttribute<DefaultValueAttribute>();
|
||||||
return (attr != null) ? attr.Value : null;
|
if (attribute == null)
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = attribute.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static PropertyValidationInfo GetPropertyValidationInfo(ModelBindingContext bindingContext)
|
internal static PropertyValidationInfo GetPropertyValidationInfo(ModelBindingContext bindingContext)
|
||||||
|
|
@ -345,7 +354,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var validationInfo = GetPropertyValidationInfo(bindingContext);
|
var validationInfo = GetPropertyValidationInfo(bindingContext);
|
||||||
|
|
||||||
// Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
|
// Eliminate provided properties from requiredProperties; leaving just *missing* required properties.
|
||||||
validationInfo.RequiredProperties.ExceptWith(dto.Results.Select(r => r.Key.PropertyName));
|
var boundProperties = dto.Results.Where(p => p.Value.IsModelBound).Select(p => p.Key.PropertyName);
|
||||||
|
validationInfo.RequiredProperties.ExceptWith(boundProperties);
|
||||||
|
|
||||||
foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
|
foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
|
||||||
{
|
{
|
||||||
|
|
@ -407,7 +417,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = dtoResult.Model ?? GetPropertyDefaultValue(property);
|
object value;
|
||||||
|
var hasDefaultValue = false;
|
||||||
|
if (dtoResult.IsModelBound)
|
||||||
|
{
|
||||||
|
value = dtoResult.Model;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hasDefaultValue = TryGetPropertyDefaultValue(property, out value);
|
||||||
|
}
|
||||||
|
|
||||||
propertyMetadata.Model = value;
|
propertyMetadata.Model = value;
|
||||||
|
|
||||||
// 'Required' validators need to run first so that we can provide useful error messages if
|
// 'Required' validators need to run first so that we can provide useful error messages if
|
||||||
|
|
@ -429,6 +449,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!dtoResult.IsModelBound && !hasDefaultValue)
|
||||||
|
{
|
||||||
|
// If we don't have a value, don't set it on the model and trounce a pre-initialized
|
||||||
|
// value.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (value != null || property.PropertyType.AllowsNullValue())
|
if (value != null || property.PropertyType.AllowsNullValue())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
var binder = new Mock<IModelBinder>();
|
var binder = new Mock<IModelBinder>();
|
||||||
binder
|
binder
|
||||||
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Callback<ModelBindingContext>(c =>
|
||||||
|
{
|
||||||
|
// This value won't go into the arguments, because we return false.
|
||||||
|
c.Model = "Hello";
|
||||||
|
})
|
||||||
.Returns(Task.FromResult(result: false));
|
.Returns(Task.FromResult(result: false));
|
||||||
|
|
||||||
var actionContext = new ActionContext(
|
var actionContext = new ActionContext(
|
||||||
|
|
@ -211,6 +216,59 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
Assert.Empty(result);
|
Assert.Empty(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetActionArgumentsAsync_DoesNotAddActionArguments_IfBinderDoesNotSetModel()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
Func<object, int> method = foo => 1;
|
||||||
|
var actionDescriptor = new ControllerActionDescriptor
|
||||||
|
{
|
||||||
|
MethodInfo = method.Method,
|
||||||
|
Parameters = new List<ParameterDescriptor>
|
||||||
|
{
|
||||||
|
new ParameterDescriptor
|
||||||
|
{
|
||||||
|
Name = "foo",
|
||||||
|
ParameterType = typeof(object),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var binder = new Mock<IModelBinder>();
|
||||||
|
binder
|
||||||
|
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Callback<ModelBindingContext>(c =>
|
||||||
|
{
|
||||||
|
Assert.False(c.IsModelSet);
|
||||||
|
})
|
||||||
|
.Returns(Task.FromResult(result: true));
|
||||||
|
|
||||||
|
var actionContext = new ActionContext(
|
||||||
|
new RouteContext(Mock.Of<HttpContext>()),
|
||||||
|
actionDescriptor)
|
||||||
|
{
|
||||||
|
Controller = Mock.Of<object>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var actionBindingContext = new ActionBindingContext()
|
||||||
|
{
|
||||||
|
ModelBinder = binder.Object,
|
||||||
|
};
|
||||||
|
|
||||||
|
var inputFormattersProvider = new Mock<IInputFormattersProvider>();
|
||||||
|
inputFormattersProvider
|
||||||
|
.SetupGet(o => o.InputFormatters)
|
||||||
|
.Returns(new List<IInputFormatter>());
|
||||||
|
|
||||||
|
var invoker = new DefaultControllerActionArgumentBinder(new DataAnnotationsModelMetadataProvider());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await invoker.GetActionArgumentsAsync(actionContext, actionBindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(result);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task GetActionArgumentsAsync_AddsActionArguments_IfBinderReturnsTrue()
|
public async Task GetActionArgumentsAsync_AddsActionArguments_IfBinderReturnsTrue()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -20,5 +20,5 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
Tracked by
|
Tracked by default-tracking-id
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,36 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Empty(result.ModelStateErrors);
|
Assert.Empty(result.ModelStateErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There should be no model state error for a top-level object
|
||||||
|
[Theory]
|
||||||
|
[InlineData("transactionId1234", "1e331f25-0869-4c87-8a94-64e6e40cb5a0")]
|
||||||
|
public async Task FromHeader_BindHeader_ToString_OnParameter_NoValues_DefaultValue(
|
||||||
|
string headerName,
|
||||||
|
string headerValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(
|
||||||
|
HttpMethod.Get,
|
||||||
|
"http://localhost/Blog/BindToStringParameterDefaultValue");
|
||||||
|
// Intentionally not setting a header value
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<Result>(body);
|
||||||
|
|
||||||
|
Assert.Equal("default-value", result.HeaderValue);
|
||||||
|
Assert.Null(result.HeaderValues);
|
||||||
|
Assert.Empty(result.ModelStateErrors);
|
||||||
|
}
|
||||||
|
|
||||||
// The action that this test hits will echo back the model-bound values
|
// The action that this test hits will echo back the model-bound values
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("transactionIds", "1e331f25-0869-4c87-8a94-64e6e40cb5a0")]
|
[InlineData("transactionIds", "1e331f25-0869-4c87-8a94-64e6e40cb5a0")]
|
||||||
|
|
@ -187,6 +217,70 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal("Title", error);
|
Assert.Equal("Title", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This model sets a value for 'Title', and the model binder won't trounce it.
|
||||||
|
//
|
||||||
|
// There's no validation error because we validate the initialized value.
|
||||||
|
[Fact]
|
||||||
|
public async Task FromHeader_BindHeader_ToModel_NoValues_InitializedValue_ValidationError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(
|
||||||
|
HttpMethod.Get,
|
||||||
|
"http://localhost/Blog/BindToModelWithInitializedValue?author=Marvin");
|
||||||
|
|
||||||
|
// Intentionally not setting a title or tags
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<Result>(body);
|
||||||
|
|
||||||
|
Assert.Equal("How to Make Soup", result.HeaderValue);
|
||||||
|
Assert.Equal<string>(new[] { "Cooking" }, result.HeaderValues);
|
||||||
|
|
||||||
|
var error = Assert.Single(result.ModelStateErrors);
|
||||||
|
Assert.Equal("Title", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This model uses default value for 'Title'.
|
||||||
|
//
|
||||||
|
// There's no validation error because we validate the default value.
|
||||||
|
[Fact]
|
||||||
|
public async Task FromHeader_BindHeader_ToModel_NoValues_DefaultValue_NoValidationError()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(
|
||||||
|
HttpMethod.Get,
|
||||||
|
"http://localhost/Blog/BindToModelWithDefaultValue?author=Marvin");
|
||||||
|
|
||||||
|
// Intentionally not setting a title or tags
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
|
||||||
|
var body = await response.Content.ReadAsStringAsync();
|
||||||
|
var result = JsonConvert.DeserializeObject<Result>(body);
|
||||||
|
|
||||||
|
Assert.Equal("How to Make Soup", result.HeaderValue);
|
||||||
|
Assert.Equal<string>(new[] { "Cooking" }, result.HeaderValues);
|
||||||
|
|
||||||
|
var error = Assert.Single(result.ModelStateErrors);
|
||||||
|
Assert.Equal("Title", error);
|
||||||
|
}
|
||||||
|
|
||||||
private class Result
|
private class Result
|
||||||
{
|
{
|
||||||
public string HeaderValue { get; set; }
|
public string HeaderValue { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var validationNode = GetValidationNode();
|
var validationNode = GetValidationNode();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = new ComplexModelDtoResult("some string", validationNode);
|
var result = new ComplexModelDtoResult(
|
||||||
|
"some string",
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("some string", result.Model);
|
Assert.Equal("some string", result.Model);
|
||||||
|
Assert.True(result.IsModelBound);
|
||||||
Assert.Equal(validationNode, result.ValidationNode);
|
Assert.Equal(validationNode, result.ValidationNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -583,7 +583,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
"ValueTypeRequired",
|
"ValueTypeRequired",
|
||||||
"FirstName",
|
"FirstName",
|
||||||
"LastName",
|
"LastName",
|
||||||
"PropertyWithDefaultValue"
|
"PropertyWithDefaultValue",
|
||||||
|
"PropertyWithInitializedValue",
|
||||||
|
"PropertyWithInitializedValueAndDefault",
|
||||||
};
|
};
|
||||||
var bindingContext = new ModelBindingContext
|
var bindingContext = new ModelBindingContext
|
||||||
{
|
{
|
||||||
|
|
@ -751,7 +753,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
|
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
|
||||||
|
|
||||||
var nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
|
var nameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
|
||||||
dto.Results[nameProperty] = new ComplexModelDtoResult("John Doe", new ModelValidationNode(nameProperty, ""));
|
dto.Results[nameProperty] = new ComplexModelDtoResult(
|
||||||
|
"John Doe",
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(nameProperty, ""));
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
|
|
@ -805,14 +810,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
var propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
|
var propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Name");
|
||||||
dto.Results[propertyMetadata] =
|
dto.Results[propertyMetadata] = new ComplexModelDtoResult(
|
||||||
new ComplexModelDtoResult("John Doe", new ModelValidationNode(propertyMetadata, "theModel.Name"));
|
"John Doe",
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(propertyMetadata, "theModel.Name"));
|
||||||
|
|
||||||
// Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this
|
// Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this
|
||||||
// case because the binding exists.
|
// case because the binding exists.
|
||||||
propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Age");
|
propertyMetadata = dto.PropertyMetadata.Single(o => o.PropertyName == "Age");
|
||||||
dto.Results[propertyMetadata] =
|
dto.Results[propertyMetadata] = new ComplexModelDtoResult(
|
||||||
new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.Age"));
|
null,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(propertyMetadata, "theModel.Age"));
|
||||||
|
|
||||||
// Act; must also Validate because null-check error handler is late-bound
|
// Act; must also Validate because null-check error handler is late-bound
|
||||||
testableBinder.ProcessDto(bindingContext, dto);
|
testableBinder.ProcessDto(bindingContext, dto);
|
||||||
|
|
@ -894,11 +903,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
// Make Age valid and City invalid.
|
// Make Age valid and City invalid.
|
||||||
var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "Age");
|
var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "Age");
|
||||||
dto.Results[propertyMetadata] =
|
dto.Results[propertyMetadata] = new ComplexModelDtoResult(
|
||||||
new ComplexModelDtoResult(23, new ModelValidationNode(propertyMetadata, "theModel.Age"));
|
23,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(propertyMetadata, "theModel.Age"));
|
||||||
|
|
||||||
propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "City");
|
propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "City");
|
||||||
dto.Results[propertyMetadata] =
|
dto.Results[propertyMetadata] = new ComplexModelDtoResult(
|
||||||
new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.City"));
|
null,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(propertyMetadata, "theModel.City"));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessDto(bindingContext, dto);
|
testableBinder.ProcessDto(bindingContext, dto);
|
||||||
|
|
@ -963,8 +977,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
// Make ValueTypeRequired invalid.
|
// Make ValueTypeRequired invalid.
|
||||||
var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "ValueTypeRequired");
|
var propertyMetadata = dto.PropertyMetadata.Single(p => p.PropertyName == "ValueTypeRequired");
|
||||||
dto.Results[propertyMetadata] =
|
dto.Results[propertyMetadata] = new ComplexModelDtoResult(
|
||||||
new ComplexModelDtoResult(null, new ModelValidationNode(propertyMetadata, "theModel.ValueTypeRequired"));
|
null,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(propertyMetadata, "theModel.ValueTypeRequired"));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessDto(bindingContext, dto);
|
testableBinder.ProcessDto(bindingContext, dto);
|
||||||
|
|
@ -999,9 +1015,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
|
var dto = new ComplexModelDto(containerMetadata, containerMetadata.Properties);
|
||||||
|
|
||||||
var firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");
|
var firstNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "FirstName");
|
||||||
dto.Results[firstNameProperty] = new ComplexModelDtoResult("John", new ModelValidationNode(firstNameProperty, ""));
|
dto.Results[firstNameProperty] = new ComplexModelDtoResult(
|
||||||
|
"John",
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(firstNameProperty, ""));
|
||||||
|
|
||||||
var lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName");
|
var lastNameProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "LastName");
|
||||||
dto.Results[lastNameProperty] = new ComplexModelDtoResult("Doe", new ModelValidationNode(lastNameProperty, ""));
|
dto.Results[lastNameProperty] = new ComplexModelDtoResult(
|
||||||
|
"Doe",
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: new ModelValidationNode(lastNameProperty, ""));
|
||||||
|
|
||||||
var dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth");
|
var dobProperty = dto.PropertyMetadata.Single(o => o.PropertyName == "DateOfBirth");
|
||||||
dto.Results[dobProperty] = null;
|
dto.Results[dobProperty] = null;
|
||||||
|
|
||||||
|
|
@ -1025,7 +1049,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.First(o => o.PropertyName == "PropertyWithDefaultValue");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.First(o => o.PropertyName == "PropertyWithDefaultValue");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||||
var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode);
|
|
||||||
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
model: null,
|
||||||
|
isModelBound: false,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var requiredValidator = bindingContext.OperationBindingContext
|
var requiredValidator = bindingContext.OperationBindingContext
|
||||||
.ValidatorProvider
|
.ValidatorProvider
|
||||||
.GetValidators(propertyMetadata)
|
.GetValidators(propertyMetadata)
|
||||||
|
|
@ -1034,7 +1063,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var person = Assert.IsType<Person>(bindingContext.Model);
|
var person = Assert.IsType<Person>(bindingContext.Model);
|
||||||
|
|
@ -1042,6 +1071,60 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.True(bindingContext.ModelState.IsValid);
|
Assert.True(bindingContext.ModelState.IsValid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetProperty_PropertyIsPreinitialized_NoValue_DoesNothing()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
||||||
|
|
||||||
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
|
||||||
|
o => o.PropertyName == "PropertyWithInitializedValue");
|
||||||
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||||
|
|
||||||
|
// This value won't be used because IsModelBound = false.
|
||||||
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
model: "bad-value",
|
||||||
|
isModelBound: false,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var person = Assert.IsType<Person>(bindingContext.Model);
|
||||||
|
Assert.Equal("preinitialized", person.PropertyWithInitializedValue);
|
||||||
|
Assert.True(bindingContext.ModelState.IsValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetProperty_PropertyIsPreinitialized_WithDefaultValue_NoValue_CallsSetter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
||||||
|
|
||||||
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(
|
||||||
|
o => o.PropertyName == "PropertyWithInitializedValueAndDefault");
|
||||||
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||||
|
|
||||||
|
// This value won't be used because IsModelBound = false.
|
||||||
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
model: "bad-value",
|
||||||
|
isModelBound: false,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var person = Assert.IsType<Person>(bindingContext.Model);
|
||||||
|
Assert.Equal("default", person.PropertyWithInitializedValueAndDefault);
|
||||||
|
Assert.True(bindingContext.ModelState.IsValid);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SetProperty_PropertyIsReadOnly_DoesNothing()
|
public void SetProperty_PropertyIsReadOnly_DoesNothing()
|
||||||
{
|
{
|
||||||
|
|
@ -1049,12 +1132,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NonUpdateableProperty");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NonUpdateableProperty");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||||
var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode);
|
|
||||||
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
model: null,
|
||||||
|
isModelBound: false,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// If didn't throw, success!
|
// If didn't throw, success!
|
||||||
|
|
@ -1069,7 +1156,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||||
var dtoResult = new ComplexModelDtoResult(new DateTime(2001, 1, 1), validationNode);
|
|
||||||
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
new DateTime(2001, 1, 1),
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var requiredValidator = bindingContext.OperationBindingContext
|
var requiredValidator = bindingContext.OperationBindingContext
|
||||||
.ValidatorProvider
|
.ValidatorProvider
|
||||||
.GetValidators(propertyMetadata)
|
.GetValidators(propertyMetadata)
|
||||||
|
|
@ -1079,7 +1171,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
validationNode.Validate(validationContext);
|
validationNode.Validate(validationContext);
|
||||||
|
|
@ -1100,12 +1192,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfDeath");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfDeath");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||||
var dtoResult = new ComplexModelDtoResult(new DateTime(1800, 1, 1), validationNode);
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
new DateTime(1800, 1, 1),
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator: null);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine
|
Assert.Equal("Date of death can't be before date of birth." + Environment.NewLine
|
||||||
|
|
@ -1118,16 +1213,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "DateOfBirth");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo");
|
||||||
var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode);
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
model: null,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
||||||
var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);
|
var validationContext = new ModelValidationContext(bindingContext, propertyMetadata);
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(bindingContext.ModelState.IsValid);
|
Assert.True(bindingContext.ModelState.IsValid);
|
||||||
|
|
@ -1141,15 +1241,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Arrange
|
// Arrange
|
||||||
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
var bindingContext = CreateContext(GetMetadataForObject(new Person()));
|
||||||
bindingContext.ModelName = " foo";
|
bindingContext.ModelName = " foo";
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "ValueTypeRequired");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "ValueTypeRequired");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo.ValueTypeRequired");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo.ValueTypeRequired");
|
||||||
var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode);
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
model: null,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(bindingContext.ModelState.IsValid);
|
Assert.False(bindingContext.ModelState.IsValid);
|
||||||
|
|
@ -1163,15 +1268,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Arrange
|
// Arrange
|
||||||
var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows()));
|
var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows()));
|
||||||
bindingContext.ModelName = "foo";
|
bindingContext.ModelName = "foo";
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NameNoAttribute");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "NameNoAttribute");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo.NameNoAttribute");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo.NameNoAttribute");
|
||||||
var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode);
|
var dtoResult = new ComplexModelDtoResult(
|
||||||
|
model: null,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(bindingContext.ModelState.IsValid);
|
Assert.False(bindingContext.ModelState.IsValid);
|
||||||
|
|
@ -1187,15 +1297,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Arrange
|
// Arrange
|
||||||
var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows()));
|
var bindingContext = CreateContext(GetMetadataForObject(new ModelWhosePropertySetterThrows()));
|
||||||
bindingContext.ModelName = "foo";
|
bindingContext.ModelName = "foo";
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "Name");
|
var propertyMetadata = bindingContext.ModelMetadata.Properties.Single(o => o.PropertyName == "Name");
|
||||||
var validationNode = new ModelValidationNode(propertyMetadata, "foo.Name");
|
var validationNode = new ModelValidationNode(propertyMetadata, "foo.Name");
|
||||||
var dtoResult = new ComplexModelDtoResult(model: null, validationNode: validationNode);
|
var dtoResult = new ComplexModelDtoResult(model: null,
|
||||||
|
isModelBound: true,
|
||||||
|
validationNode: validationNode);
|
||||||
|
|
||||||
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
var requiredValidator = GetRequiredValidator(bindingContext, propertyMetadata);
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
testableBinder.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(bindingContext.ModelState.IsValid);
|
Assert.False(bindingContext.ModelState.IsValid);
|
||||||
|
|
@ -1290,6 +1404,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
[DefaultValue(typeof(decimal), "123.456")]
|
[DefaultValue(typeof(decimal), "123.456")]
|
||||||
public decimal PropertyWithDefaultValue { get; set; }
|
public decimal PropertyWithDefaultValue { get; set; }
|
||||||
|
|
||||||
|
public string PropertyWithInitializedValue { get; set; } = "preinitialized";
|
||||||
|
|
||||||
|
[DefaultValue("default")]
|
||||||
|
public string PropertyWithInitializedValueAndDefault { get; set; } = "preinitialized";
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PersonWithBindExclusion
|
private class PersonWithBindExclusion
|
||||||
|
|
@ -1497,20 +1616,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return base.GetMetadataForProperties(bindingContext);
|
return base.GetMetadataForProperties(bindingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void SetPropertyPublic(ModelBindingContext bindingContext,
|
public new void SetProperty(ModelBindingContext bindingContext,
|
||||||
ModelMetadata propertyMetadata,
|
|
||||||
ComplexModelDtoResult dtoResult,
|
|
||||||
IModelValidator requiredValidator)
|
|
||||||
{
|
|
||||||
base.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void SetProperty(ModelBindingContext bindingContext,
|
|
||||||
ModelMetadata propertyMetadata,
|
ModelMetadata propertyMetadata,
|
||||||
ComplexModelDtoResult dtoResult,
|
ComplexModelDtoResult dtoResult,
|
||||||
IModelValidator requiredValidator)
|
IModelValidator requiredValidator)
|
||||||
{
|
{
|
||||||
SetPropertyPublic(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
base.SetProperty(bindingContext, propertyMetadata, dtoResult, requiredValidator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,19 @@ namespace FiltersWebSite
|
||||||
|
|
||||||
public override void OnActionExecuting(ActionExecutingContext context)
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
{
|
{
|
||||||
if (context.ActionArguments["fromGlobalActionFilter"] == null)
|
object obj;
|
||||||
|
List<ContentResult> filters;
|
||||||
|
|
||||||
|
if (context.ActionArguments.TryGetValue("fromGlobalActionFilter", out obj))
|
||||||
{
|
{
|
||||||
context.ActionArguments["fromGlobalActionFilter"] = new List<ContentResult>();
|
filters = (List<ContentResult>)obj;
|
||||||
}
|
}
|
||||||
(context.ActionArguments["fromGlobalActionFilter"] as List<ContentResult>)
|
{
|
||||||
.Add(Helpers.GetContentResult(context.Result, "Controller override - OnActionExecuting"));
|
filters = new List<ContentResult>();
|
||||||
|
context.ActionArguments.Add("fromGlobalActionFilter", filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
filters.Add(Helpers.GetContentResult(context.Result, "Controller override - OnActionExecuting"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnActionExecuted(ActionExecutedContext context)
|
public override void OnActionExecuted(ActionExecutedContext context)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// 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.
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
@ -21,6 +22,17 @@ namespace ModelBindingWebSite.Controllers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Echo back the header value
|
||||||
|
[HttpGet("BindToStringParameterDefaultValue")]
|
||||||
|
public object BindToStringParameterDefaultValue([FromHeader] string transactionId = "default-value")
|
||||||
|
{
|
||||||
|
return new Result()
|
||||||
|
{
|
||||||
|
HeaderValue = transactionId,
|
||||||
|
ModelStateErrors = ModelState.Where(kvp => kvp.Value.Errors.Count > 0).Select(kvp => kvp.Key).ToArray(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Echo back the header values
|
// Echo back the header values
|
||||||
[HttpGet("BindToStringArrayParameter")]
|
[HttpGet("BindToStringArrayParameter")]
|
||||||
public object BindToStringArrayParameter([FromHeader] string[] transactionIds)
|
public object BindToStringArrayParameter([FromHeader] string[] transactionIds)
|
||||||
|
|
@ -53,6 +65,29 @@ namespace ModelBindingWebSite.Controllers
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("BindToModelWithInitializedValue")]
|
||||||
|
public object BindToModelWithInitializedValue(BlogPostWithInitializedValue blogPost)
|
||||||
|
{
|
||||||
|
return new Result()
|
||||||
|
{
|
||||||
|
HeaderValue = blogPost.Title,
|
||||||
|
HeaderValues = blogPost.Tags,
|
||||||
|
ModelStateErrors = ModelState.Where(kvp => kvp.Value.Errors.Count > 0).Select(kvp => kvp.Key).ToArray(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("BindToModelWithDefaultValue")]
|
||||||
|
public object BindToModelWithDefaultValue(BlogPostWithDefaultValue blogPost)
|
||||||
|
{
|
||||||
|
return new Result()
|
||||||
|
{
|
||||||
|
HeaderValue = blogPost.Title,
|
||||||
|
HeaderValues = blogPost.Tags,
|
||||||
|
ModelStateErrors = ModelState.Where(kvp => kvp.Value.Errors.Count > 0).Select(kvp => kvp.Key).ToArray(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private class Result
|
private class Result
|
||||||
{
|
{
|
||||||
public string HeaderValue { get; set; }
|
public string HeaderValue { get; set; }
|
||||||
|
|
@ -73,5 +108,31 @@ namespace ModelBindingWebSite.Controllers
|
||||||
|
|
||||||
public string Author { get; set; }
|
public string Author { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class BlogPostWithInitializedValue
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[FromHeader]
|
||||||
|
public string Title { get; set; } = "How to Make Soup";
|
||||||
|
|
||||||
|
[FromHeader]
|
||||||
|
public string[] Tags { get; set; } = new string[] { "Cooking" };
|
||||||
|
|
||||||
|
public string Author { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BlogPostWithDefaultValue
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[FromHeader]
|
||||||
|
[DefaultValue("How to Make Soup")]
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
[FromHeader]
|
||||||
|
[DefaultValue(new string[] { "Cooking" })]
|
||||||
|
public string[] Tags { get; set; }
|
||||||
|
|
||||||
|
public string Author { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue