Allow ValidationAttributes and IValidatableObjects to resolve services from the RequestServices provider by injecting it into the ValidationContext.

This commit is contained in:
Derek Gray 2015-12-15 13:35:58 -06:00 committed by Ryan Nowak
parent a229b20c48
commit 328f4d648b
4 changed files with 111 additions and 3 deletions

View File

@ -82,7 +82,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
var container = validationContext.Container;
var context = new ValidationContext(container ?? validationContext.Model)
var context = new ValidationContext(
instance: container ?? validationContext.Model,
serviceProvider: validationContext.ActionContext?.HttpContext?.RequestServices,
items: null)
{
DisplayName = metadata.GetDisplayName(),
MemberName = memberName

View File

@ -29,7 +29,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
throw new InvalidOperationException(message);
}
var validationContext = new ValidationContext(validatable, serviceProvider: null, items: null);
var validationContext = new ValidationContext(
instance: validatable,
serviceProvider: context.ActionContext?.HttpContext?.RequestServices,
items: null);
return ConvertResults(validatable.Validate(validationContext));
}

View File

@ -8,8 +8,10 @@ using System.Linq;
#if DNXCORE50
using System.Reflection;
#endif
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
using Microsoft.AspNet.Testing;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
@ -431,6 +433,49 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
Assert.Equal("Error3", error.ErrorMessage);
}
[Fact]
public void Validate_ComplexType_IValidatableObject_CanUseRequestServices()
{
// Arrange
var service = new Mock<IExampleService>();
service.Setup(x => x.DoSomething()).Verifiable();
var provider = new ServiceCollection().AddSingleton(service.Object).BuildServiceProvider();
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(x => x.RequestServices).Returns(provider);
var actionContext = new ActionContext { HttpContext = httpContext.Object };
var validatorProvider = CreateValidatorProvider();
var modelState = actionContext.ModelState;
var validationState = new ValidationStateDictionary();
var validator = CreateValidator();
var model = new Mock<IValidatableObject>();
model
.Setup(x => x.Validate(It.IsAny<ValidationContext>()))
.Callback((ValidationContext context) =>
{
var receivedService = context.GetService<IExampleService>();
Assert.Equal(service.Object, receivedService);
receivedService.DoSomething();
})
.Returns(new List<ValidationResult>());
// Act
validator.Validate(
actionContext,
validatorProvider,
validationState,
null,
model.Object);
// Assert
service.Verify();
}
[Fact]
[ReplaceCulture]
public void Validate_ComplexType_FieldsAreIgnored_Valid()
@ -1071,5 +1116,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
}
}
public interface IExampleService
{
void DoSomething();
}
}
}

View File

@ -5,7 +5,9 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.DataAnnotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Moq;
using Xunit;
@ -152,7 +154,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
}
[Fact]
public void Validatate_ValidationResultSuccess()
public void Validate_ValidationResultSuccess()
{
// Arrange
var metadata = _metadataProvider.GetMetadataForType(typeof(string));
@ -315,6 +317,50 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
Assert.Equal("Longueur est invalide : 4", validationResult.Message);
}
[Fact]
public void Validate_CanUseRequestServices_WithinValidationAttribute()
{
// Arrange
var service = new Mock<IExampleService>();
service.Setup(x => x.DoSomething()).Verifiable();
var provider = new ServiceCollection().AddSingleton(service.Object).BuildServiceProvider();
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(x => x.RequestServices).Returns(provider);
var attribute = new Mock<TestableValidationAttribute> { CallBase = true };
attribute
.Setup(p => p.IsValidPublic(It.IsAny<object>(), It.IsAny<ValidationContext>()))
.Callback((object o, ValidationContext context) =>
{
var receivedService = context.GetService<IExampleService>();
Assert.Equal(service.Object, receivedService);
receivedService.DoSomething();
});
var validator = new DataAnnotationsModelValidator(
new ValidationAttributeAdapterProvider(),
attribute.Object,
stringLocalizer: null);
var validationContext = new ModelValidationContext(
actionContext: new ActionContext
{
HttpContext = httpContext.Object
},
modelMetadata: _metadataProvider.GetMetadataForType(typeof(object)),
metadataProvider: _metadataProvider,
container: null,
model: new object());
// Act
var results = validator.Validate(validationContext);
// Assert
service.Verify();
}
private const string LocalizationKey = "LocalizeIt";
public static TheoryData Validate_AttributesIncludeValues
@ -430,5 +476,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
{
public string Name { get; set; }
}
public interface IExampleService
{
void DoSomething();
}
}
}