From 5a83383179abcfad5ebddc7736549fa096c1df9c Mon Sep 17 00:00:00 2001 From: Harsh Gupta Date: Thu, 9 Oct 2014 16:42:52 -0700 Subject: [PATCH] Adding ApiController.Validate : Fixes #1286 --- .../ApiController.cs | 35 +++++++++++++ .../WebApiCompatShimBasicTest.cs | 47 ++++++++++++++++++ .../Controllers/BasicApiController.cs | 49 +++++++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs index 3d9c2bfd0f..efcefa8f32 100644 --- a/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs +++ b/src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs @@ -7,6 +7,7 @@ using Microsoft.AspNet.Http; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.WebApiCompatShim; +using Microsoft.Framework.DependencyInjection; namespace System.Web.Http { @@ -83,6 +84,40 @@ namespace System.Web.Http GC.SuppressFinalize(this); } + /// + /// Validates the given entity and adds the validation errors to the + /// under an empty prefix. + /// + /// The type of the entity to be validated. + /// The entity being validated. + public void Validate(TEntity entity) + { + Validate(entity, keyPrefix: string.Empty); + } + + /// + /// Validates the given entity and adds the validation errors to the . + /// + /// The type of the entity to be validated. + /// The entity being validated. + /// + /// The key prefix under which the model state errors would be added in the + /// . + /// + public void Validate(TEntity entity, string keyPrefix) + { + var validator = Context.RequestServices.GetService(); + var metadataProvider = Context.RequestServices.GetService(); + var modelMetadata = metadataProvider.GetMetadataForType(() => entity, typeof(TEntity)); + var validatorProvider = Context.RequestServices.GetService(); + var modelValidationContext = new ModelValidationContext(metadataProvider, + validatorProvider, + ModelState, + modelMetadata, + containerMetadata: null); + validator.Validate(modelValidationContext, keyPrefix); + } + protected virtual void Dispose(bool disposing) { } diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs index ae897181bd..b37a95c9c1 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs @@ -81,6 +81,53 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal(expected, formatters); } + [Fact] + public async Task ApiController_CanValidateCustomObjectWithPrefix_Fails() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetStringAsync( + "http://localhost/api/Blog/BasicApi/ValidateObjectWithPrefixFails?prefix=prefix"); + + // Assert + var json = JsonConvert.DeserializeObject>(response); + Assert.Equal(1, json.Count); + Assert.Equal("The field ID must be between 0 and 100.", json["prefix.ID"]); + } + + [Fact] + public async Task ApiController_CanValidateCustomObject_IsSuccessFul() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetStringAsync("http://localhost/api/Blog/BasicApi/ValidateObject_Passes"); + + // Assert + Assert.Equal("true", response); + } + + [Fact] + public async Task ApiController_CanValidateCustomObject_Fails() + { + // Arrange + var server = TestServer.Create(_provider, _app); + var client = server.CreateClient(); + + // Act + var response = await client.GetStringAsync("http://localhost/api/Blog/BasicApi/ValidateObjectFails"); + + // Assert + var json = JsonConvert.DeserializeObject>(response); + Assert.Equal(1, json.Count); + Assert.Equal("The field ID must be between 0 and 100.", json["ID"]); + } + [Fact] public async Task ApiController_RequestProperty() { diff --git a/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs b/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs index 09478361be..36cda533c4 100644 --- a/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs +++ b/test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs @@ -1,6 +1,8 @@ // 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 System.Threading.Tasks; using System.Web.Http; @@ -45,5 +47,52 @@ namespace WebApiCompatShimWebSite { return OptionsAccessor.Options.Formatters.Select(f => f.GetType().FullName).ToArray(); } + + [HttpGet] + public bool ValidateObject_Passes() + { + var entity = new TestEntity { ID = 42 }; + Validate(entity); + return ModelState.IsValid; + } + + [HttpGet] + public object ValidateObjectFails() + { + var entity = new TestEntity { ID = -1 }; + Validate(entity); + return CreateValidationDictionary(); + } + + [HttpGet] + public object ValidateObjectWithPrefixFails(string prefix) + { + var entity = new TestEntity { ID = -1 }; + Validate(entity, prefix); + return CreateValidationDictionary(); + } + + private class TestEntity + { + [Range(0, 100)] + public int ID { get; set; } + } + + private Dictionary CreateValidationDictionary() + { + var result = new Dictionary(); + foreach (var item in ModelState) + { + var error = item.Value.Errors.SingleOrDefault(); + if (error != null) + { + var value = error.Exception != null ? error.Exception.Message : + error.ErrorMessage; + result.Add(item.Key, value); + } + } + + return result; + } } } \ No newline at end of file