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