Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit 71737de

Browse files
committed
Adding ApiController.Validate : Fixes #1286
1 parent 8203b1e commit 71737de

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed

src/Microsoft.AspNet.Mvc.WebApiCompatShim/ApiController.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.AspNet.Mvc;
88
using Microsoft.AspNet.Mvc.ModelBinding;
99
using Microsoft.AspNet.Mvc.WebApiCompatShim;
10+
using Microsoft.Framework.DependencyInjection;
1011

1112
namespace System.Web.Http
1213
{
@@ -83,6 +84,44 @@ public void Dispose()
8384
GC.SuppressFinalize(this);
8485
}
8586

87+
/// <summary>
88+
/// Validates the given entity and adds the validation errors to the <see cref="ApiController.ModelState"/>
89+
/// under the empty prefix, if any.
90+
/// </summary>
91+
/// <typeparam name="TEntity">The type of the entity to be validated.</typeparam>
92+
/// <param name="entity">The entity being validated.</param>
93+
public void Validate<TEntity>(TEntity entity)
94+
{
95+
Validate(entity, keyPrefix: String.Empty);
96+
}
97+
98+
/// <summary>
99+
/// Validates the given entity and adds the validation errors to the <see cref="ApiController.ModelState"/>,
100+
/// if any.
101+
/// </summary>
102+
/// <typeparam name="TEntity">The type of the entity to be validated.</typeparam>
103+
/// <param name="entity">The entity being validated.</param>
104+
/// <param name="keyPrefix">
105+
/// The key prefix under which the model state errors would be added in the
106+
/// <see cref="ApiController.ModelState"/>.
107+
/// </param>
108+
public void Validate<TEntity>(TEntity entity, string keyPrefix)
109+
{
110+
var validator = Context.RequestServices.GetService<IBodyModelValidator>();
111+
if (validator != null)
112+
{
113+
var metadataProvider = Context.RequestServices.GetService<IModelMetadataProvider>();
114+
var modelMetadata = metadataProvider.GetMetadataForType(() => entity, typeof(TEntity));
115+
var validatorProvider = Context.RequestServices.GetService<ICompositeModelValidatorProvider>();
116+
var modelValidationContext = new ModelValidationContext(metadataProvider,
117+
validatorProvider,
118+
ModelState,
119+
modelMetadata,
120+
containerMetadata: null);
121+
validator.Validate(modelValidationContext, keyPrefix);
122+
}
123+
}
124+
86125
protected virtual void Dispose(bool disposing)
87126
{
88127
}

test/Microsoft.AspNet.Mvc.FunctionalTests/WebApiCompatShimBasicTest.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
using Microsoft.AspNet.TestHost;
1313
using Newtonsoft.Json;
1414
using Xunit;
15+
using Newtonsoft.Json;
16+
using System.Collections.Generic;
1517

1618
namespace Microsoft.AspNet.Mvc.FunctionalTests
1719
{
@@ -79,6 +81,75 @@ public async Task Options_SetsDefaultFormatters()
7981
// Assert
8082
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
8183
Assert.Equal(expected, formatters);
84+
85+
// Assert
86+
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
87+
Assert.Equal(1, json.Count);
88+
Assert.Equal("The field ID must be between 0 and 100.", json["ID"]);
89+
}
90+
91+
[Fact]
92+
public async Task ApiController_CanValidateCustomObjectWithPrefix_Fails()
93+
{
94+
// Arrange
95+
var server = TestServer.Create(_provider, _app);
96+
var client = server.CreateClient();
97+
98+
// Act
99+
var response = await client.GetStringAsync(
100+
"http://localhost/BasicApi/ValidateObjectWithPrefixFails?prefix=prefix");
101+
102+
// Assert
103+
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
104+
Assert.Equal(1, json.Count);
105+
Assert.Equal("The field ID must be between 0 and 100.", json["prefix.ID"]);
106+
}
107+
108+
[Fact]
109+
public async Task ApiController_CanValidateCustomObject_IsSuccessFul()
110+
{
111+
// Arrange
112+
var server = TestServer.Create(_provider, _app);
113+
var client = server.CreateClient();
114+
115+
// Act
116+
var response = await client.GetStringAsync("http://localhost/BasicApi/ValidateObject_Passes");
117+
118+
// Assert
119+
Assert.Equal("true", response);
120+
}
121+
122+
[Fact]
123+
public async Task ApiController_CanValidateCustomObject_Fails()
124+
{
125+
// Arrange
126+
var server = TestServer.Create(_provider, _app);
127+
var client = server.CreateClient();
128+
129+
// Act
130+
var response = await client.GetStringAsync("http://localhost/BasicApi/ValidateObjectFails");
131+
132+
// Assert
133+
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
134+
Assert.Equal(1, json.Count);
135+
Assert.Equal("The field ID must be between 0 and 100.", json["ID"]);
136+
}
137+
138+
[Fact]
139+
public async Task ApiController_CanValidateCustomObjectWithPrefix_Fails()
140+
{
141+
// Arrange
142+
var server = TestServer.Create(_provider, _app);
143+
var client = server.CreateClient();
144+
145+
// Act
146+
var response = await client.GetStringAsync(
147+
"http://localhost/BasicApi/ValidateObjectWithPrefixFails?prefix=prefix");
148+
149+
// Assert
150+
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
151+
Assert.Equal(1, json.Count);
152+
Assert.Equal("The field ID must be between 0 and 100.", json["prefix.ID"]);
82153
}
83154

84155
[Fact]

test/WebSites/WebApiCompatShimWebSite/Controllers/BasicApiController.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System.ComponentModel.DataAnnotations;
45
using System.Linq;
56
using System.Threading.Tasks;
67
using System.Web.Http;
@@ -45,5 +46,49 @@ public string[] GetFormatters()
4546
{
4647
return OptionsAccessor.Options.Formatters.Select(f => f.GetType().FullName).ToArray();
4748
}
49+
50+
public bool ValidateObject_Passes()
51+
{
52+
var entity = new TestEntity { ID = 42 };
53+
Validate(entity);
54+
return ModelState.IsValid;
55+
}
56+
57+
public object ValidateObjectFails()
58+
{
59+
var entity = new TestEntity { ID = -1 };
60+
Validate(entity);
61+
return CreateValidationDictionary();
62+
}
63+
64+
public object ValidateObjectWithPrefixFails(string prefix)
65+
{
66+
var entity = new TestEntity { ID = -1 };
67+
Validate(entity, prefix);
68+
return CreateValidationDictionary();
69+
}
70+
71+
private class TestEntity
72+
{
73+
[Range(0, 100)]
74+
public int ID { get; set; }
75+
}
76+
77+
private Dictionary<string, string> CreateValidationDictionary()
78+
{
79+
var result = new Dictionary<string, string>();
80+
foreach (var item in ModelState)
81+
{
82+
var error = item.Value.Errors.SingleOrDefault();
83+
if (error != null)
84+
{
85+
var value = error.Exception != null ? error.Exception.Message :
86+
error.ErrorMessage;
87+
result.Add(item.Key, value);
88+
}
89+
}
90+
91+
return result;
92+
}
4893
}
4994
}

0 commit comments

Comments
 (0)