diff --git a/Mvc.sln b/Mvc.sln index d11ab11567..3db8c17b26 100644 --- a/Mvc.sln +++ b/Mvc.sln @@ -126,6 +126,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Mvc.Common EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "XmlFormattersWebSite", "test\WebSites\XmlFormattersWebSite\XmlFormattersWebSite.kproj", "{C3123A70-41C4-4122-AD1C-D35DF8958DD7}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ValidationWebSite", "test\WebSites\ValidationWebSite\ValidationWebSite.kproj", "{87AB84B2-22C1-43C6-BB8A-1D327B446FB0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -688,6 +690,18 @@ Global {BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|Mixed Platforms.Build.0 = Release|Any CPU {BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|x86.ActiveCfg = Release|Any CPU {BDEEBE09-C0C4-433C-B0B8-8478C9776996}.Release|x86.Build.0 = Release|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|x86.ActiveCfg = Debug|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Debug|x86.Build.0 = Debug|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Any CPU.Build.0 = Release|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|x86.ActiveCfg = Release|Any CPU + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0}.Release|x86.Build.0 = Release|Any CPU {0449D6D2-BE1B-4E29-8E1B-444420802C03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0449D6D2-BE1B-4E29-8E1B-444420802C03}.Debug|Any CPU.Build.0 = Debug|Any CPU {0449D6D2-BE1B-4E29-8E1B-444420802C03}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -773,5 +787,6 @@ Global {BDEEBE09-C0C4-433C-B0B8-8478C9776996} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} {0449D6D2-BE1B-4E29-8E1B-444420802C03} = {3BA657BF-28B1-42DA-B5B0-1C4601FCF7B1} {C3123A70-41C4-4122-AD1C-D35DF8958DD7} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} + {87AB84B2-22C1-43C6-BB8A-1D327B446FB0} = {16703B76-C9F7-4C75-AE6C-53D92E308E3C} EndGlobalSection EndGlobal diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelMetadataAttributeTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelMetadataAttributeTest.cs new file mode 100644 index 0000000000..d6defd2631 --- /dev/null +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/ModelMetadataAttributeTest.cs @@ -0,0 +1,182 @@ +// 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.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.TestHost; +using Newtonsoft.Json; +using Xunit; + +namespace Microsoft.AspNet.Mvc.FunctionalTests +{ + public class ModelMetadataAttributeTest + { + private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(ValidationWebSite)); + private readonly Action _app = new ValidationWebSite.Startup().Configure; + + [Fact] + public async Task ModelMetaDataTypeAttribute_ValidBaseClass_EmptyResponseBody() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var input = "{ \"Name\": \"MVC\", \"Contact\":\"4258959019\", \"Category\":\"Technology\"," + + "\"CompanyName\":\"Microsoft\", \"Country\":\"USA\",\"Price\": 21, \"ProductDetails\": {\"Detail1\": \"d1\"," + + " \"Detail2\": \"d2\", \"Detail3\": \"d3\"}}"; + var content = new StringContent(input, Encoding.UTF8, "application/json"); + + var url = "http://localhost/ModelMetadataTypeValidation/ValidateProductViewModelIncludingMetadata"; + + // Act + var response = await client.PostAsync(url, content); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("{}", body); + } + + [Fact] + public async Task ModelMetaDataTypeAttribute_InvalidPropertiesAndSubPropertiesOnBaseClass_ReturnsErrors() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var input = "{ \"Price\": 2, \"ProductDetails\": {\"Detail1\": \"d1\"}}"; + var content = new StringContent(input, Encoding.UTF8, "application/json"); + + var url = "http://localhost/ModelMetadataTypeValidation/ValidateProductViewModelIncludingMetadata"; + + // Act + var response = await client.PostAsync(url, content); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + var json = JsonConvert.DeserializeObject>(body); + Assert.Equal(6, json.Count); + Assert.Equal("CompanyName cannot be null or empty.", json["product.CompanyName"]); + Assert.Equal("The field Price must be between 20 and 100.", json["product.Price"]); + Assert.Equal("The Category field is required.", json["product.Category"]); + Assert.Equal("The Contact Us field is required.", json["product.Contact"]); + Assert.Equal("The Detail2 field is required.", json["product.ProductDetails.Detail2"]); + Assert.Equal("The Detail3 field is required.", json["product.ProductDetails.Detail3"]); + } + + [Fact] + public async Task ModelMetaDataTypeAttribute_InvalidComplexTypePropertyOnBaseClass_ReturnsErrors() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var input = "{ \"Contact\":\"4255678765\", \"Category\":\"Technology\"," + + "\"CompanyName\":\"Microsoft\", \"Country\":\"USA\",\"Price\": 21 }"; + var content = new StringContent(input, Encoding.UTF8, "application/json"); + + var url = "http://localhost/ModelMetadataTypeValidation/ValidateProductViewModelIncludingMetadata"; + + // Act + var response = await client.PostAsync(url, content); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + var json = JsonConvert.DeserializeObject>(body); + Assert.Equal(1, json.Count); + Assert.Equal("The ProductDetails field is required.", json["product.ProductDetails"]); + } + + [Fact] + public async Task ModelMetaDataTypeAttribute_InvalidClassAttributeOnBaseClass_ReturnsErrors() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var input = "{ \"Contact\":\"4258959019\", \"Category\":\"Technology\"," + + "\"CompanyName\":\"Microsoft\", \"Country\":\"UK\",\"Price\": 21, \"ProductDetails\": {\"Detail1\": \"d1\"," + + " \"Detail2\": \"d2\", \"Detail3\": \"d3\"}}"; + + var content = new StringContent(input, Encoding.UTF8, "application/json"); + + var url = "http://localhost/ModelMetadataTypeValidation/ValidateProductViewModelIncludingMetadata"; + + // Act + var response = await client.PostAsync(url, content); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + var json = JsonConvert.DeserializeObject>(body); + Assert.Equal(1, json.Count); + Assert.Equal("Product must be made in the USA if it is not named.", json["product"]); + } + + [Fact] + public async Task ModelMetaDataTypeAttribute_ValidDerivedClass_EmptyResponseBody() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var input = "{ \"Name\": \"MVC\", \"Contact\":\"4258959019\", \"Category\":\"Technology\"," + + "\"CompanyName\":\"Microsoft\", \"Country\":\"USA\", \"Version\":\"2\"," + + "\"DatePurchased\": \"/Date(1297246301973)/\", \"Price\" : \"110\" }"; + var content = new StringContent(input, Encoding.UTF8, "application/json"); + + var url = "http://localhost/ModelMetadataTypeValidation/ValidateSoftwareViewModelIncludingMetadata"; + + // Act + var response = await client.PostAsync(url, content); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("{}", body); + } + + [Fact] + public async Task ModelMetaDataTypeAttribute_InvalidPropertiesOnDerivedClass_ReturnsErrors() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var input = "{ \"Name\": \"MVC\", \"Contact\":\"425-895-9019\", \"Category\":\"Technology\"," + + "\"CompanyName\":\"Microsoft\", \"Country\":\"USA\",\"Price\": 2}"; + var content = new StringContent(input, Encoding.UTF8, "application/json"); + + var url = "http://localhost/ModelMetadataTypeValidation/ValidateSoftwareViewModelIncludingMetadata"; + + // Act + var response = await client.PostAsync(url, content); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + var json = JsonConvert.DeserializeObject>(body); + Assert.Equal(2, json.Count); + Assert.Equal("The field Price must be between 100 and 200.", json["software.Price"]); + Assert.Equal("The field Contact must be a string with a maximum length of 10.", json["software.Contact"]); + } + + [Fact] + public async Task ModelMetaDataTypeAttribute_InvalidClassAttributeOnBaseClassProduct_ReturnsErrors() + { + // Arrange + var server = TestServer.Create(_services, _app); + var client = server.CreateClient(); + var input = "{ \"Contact\":\"4258959019\", \"Category\":\"Technology\"," + + "\"CompanyName\":\"Microsoft\", \"Country\":\"UK\",\"Version\":\"2\"," + + "\"DatePurchased\": \"/Date(1297246301973)/\", \"Price\" : \"110\" }"; + var content = new StringContent(input, Encoding.UTF8, "application/json"); + + var url = "http://localhost/ModelMetadataTypeValidation/ValidateSoftwareViewModelIncludingMetadata"; + + // Act + var response = await client.PostAsync(url, content); + + // Assert + var body = await response.Content.ReadAsStringAsync(); + var json = JsonConvert.DeserializeObject>(body); + Assert.Equal(1, json.Count); + Assert.Equal("Product must be made in the USA if it is not named.", json["software"]); + } + + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/TryValidateModelTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/TryValidateModelTest.cs index 23d9364d79..da14948530 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/TryValidateModelTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/TryValidateModelTest.cs @@ -4,6 +4,8 @@ using System; using System.Collections.Generic; using System.Net; +using System.Net.Http; +using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.TestHost; @@ -14,60 +16,76 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests { public class TryValidateModelTest { - private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(FormatterWebSite)); - private readonly Action _app = new FormatterWebSite.Startup().Configure; + private readonly IServiceProvider _services = TestHelper.CreateServices(nameof(ValidationWebSite)); + private readonly Action _app = new ValidationWebSite.Startup().Configure; [Fact] - public async Task TryValidateModel_SimpleModelInvalidProperties() + public async Task TryValidateModel_ClearParameterValidationError_ReturnsErrorsForInvalidProperties() { // Arrange var server = TestServer.Create(_services, _app); var client = server.CreateClient(); + var input = "{ \"Price\": 2, \"Contact\": \"acvrdzersaererererfdsfdsfdsfsdf\", "+ + "\"ProductDetails\": {\"Detail1\": \"d1\", \"Detail2\": \"d2\", \"Detail3\": \"d3\"}}"; + var content = new StringContent(input, Encoding.UTF8, "application/json"); + var url = + "http://localhost/ModelMetadataTypeValidation/" + + "TryValidateModelAfterClearingValidationErrorInParameter?theImpossibleString=test"; // Act - var response = await client.GetAsync("http://localhost/TryValidateModel/GetInvalidUser"); + var response = await client.PostAsync(url, content); // Assert - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var json = JsonConvert.DeserializeObject>( - await response.Content.ReadAsStringAsync()); - Assert.Equal("The field Id must be between 1 and 2000.", json["Id"][0]); - Assert.Equal( - "The field Name must be a string or array type with a minimum length of '5'.", json["Name"][0]); + var body = await response.Content.ReadAsStringAsync(); + var json = JsonConvert.DeserializeObject>(body); + Assert.Equal(8, json.Count); + Assert.Equal("CompanyName cannot be null or empty.", json["product.CompanyName"]); + Assert.Equal("The field Price must be between 20 and 100.", json["product.Price"]); + Assert.Equal("The Category field is required.", json["product.Category"]); + Assert.Equal("The field Contact Us must be a string with a maximum length of 20."+ + "The field Contact Us must match the regular expression '^[0-9]*$'.", json["product.Contact"]); + Assert.Equal("CompanyName cannot be null or empty.", json["CompanyName"]); + Assert.Equal("The field Price must be between 20 and 100.", json["Price"]); + Assert.Equal("The Category field is required.", json["Category"]); + Assert.Equal("The field Contact Us must be a string with a maximum length of 20."+ + "The field Contact Us must match the regular expression '^[0-9]*$'.", json["Contact"]); } [Fact] - public async Task TryValidateModel_DerivedModelInvalidType() + public async Task TryValidateModel_InvalidTypeOnDerivedModel_ReturnsErrors() { // Arrange var server = TestServer.Create(_services, _app); var client = server.CreateClient(); + var url = + "http://localhost/ModelMetadataTypeValidation/TryValidateModelSoftwareViewModelWithPrefix"; // Act - var response = await client.GetAsync("http://localhost/TryValidateModel/GetInvalidAdminWithPrefix"); + var response = await client.GetAsync(url); // Assert - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - - var json = JsonConvert.DeserializeObject>( - await response.Content.ReadAsStringAsync()); - Assert.Equal("AdminAccessCode property does not have the right value", json["admin"][0]); + var body = await response.Content.ReadAsStringAsync(); + var json = JsonConvert.DeserializeObject>(body); + Assert.Equal(1, json.Count); + Assert.Equal("Product must be made in the USA if it is not named.", json["software"]); } [Fact] - public async Task TryValidateModel_ValidDerivedModel() + public async Task TryValidateModel_ValidDerivedModel_ReturnsEmptyResponseBody() { // Arrange var server = TestServer.Create(_services, _app); var client = server.CreateClient(); + var url = + "http://localhost/ModelMetadataTypeValidation/TryValidateModelValidModelNoPrefix"; // Act - var response = await client.GetAsync("http://localhost/TryValidateModel/GetValidAdminWithPrefix"); + var response = await client.GetAsync(url); // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("Admin user created successfully", await response.Content.ReadAsStringAsync()); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("{}", body); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json index 3b3a5c5ee7..dfeaeca892 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/project.json @@ -32,6 +32,7 @@ "TagHelperSample.Web": "1.0.0", "TagHelpersWebSite": "1.0.0", "UrlHelperWebSite": "1.0.0", + "ValidationWebSite": "1.0.0", "ValueProvidersWebSite": "1.0.0", "VersioningWebSite": "1.0.0", "ViewComponentWebSite": "1.0.0", diff --git a/test/WebSites/FormatterWebSite/Controllers/TryValidateModelController.cs b/test/WebSites/FormatterWebSite/Controllers/TryValidateModelController.cs deleted file mode 100644 index d041232b94..0000000000 --- a/test/WebSites/FormatterWebSite/Controllers/TryValidateModelController.cs +++ /dev/null @@ -1,64 +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 Microsoft.AspNet.Mvc; - -namespace FormatterWebSite -{ - public class TryValidateModelController : Controller - { - [HttpGet] - public IActionResult GetInvalidUser() - { - var user = new User - { - Id = 0, - Name = "x" - }; - - // If ModelState.InValid is false return BadRequestOjectResult; else return empty string. - if (!TryValidateModel(user)) - { - return new BadRequestObjectResult(ModelState); - } - - return Content(string.Empty); - } - - [HttpGet] - public IActionResult GetInvalidAdminWithPrefix() - { - var admin = new Administrator() - { - Id = 1, - Name = "John Doe", - Designation = "Administrator", - AdminAccessCode = 0 - }; - if (!TryValidateModel(admin,"admin")) - { - return new BadRequestObjectResult(ModelState); - } - - return Content(string.Empty); - } - - [HttpGet] - public IActionResult GetValidAdminWithPrefix() - { - var admin = new Administrator() - { - Id = 1, - Name = "John Doe", - Designation = "Administrator", - AdminAccessCode = 1 - }; - if (!TryValidateModel(admin, "admin")) - { - return new BadRequestObjectResult(ModelState); - } - - return Content("Admin user created successfully"); - } - } -} diff --git a/test/WebSites/FormatterWebSite/Models/Administrator.cs b/test/WebSites/FormatterWebSite/Models/Administrator.cs deleted file mode 100644 index ce82dd9ded..0000000000 --- a/test/WebSites/FormatterWebSite/Models/Administrator.cs +++ /dev/null @@ -1,13 +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; - -namespace FormatterWebSite -{ - [AdminValidator] - public class Administrator : User - { - public int AdminAccessCode { get; set; } - } -} \ No newline at end of file diff --git a/test/WebSites/FormatterWebSite/Models/AdminValidator.cs b/test/WebSites/ValidationWebSite/CompanyNameAttribute.cs similarity index 59% rename from test/WebSites/FormatterWebSite/Models/AdminValidator.cs rename to test/WebSites/ValidationWebSite/CompanyNameAttribute.cs index 03ebcf4b4a..9c5ae654d7 100644 --- a/test/WebSites/FormatterWebSite/Models/AdminValidator.cs +++ b/test/WebSites/ValidationWebSite/CompanyNameAttribute.cs @@ -3,17 +3,16 @@ using System.ComponentModel.DataAnnotations; -namespace FormatterWebSite +namespace ValidationWebSite { - public class AdminValidator : ValidationAttribute + public class CompanyNameAttribute : ValidationAttribute { protected override ValidationResult IsValid(object value, ValidationContext validationContext) { - var admin = (Administrator)value; - - if (admin.AdminAccessCode != 1) + var valueString = value as string; + if (string.IsNullOrEmpty(valueString)) { - return new ValidationResult ("AdminAccessCode property does not have the right value"); + return new ValidationResult("CompanyName cannot be null or empty."); } return null; diff --git a/test/WebSites/ValidationWebSite/Controllers/ModelMetadataTypeAttributeController.cs b/test/WebSites/ValidationWebSite/Controllers/ModelMetadataTypeAttributeController.cs new file mode 100644 index 0000000000..1bc77e607f --- /dev/null +++ b/test/WebSites/ValidationWebSite/Controllers/ModelMetadataTypeAttributeController.cs @@ -0,0 +1,102 @@ +// 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.ComponentModel.DataAnnotations; +using System.Linq; +using Microsoft.AspNet.Mvc; +using ValidationWebSite.ViewModels; + +namespace ValidationWebSite.Controllers +{ + public class ModelMetadataTypeValidationController : Controller + { + [HttpPost] + public object ValidateProductViewModelIncludingMetadata([FromBody] ProductViewModel product) + { + return CreateValidationDictionary(); + } + + [HttpPost] + public object ValidateSoftwareViewModelIncludingMetadata([FromBody] SoftwareViewModel software) + { + return CreateValidationDictionary(); + } + + [HttpPost] + public object TryValidateModelAfterClearingValidationErrorInParameter( + [Required, MaxLength(3), StringLength(10, MinimumLength = 3)] string theImpossibleString, + [FromBody] ProductViewModel product) + { + // Clear ModelState entry. TryValidateModel should not add entries except those found within the + // passed model. + ModelState["theImpossibleString"].Errors.Clear(); + + TryValidateModel(product); + + return CreateValidationDictionary(); + } + + [HttpGet] + public object TryValidateModelSoftwareViewModelWithPrefix() + { + var softwareViewModel = new SoftwareViewModel + { + Category = "Technology", + CompanyName = "Microsoft", + Contact = "4258393231", + Country = "UK", + DatePurchased = new DateTime(2010, 10, 10), + Price = 110, + Version = "2" + }; + + TryValidateModel(softwareViewModel, "software"); + + return CreateValidationDictionary(); + } + + [HttpGet] + public object TryValidateModelValidModelNoPrefix() + { + var softwareViewModel = new SoftwareViewModel + { + Category = "Technology", + CompanyName = "Microsoft", + Contact = "4258393231", + Country = "USA", + DatePurchased = new DateTime(2010, 10, 10), + Name = "MVC", + Price = 110, + Version = "2" + }; + + TryValidateModel(softwareViewModel); + + return CreateValidationDictionary(); + } + + private Dictionary CreateValidationDictionary() + { + var result = new Dictionary(); + foreach (var item in ModelState) + { + var errorMessage = string.Empty; + foreach (var error in item.Value.Errors) + { + if (error != null) + { + errorMessage = errorMessage + error.ErrorMessage; + } + } + if (!string.IsNullOrEmpty(errorMessage)) + { + result.Add(item.Key, errorMessage); + } + } + + return result; + } + } +} diff --git a/test/WebSites/ValidationWebSite/Models/Product.cs b/test/WebSites/ValidationWebSite/Models/Product.cs new file mode 100644 index 0000000000..9490fe35a0 --- /dev/null +++ b/test/WebSites/ValidationWebSite/Models/Product.cs @@ -0,0 +1,30 @@ +// 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.ComponentModel.DataAnnotations; + +namespace ValidationWebSite.Models +{ + [ProductValidator] + public class Product + { + public string Name { get; set; } + + [StringLength(20)] + [RegularExpression("^[0-9]*$")] + [Display(Name = "Contact Us")] + public string Contact { get; set; } + + [Range(0, 100)] + public virtual int Price { get; set; } + + [CompanyName] + public string CompanyName { get; set; } + + public string Country { get; set; } + + [Required] + public ProductDetails ProductDetails { get; set; } + } +} \ No newline at end of file diff --git a/test/WebSites/ValidationWebSite/Models/ProductDetails.cs b/test/WebSites/ValidationWebSite/Models/ProductDetails.cs new file mode 100644 index 0000000000..aaa530f0fc --- /dev/null +++ b/test/WebSites/ValidationWebSite/Models/ProductDetails.cs @@ -0,0 +1,19 @@ +// 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.ComponentModel.DataAnnotations; + +namespace ValidationWebSite.Models +{ + public class ProductDetails + { + [Required] + public string Detail1 { get; set; } + + [Required] + public string Detail2 { get; set; } + + [Required] + public string Detail3 { get; set; } + } +} \ No newline at end of file diff --git a/test/WebSites/ValidationWebSite/Models/Software.cs b/test/WebSites/ValidationWebSite/Models/Software.cs new file mode 100644 index 0000000000..5d8cb43c90 --- /dev/null +++ b/test/WebSites/ValidationWebSite/Models/Software.cs @@ -0,0 +1,22 @@ +// 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.ComponentModel.DataAnnotations; + +namespace ValidationWebSite.Models +{ + public class Software : Product + { + public string Version { get; set; } + + [Required] + public DateTime DatePurchased { get; set; } + + [Range(100, 200)] + public override int Price { get; set; } + + [StringLength(10)] + public new string Contact { get; set; } + } +} \ No newline at end of file diff --git a/test/WebSites/ValidationWebSite/ProductValidatorAttibute.cs b/test/WebSites/ValidationWebSite/ProductValidatorAttibute.cs new file mode 100644 index 0000000000..4df016d466 --- /dev/null +++ b/test/WebSites/ValidationWebSite/ProductValidatorAttibute.cs @@ -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.ComponentModel.DataAnnotations; +using ValidationWebSite.ViewModels; + +namespace ValidationWebSite +{ + public class ProductValidatorAttribute : ValidationAttribute + { + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + var product = value as ProductViewModel; + if (product != null) + { + if (!product.Country.Equals("USA") || string.IsNullOrEmpty(product.Name)) + { + return new ValidationResult("Product must be made in the USA if it is not named."); + } + else + { + return null; + } + } + var software = value as SoftwareViewModel; + if (software != null) + { + if (!software.Country.Equals("USA") || string.IsNullOrEmpty(software.Name)) + { + return new ValidationResult("Product must be made in the USA if it is not named."); + } + else + { + return null; + } + } + + return new ValidationResult("Expected either ProductViewModel or SoftwareViewModel instance but got " + + value.GetType() + " instance"); + } + } +} \ No newline at end of file diff --git a/test/WebSites/ValidationWebSite/Startup.cs b/test/WebSites/ValidationWebSite/Startup.cs new file mode 100644 index 0000000000..71cfda98e5 --- /dev/null +++ b/test/WebSites/ValidationWebSite/Startup.cs @@ -0,0 +1,33 @@ +// 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.Builder; +using Microsoft.Framework.DependencyInjection; + +namespace ValidationWebSite +{ + public class Startup + { + public void Configure(IApplicationBuilder app) + { + var configuration = app.GetTestConfiguration(); + + // Set up application services + app.UseServices(services => + { + // Add MVC services to the services container + services.AddMvc(configuration); + }); + + app.UseErrorReporter(); + + // Add MVC to the request pipeline + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller}/{action}/{id?}"); + }); + } + } +} diff --git a/test/WebSites/ValidationWebSite/ValidationWebSite.kproj b/test/WebSites/ValidationWebSite/ValidationWebSite.kproj new file mode 100644 index 0000000000..5aede73054 --- /dev/null +++ b/test/WebSites/ValidationWebSite/ValidationWebSite.kproj @@ -0,0 +1,23 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 87ab84b2-22c1-43c6-bb8a-1d327b446fb0 + ..\..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + 49635 + + + + + + + + \ No newline at end of file diff --git a/test/WebSites/ValidationWebSite/ViewModels/ProductViewModel.cs b/test/WebSites/ValidationWebSite/ViewModels/ProductViewModel.cs new file mode 100644 index 0000000000..f8fbcb3b63 --- /dev/null +++ b/test/WebSites/ValidationWebSite/ViewModels/ProductViewModel.cs @@ -0,0 +1,32 @@ +// 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.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Mvc; +using ValidationWebSite.Models; + +namespace ValidationWebSite.ViewModels +{ + [ModelMetadataType(typeof(Product))] + public class ProductViewModel + { + public string Name { get; set; } + + [Required] + public string Contact { get; set; } + + [Range(20, 100)] + public int Price { get; set; } + + [RegularExpression("^[a-zA-Z]*$")] + [Required] + public string Category { get; set; } + + public string CompanyName { get; set; } + + public string Country { get; set; } + + public ProductDetails ProductDetails { get; set; } + } +} \ No newline at end of file diff --git a/test/WebSites/ValidationWebSite/ViewModels/SoftwareViewModel.cs b/test/WebSites/ValidationWebSite/ViewModels/SoftwareViewModel.cs new file mode 100644 index 0000000000..6356004f76 --- /dev/null +++ b/test/WebSites/ValidationWebSite/ViewModels/SoftwareViewModel.cs @@ -0,0 +1,31 @@ +// 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.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Mvc; +using ValidationWebSite.Models; + +namespace ValidationWebSite.ViewModels +{ + [ModelMetadataType(typeof(Software))] + public class SoftwareViewModel + { + [RegularExpression("^[0-9]*$")] + public string Version { get; set; } + + public DateTime DatePurchased { get; set; } + + public int Price { get; set; } + + public string Contact { get; set; } + + public string Country { get; set; } + + public string Name { get; set; } + + public string Category { get; set; } + + public string CompanyName { get; set; } + } +} \ No newline at end of file diff --git a/test/WebSites/ValidationWebSite/project.json b/test/WebSites/ValidationWebSite/project.json new file mode 100644 index 0000000000..c582a1382f --- /dev/null +++ b/test/WebSites/ValidationWebSite/project.json @@ -0,0 +1,19 @@ +{ + "commands": { + "web": "Microsoft.AspNet.Hosting server=Microsoft.AspNet.Server.WebListener server.urls=http://localhost:5001", + "kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5000" + }, + "dependencies": { + "Kestrel": "1.0.0-*", + "Microsoft.AspNet.Mvc": "6.0.0-*", + "Microsoft.AspNet.Mvc.TestConfiguration": "1.0.0", + "Microsoft.AspNet.Server.IIS": "1.0.0-*", + "Microsoft.AspNet.Server.WebListener": "1.0.0-*", + "Microsoft.AspNet.StaticFiles": "1.0.0-*" + }, + "frameworks": { + "aspnet50": { }, + "aspnetcore50": { } + }, + "webroot": "wwwroot" +} diff --git a/test/WebSites/ValidationWebSite/wwwroot/readme.md b/test/WebSites/ValidationWebSite/wwwroot/readme.md new file mode 100644 index 0000000000..047eb18bae Binary files /dev/null and b/test/WebSites/ValidationWebSite/wwwroot/readme.md differ