Fix for #563 - FromForm, FromQuery and FromValue allow providing a Name which is used as a prefix. Also the name is used for reporting model state errors.
This commit is contained in:
parent
1cb170ce02
commit
85b6382c69
|
|
@ -10,9 +10,12 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// Specifies that a parameter or property should be bound using form-data in the request body.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class FromFormAttribute : Attribute, IBindingSourceMetadata
|
||||
public class FromFormAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public BindingSource BindingSource { get { return BindingSource.Form; } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// Specifies that a parameter or property should be bound using the request query string.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class FromQueryAttribute : Attribute, IBindingSourceMetadata
|
||||
public class FromQueryAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public BindingSource BindingSource { get { return BindingSource.Query; } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@ namespace Microsoft.AspNet.Mvc
|
|||
/// Specifies that a parameter or property should be bound using route-data from the current request.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class FromRouteAttribute : Attribute, IBindingSourceMetadata
|
||||
public class FromRouteAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public BindingSource BindingSource { get { return BindingSource.Path; } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var dto = (ComplexModelDto)bindingContext.ModelMetadata.Model;
|
||||
foreach (var propertyMetadata in dto.PropertyMetadata)
|
||||
{
|
||||
var propertyModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName,
|
||||
propertyMetadata.PropertyName);
|
||||
var propertyModelName = ModelBindingHelper.CreatePropertyModelName(
|
||||
bindingContext.ModelName,
|
||||
propertyMetadata.BinderModelName ?? propertyMetadata.PropertyName);
|
||||
|
||||
var propertyBindingContext = new ModelBindingContext(bindingContext,
|
||||
propertyModelName,
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// to just those that match. We can skip filtering when IsGreedy == true, because that can't use
|
||||
// value providers.
|
||||
//
|
||||
// We also want to base this filtering on the - top-level value profider in case the data source
|
||||
// We also want to base this filtering on the - top-level value provider in case the data source
|
||||
// on this property doesn't intersect with the ambient data source.
|
||||
//
|
||||
// Ex:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
|||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IModelBinder"/> which binds models from the request headers when a model
|
||||
/// An <see cref="IModelBinder"/> which binds models from the request headers when a model
|
||||
/// has the binding source <see cref="BindingSource.Header"/>/
|
||||
/// </summary>
|
||||
public class HeaderModelBinder : BindingSourceModelBinder
|
||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var request = bindingContext.OperationBindingContext.HttpContext.Request;
|
||||
var modelMetadata = bindingContext.ModelMetadata;
|
||||
|
||||
// Property name can be null if the model metadata represents a type (rahter than a property or parameter).
|
||||
// Property name can be null if the model metadata represents a type (rather than a property or parameter).
|
||||
var headerName = modelMetadata.BinderModelName ?? modelMetadata.PropertyName ?? bindingContext.ModelName;
|
||||
object model = null;
|
||||
if (bindingContext.ModelType == typeof(string))
|
||||
|
|
|
|||
|
|
@ -176,8 +176,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
var propertyModelName = ModelBindingHelper.CreatePropertyModelName(bindingContext.ModelName,
|
||||
metadata.PropertyName);
|
||||
var propertyModelName = ModelBindingHelper.CreatePropertyModelName(
|
||||
bindingContext.ModelName,
|
||||
metadata.BinderModelName ?? metadata.PropertyName);
|
||||
|
||||
if (await valueProvider.ContainsPrefixAsync(propertyModelName))
|
||||
{
|
||||
|
|
@ -357,14 +358,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
foreach (var missingRequiredProperty in validationInfo.RequiredProperties)
|
||||
{
|
||||
var addedError = false;
|
||||
var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
|
||||
bindingContext.ModelName, missingRequiredProperty);
|
||||
|
||||
// Update Model as SetProperty() would: Place null value where validator will check for non-null. This
|
||||
// ensures a failure result from a required validator (if any) even for a non-nullable property.
|
||||
// (Otherwise, propertyMetadata.Model is likely already null.)
|
||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[missingRequiredProperty];
|
||||
propertyMetadata.Model = null;
|
||||
var propertyName = propertyMetadata.BinderModelName ?? missingRequiredProperty;
|
||||
var modelStateKey = ModelBindingHelper.CreatePropertyModelName(
|
||||
bindingContext.ModelName,
|
||||
propertyName);
|
||||
|
||||
// Execute validator (if any) to get custom error message.
|
||||
IModelValidator validator;
|
||||
|
|
@ -379,7 +382,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
bindingContext.ModelState.TryAddModelError(
|
||||
modelStateKey,
|
||||
Resources.FormatMissingRequiredMember(missingRequiredProperty));
|
||||
Resources.FormatMissingRequiredMember(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -156,7 +156,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
foreach (var childMetadata in metadata.Properties)
|
||||
{
|
||||
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, childMetadata.PropertyName);
|
||||
var propertyName = childMetadata.BinderModelName ?? childMetadata.PropertyName;
|
||||
var childKey = ModelBindingHelper.CreatePropertyModelName(currentModelKey, propertyName);
|
||||
if (!ValidateNonVisitedNodeAndChildren(childKey, childMetadata, validationContext, validators: null))
|
||||
{
|
||||
isValid = false;
|
||||
|
|
|
|||
|
|
@ -1,11 +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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
// 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.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using ModelBindingWebSite;
|
||||
using ModelBindingWebSite.Controllers;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
public class ModelBindingFromFormTest
|
||||
{
|
||||
private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(ModelBindingWebSite));
|
||||
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
||||
|
||||
[Fact]
|
||||
public async Task FromForm_CustomModelPrefix_ForParameter()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url = "http://localhost/FromFormAttribute_Company/CreateCompany";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string,string>("customPrefix.Employees[0].Name", "somename"),
|
||||
};
|
||||
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||
|
||||
var employee = Assert.Single(company.Employees);
|
||||
|
||||
Assert.Equal("somename", employee.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromForm_CustomModelPrefix_ForCollectionParameter()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url = "http://localhost/FromFormAttribute_Company/CreateCompanyFromEmployees";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("customPrefix[0].Department", "Contoso"),
|
||||
};
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||
|
||||
var employee = Assert.Single(company.Employees);
|
||||
Assert.Equal("Contoso", employee.Department);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromForm_CustomModelPrefix_ForProperty()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url = "http://localhost/FromFormAttribute_Company/CreateCompany";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("customPrefix.Employees[0].EmployeeSSN", "123132131"),
|
||||
};
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||
|
||||
var employee = Assert.Single(company.Employees);
|
||||
Assert.Equal("123132131", employee.SSN);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromForm_CustomModelPrefix_ForCollectionProperty()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url = "http://localhost/FromFormAttribute_Company/CreateDepartment";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>("department.TestEmployees[0].EmployeeSSN", "123132131"),
|
||||
};
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var department = JsonConvert.DeserializeObject<
|
||||
FromFormAttribute_CompanyController.FromForm_Department>(body);
|
||||
|
||||
var employee = Assert.Single(department.Employees);
|
||||
Assert.Equal("123132131", employee.SSN);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromForm_NonExistingValueAddsValidationErrors_OnProperty_UsingCustomModelPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url = "http://localhost/FromFormAttribute_Company/ValidateDepartment";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
|
||||
// No values.
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>();
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<Result>(body);
|
||||
Assert.Null(result.Value);
|
||||
var error = Assert.Single(result.ModelStateErrors);
|
||||
Assert.Equal("TestEmployees", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -93,8 +93,32 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
var result = JsonConvert.DeserializeObject<Result>(body);
|
||||
Assert.Equal<string>(tags, result.HeaderValues);
|
||||
var error = Assert.Single(result.ModelStateErrors);
|
||||
Assert.Equal("Title", error);
|
||||
Assert.Equal("BlogTitle", error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromHeader_NonExistingHeaderAddsValidationErrors_OnCollectionProperty_CustomName()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/Blog/BindToProperty/CustomName");
|
||||
request.Headers.TryAddWithoutValidation("BlogTitle", "Cooking Receipes.");
|
||||
|
||||
// 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<string>("Cooking Receipes.", result.HeaderValue);
|
||||
var error = Assert.Single(result.ModelStateErrors);
|
||||
Assert.Equal("BlogTags", error);
|
||||
}
|
||||
|
||||
// The action that this test hits will echo back the model-bound value
|
||||
[Fact]
|
||||
public async Task FromHeader_BindHeader_ToString_OnParameter_CustomName()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,130 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using ModelBindingWebSite;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
using ModelBindingWebSite.Controllers;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
public class ModelBindingFromQueryTest
|
||||
{
|
||||
private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(ModelBindingWebSite));
|
||||
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
||||
|
||||
[Fact]
|
||||
public async Task FromQuery_CustomModelPrefix_ForParameter()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// [FromQuery(Name = "customPrefix")] is used to apply a prefix
|
||||
var url =
|
||||
"http://localhost/FromQueryAttribute_Company/CreateCompany?customPrefix.Employees[0].Name=somename";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||
|
||||
var employee = Assert.Single(company.Employees);
|
||||
Assert.Equal("somename", employee.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromQuery_CustomModelPrefix_ForCollectionParameter()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url =
|
||||
"http://localhost/FromQueryAttribute_Company/CreateCompanyFromEmployees?customPrefix[0].Name=somename";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||
|
||||
var employee = Assert.Single(company.Employees);
|
||||
Assert.Equal("somename", employee.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromQuery_CustomModelPrefix_ForProperty()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// [FromQuery(Name = "EmployeeId")] is used to apply a prefix
|
||||
var url =
|
||||
"http://localhost/FromQueryAttribute_Company/CreateCompany?customPrefix.Employees[0].EmployeeId=1234";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||
|
||||
var employee = Assert.Single(company.Employees);
|
||||
|
||||
Assert.Equal(1234, employee.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromQuery_CustomModelPrefix_ForCollectionProperty()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url =
|
||||
"http://localhost/FromQueryAttribute_Company/CreateDepartment?TestEmployees[0].EmployeeId=1234";
|
||||
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var department = JsonConvert.DeserializeObject<
|
||||
FromQueryAttribute_CompanyController.FromQuery_Department>(body);
|
||||
|
||||
var employee = Assert.Single(department.Employees);
|
||||
Assert.Equal(1234, employee.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromQuery_NonExistingValueAddsValidationErrors_OnProperty_UsingCustomModelPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url =
|
||||
"http://localhost/FromQueryAttribute_Company/ValidateDepartment?TestEmployees[0].Department=contoso";
|
||||
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<Result>(body);
|
||||
var error = Assert.Single(result.ModelStateErrors);
|
||||
Assert.Equal("TestEmployees[0].EmployeeId", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// 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.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using ModelBindingWebSite;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||
{
|
||||
public class ModelBindingFromRouteTest
|
||||
{
|
||||
private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(ModelBindingWebSite));
|
||||
private readonly Action<IApplicationBuilder> _app = new ModelBindingWebSite.Startup().Configure;
|
||||
|
||||
[Fact]
|
||||
public async Task FromRoute_CustomModelPrefix_ForParameter()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// [FromRoute(Name = "customPrefix")] is used to apply a prefix
|
||||
var url =
|
||||
"http://localhost/FromRouteAttribute_Company/CreateEmployee/somename";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var employee = JsonConvert.DeserializeObject<Employee>(body);
|
||||
Assert.Equal("somename", employee.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FromRoute_CustomModelPrefix_ForProperty()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// [FromRoute(Name = "EmployeeId")] is used to apply a prefix
|
||||
var url =
|
||||
"http://localhost/FromRouteAttribute_Company/CreateEmployee/somename/1234";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var employee = JsonConvert.DeserializeObject<Employee>(body);
|
||||
Assert.Equal(1234, employee.TaxId);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task FromRoute_NonExistingValueAddsValidationErrors_OnProperty_UsingCustomModelPrefix()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
// [FromRoute(Name = "TestEmployees")] is used to apply a prefix
|
||||
var url =
|
||||
"http://localhost/FromRouteAttribute_Company/ValidateDepartment/contoso";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
|
||||
// No values.
|
||||
var nameValueCollection = new List<KeyValuePair<string, string>>();
|
||||
request.Content = new FormUrlEncodedContent(nameValueCollection);
|
||||
|
||||
// Act
|
||||
var response = await client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<Result>(body);
|
||||
Assert.Null(result.Value);
|
||||
var error = Assert.Single(result.ModelStateErrors);
|
||||
Assert.Equal("TestEmployees", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,27 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal("somename", employee.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ModelBinderAttribute_CustomModelPrefix_OnProperty()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.CreateClient();
|
||||
|
||||
var url =
|
||||
"http://localhost/ModelBinderAttribute_Company/CreateCompany?employees[0].Alias=somealias";
|
||||
|
||||
// Act
|
||||
var response = await client.GetAsync(url);
|
||||
|
||||
// Assert
|
||||
var body = await response.Content.ReadAsStringAsync();
|
||||
var company = JsonConvert.DeserializeObject<Company>(body);
|
||||
|
||||
var employee = Assert.Single(company.Employees);
|
||||
Assert.Equal("somealias", employee.EmailAlias);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("GetBinderType_UseModelBinderOnType")]
|
||||
[InlineData("GetBinderType_UseModelBinderProviderOnType")]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
{
|
||||
[Route("FromFormAttribute_Company/[action]")]
|
||||
public class FromFormAttribute_CompanyController : Controller
|
||||
{
|
||||
public Company CreateCompany([FromForm(Name = "customPrefix")] Company company)
|
||||
{
|
||||
return company;
|
||||
}
|
||||
|
||||
public FromForm_Department CreateDepartment(FromForm_Department department)
|
||||
{
|
||||
return department;
|
||||
}
|
||||
|
||||
public Company CreateCompanyFromEmployees([FromForm(Name = "customPrefix")] IList<Employee> employees)
|
||||
{
|
||||
return new Company { Employees = employees };
|
||||
}
|
||||
|
||||
public object ValidateDepartment(FromForm_Department department)
|
||||
{
|
||||
return new Result()
|
||||
{
|
||||
Value = department.Employees,
|
||||
ModelStateErrors = ModelState.Where(kvp => kvp.Value.Errors.Count > 0).Select(kvp => kvp.Key).ToArray(),
|
||||
};
|
||||
}
|
||||
|
||||
public class FromForm_Department
|
||||
{
|
||||
[FromForm(Name = "TestEmployees")]
|
||||
[Required]
|
||||
public IEnumerable<Employee> Employees { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -128,6 +128,7 @@ namespace ModelBindingWebSite.Controllers
|
|||
public string Title { get; set; }
|
||||
|
||||
[FromHeader(Name = "BlogTags")]
|
||||
[Required]
|
||||
public string[] Tags { get; set; }
|
||||
|
||||
public string Author { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
{
|
||||
[Route("FromQueryAttribute_Company/[action]")]
|
||||
public class FromQueryAttribute_CompanyController : Controller
|
||||
{
|
||||
public Company CreateCompany([FromQuery(Name = "customPrefix")] Company company)
|
||||
{
|
||||
return company;
|
||||
}
|
||||
|
||||
public Company CreateCompanyFromEmployees([FromQuery(Name = "customPrefix")] IList<Employee> employees)
|
||||
{
|
||||
return new Company { Employees = employees };
|
||||
}
|
||||
|
||||
public FromQuery_Department CreateDepartment(FromQuery_Department department)
|
||||
{
|
||||
return department;
|
||||
}
|
||||
|
||||
public object ValidateDepartment(FromQuery_Department department)
|
||||
{
|
||||
return new Result()
|
||||
{
|
||||
Value = department.Employees,
|
||||
ModelStateErrors = ModelState.Where(kvp => kvp.Value.Errors.Count > 0).Select(kvp => kvp.Key).ToArray(),
|
||||
};
|
||||
}
|
||||
|
||||
public class FromQuery_Department
|
||||
{
|
||||
[FromQuery(Name = "TestEmployees")]
|
||||
[Required]
|
||||
public IEnumerable<Employee> Employees { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
{
|
||||
[Route("FromRouteAttribute_Company/[action]/{customPrefix.Name}")]
|
||||
public class FromRouteAttribute_CompanyController : Controller
|
||||
{
|
||||
[HttpGet("{customPrefix.EmployeeTaxId?}")]
|
||||
public Employee CreateEmployee([FromRoute(Name = "customPrefix")] Employee employee)
|
||||
{
|
||||
return employee;
|
||||
}
|
||||
|
||||
public Company CreateCompanyFromEmployees([FromRoute(Name = "customPrefix")] IList<Employee> employees)
|
||||
{
|
||||
return new Company { Employees = employees };
|
||||
}
|
||||
|
||||
public object ValidateDepartment(FromRoute_Department department)
|
||||
{
|
||||
return new Result()
|
||||
{
|
||||
Value = department.Employees,
|
||||
ModelStateErrors = ModelState.Where(kvp => kvp.Value.Errors.Count > 0).Select(kvp => kvp.Key).ToArray(),
|
||||
};
|
||||
}
|
||||
|
||||
public class FromRoute_Department
|
||||
{
|
||||
[FromRoute(Name = "TestEmployees")]
|
||||
[Required]
|
||||
public IEnumerable<Employee> Employees { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite.Controllers
|
||||
|
|
@ -13,5 +14,10 @@ namespace ModelBindingWebSite.Controllers
|
|||
{
|
||||
return company;
|
||||
}
|
||||
|
||||
public Company CreateCompany(IList<Employee> employees)
|
||||
{
|
||||
return new Company { Employees = employees };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace ModelBindingWebSite
|
||||
{
|
||||
public class Employee : Person
|
||||
|
|
@ -9,5 +12,18 @@ namespace ModelBindingWebSite
|
|||
public string Department { get; set; }
|
||||
|
||||
public string Location { get; set; }
|
||||
|
||||
[FromQuery(Name = "EmployeeId")]
|
||||
[Range(1, 10000)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[FromRoute(Name = "EmployeeTaxId")]
|
||||
public int TaxId { get; set; }
|
||||
|
||||
[FromForm(Name = "EmployeeSSN")]
|
||||
public string SSN { get; set; }
|
||||
|
||||
[ModelBinder(Name = "Alias")]
|
||||
public string EmailAlias { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 ModelBindingWebSite
|
||||
{
|
||||
public class Result
|
||||
{
|
||||
public object Value { get; set; }
|
||||
|
||||
public string[] ModelStateErrors { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue