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

Commit db84a0f

Browse files
committed
Adding ApiController.Validate : Fixes #1286
1 parent 931d97f commit db84a0f

File tree

3 files changed

+156
-1
lines changed

3 files changed

+156
-1
lines changed

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.AspNet.Http;
66
using Microsoft.AspNet.Mvc;
77
using Microsoft.AspNet.Mvc.ModelBinding;
8+
using Microsoft.Framework.DependencyInjection;
89

910
namespace System.Web.Http
1011
{
@@ -27,7 +28,8 @@ public HttpContext Context
2728
}
2829

2930
/// <summary>
30-
/// Gets model state after the model binding process. This ModelState will be empty before model binding happens.
31+
/// Gets model state after the model binding process. This ModelState will be empty before model binding
32+
/// happens.
3133
/// </summary>
3234
public ModelStateDictionary ModelState
3335
{
@@ -58,6 +60,43 @@ public void Dispose()
5860
GC.SuppressFinalize(this);
5961
}
6062

63+
/// <summary>
64+
/// Validates the given entity and adds the validation errors to the <see cref="ApiController.ModelState"/>
65+
/// under the empty prefix, if any.
66+
/// </summary>
67+
/// <typeparam name="TEntity">The type of the entity to be validated.</typeparam>
68+
/// <param name="entity">The entity being validated.</param>
69+
public void Validate<TEntity>(TEntity entity)
70+
{
71+
Validate(entity, keyPrefix: String.Empty);
72+
}
73+
74+
/// <summary>
75+
/// Validates the given entity and adds the validation errors to the <see cref="ApiController.ModelState"/>,
76+
/// if any.
77+
/// </summary>
78+
/// <typeparam name="TEntity">The type of the entity to be validated.</typeparam>
79+
/// <param name="entity">The entity being validated.</param>
80+
/// <param name="keyPrefix">
81+
/// The key prefix under which the model state errors would be added in the <see cref="ApiController.ModelState"/>.
82+
/// </param>
83+
public void Validate<TEntity>(TEntity entity, string keyPrefix)
84+
{
85+
var validator = Context.RequestServices.GetService<IBodyModelValidator>();
86+
if (validator != null)
87+
{
88+
var metadataProvider = Context.RequestServices.GetService<IModelMetadataProvider>();
89+
var modelMetadata = metadataProvider.GetMetadataForType(() => entity, typeof(TEntity));
90+
var validatorProvider = Context.RequestServices.GetService<ICompositeModelValidatorProvider>();
91+
var modelValidationContext = new ModelValidationContext(metadataProvider,
92+
validatorProvider,
93+
ModelState,
94+
modelMetadata,
95+
containerMetadata: null);
96+
validator.Validate(modelValidationContext, keyPrefix);
97+
}
98+
}
99+
61100
protected virtual void Dispose(bool disposing)
62101
{
63102
}

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using Microsoft.AspNet.TestHost;
1111
using Newtonsoft.Json;
1212
using Xunit;
13+
using Newtonsoft.Json;
14+
using System.Collections.Generic;
1315

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

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)