ModelState.IsValid even when it should not be? - c#

I have API where I need to validate my user model. I choose an approach where I create different classes for Create/Edit actions to avoid mass-assignment and divide validation and actual model apart.
I don't know why but ModelState.IsValid returns true even when it should not. Am I doing something wrong?
Controller
public HttpResponseMessage Post(UserCreate user)
{
if (ModelState.IsValid) // It's valid even when user = null
{
var newUser = new User
{
Username = user.Username,
Password = user.Password,
Name = user.Name
};
_db.Users.Add(newUser);
_db.SaveChanges();
return Request.CreateResponse(HttpStatusCode.Created, new { newUser.Id, newUser.Username, newUser.Name });
}
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
Model
public class UserCreate
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string Name { get; set; }
}
Debug proof

The ModelState.IsValid internally checks the Values.All(modelState => modelState.Errors.Count == 0) expression.
Because there was no input the Values collection will be empty so ModelState.IsValid will be true.
So you need to explicitly handle this case with:
if (user != null && ModelState.IsValid)
{
}
Whether this is a good or bad design decision that if you validate nothing it will true is a different question...

Here is an action filter to check for null models or invalid models. (so you dont have to write the check on every action)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace Studio.Lms.TrackingServices.Filters
{
public class ValidateViewModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionArguments.Any(kv => kv.Value == null)) {
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Arguments cannot be null");
}
if (actionContext.ModelState.IsValid == false) {
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
}
You can register it globally:
config.Filters.Add(new ValidateViewModelAttribute());
Or use it on demand on classes/actions
[ValidateViewModel]
public class UsersController : ApiController
{ ...

I wrote a custom filter which not only ensures that all non optional object properties are passed, but also checks if model state is valid:
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public sealed class ValidateModelAttribute : ActionFilterAttribute
{
private static readonly ConcurrentDictionary<HttpActionDescriptor, IList<string>> NotNullParameterNames =
new ConcurrentDictionary<HttpActionDescriptor, IList<string>> ();
/// <summary>
/// Occurs before the action method is invoked.
/// </summary>
/// <param name="actionContext">The action context.</param>
public override void OnActionExecuting (HttpActionContext actionContext)
{
var not_null_parameter_names = GetNotNullParameterNames (actionContext);
foreach (var not_null_parameter_name in not_null_parameter_names)
{
object value;
if (!actionContext.ActionArguments.TryGetValue (not_null_parameter_name, out value) || value == null)
actionContext.ModelState.AddModelError (not_null_parameter_name, "Parameter \"" + not_null_parameter_name + "\" was not specified.");
}
if (actionContext.ModelState.IsValid == false)
actionContext.Response = actionContext.Request.CreateErrorResponse (HttpStatusCode.BadRequest, actionContext.ModelState);
}
private static IList<string> GetNotNullParameterNames (HttpActionContext actionContext)
{
var result = NotNullParameterNames.GetOrAdd (actionContext.ActionDescriptor,
descriptor => descriptor.GetParameters ()
.Where (p => !p.IsOptional && p.DefaultValue == null &&
!p.ParameterType.IsValueType &&
p.ParameterType != typeof (string))
.Select (p => p.ParameterName)
.ToList ());
return result;
}
}
And I put it in global filter for all Web API actions:
config.Filters.Add (new ValidateModelAttribute ());

Updated slightly for asp.net core...
[AttributeUsage(AttributeTargets.Method)]
public sealed class CheckRequiredModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var requiredParameters = context.ActionDescriptor.Parameters.Where(
p => ((ControllerParameterDescriptor)p).ParameterInfo.GetCustomAttribute<RequiredModelAttribute>() != null).Select(p => p.Name);
foreach (var argument in context.ActionArguments.Where(a => requiredParameters.Contains(a.Key, StringComparer.Ordinal)))
{
if (argument.Value == null)
{
context.ModelState.AddModelError(argument.Key, $"The argument '{argument.Key}' cannot be null.");
}
}
if (!context.ModelState.IsValid)
{
var errors = context.ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
context.Result = new BadRequestObjectResult(errors);
return;
}
base.OnActionExecuting(context);
}
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RequiredModelAttribute : Attribute
{
}
services.AddMvc(options =>
{
options.Filters.Add(typeof(CheckRequiredModelAttribute));
});
public async Task<IActionResult> CreateAsync([FromBody][RequiredModel]RequestModel request, CancellationToken cancellationToken)
{
//...
}

This happened to me, and in my case, I had to change using Microsoft.Build.Framework; to using System.ComponentModel.DataAnnotations; (and add the reference).

I was looking for a solution to this problem and came out here first. After some further research I have realized the following solution:
How do you use my solution?
You can register it globally:
config.Filters.Add(new ValidateModelStateAttribute());
Or use it on demand for a class
[ValidateModelState]
public class UsersController : ApiController
{...
or for a methode
[ValidateModelState]
public IHttpActionResult Create([Required] UserModel data)
{...
As you can see, a [System.ComponentModel.DataAnnotations.Required] atribute has been placed in the method parameter.
This indicates that the model is required and can not be null.
You can also use with a custom message:
[ValidateModelState]
public IHttpActionResult Create([Required(ErrorMessage = "Custom message")] UserModel data)
{...
Here is my code:
using System;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace your_base_namespace.Web.Http.Filters
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
public class ValidateModelStateAttribute : ActionFilterAttribute
{
private delegate void ValidateHandler(HttpActionContext actionContext);
private static readonly ConcurrentDictionary<HttpActionBinding, ValidateHandler> _validateActionByActionBinding;
static ValidateModelStateAttribute()
{
_validateActionByActionBinding = new ConcurrentDictionary<HttpActionBinding, ValidateHandler>();
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
GetValidateHandler(actionContext.ActionDescriptor.ActionBinding)(actionContext);
if (actionContext.ModelState.IsValid)
return;
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
private ValidateHandler GetValidateHandler(HttpActionBinding actionBinding)
{
ValidateHandler validateAction;
if (!_validateActionByActionBinding.TryGetValue(actionBinding, out validateAction))
_validateActionByActionBinding.TryAdd(actionBinding, validateAction = CreateValidateHandler(actionBinding));
return validateAction;
}
private ValidateHandler CreateValidateHandler(HttpActionBinding actionBinding)
{
ValidateHandler handler = new ValidateHandler(c => { });
var parameters = actionBinding.ParameterBindings;
for (int i = 0; i < parameters.Length; i++)
{
var parameterDescriptor = (ReflectedHttpParameterDescriptor)parameters[i].Descriptor;
var attribute = parameterDescriptor.ParameterInfo.GetCustomAttribute<RequiredAttribute>(true);
if (attribute != null)
handler += CreateValidateHandler(attribute, parameterDescriptor.ParameterName);
}
return handler;
}
private static ValidateHandler CreateValidateHandler(ValidationAttribute attribute, string name)
{
return CreateValidateHandler(attribute, new ValidationContext(new object()) { MemberName = name });
}
private static ValidateHandler CreateValidateHandler(ValidationAttribute attribute, ValidationContext context)
{
return new ValidateHandler(actionContext =>
{
object value;
actionContext.ActionArguments.TryGetValue(context.MemberName, out value);
var validationResult = attribute.GetValidationResult(value, context);
if (validationResult != null)
actionContext.ModelState.AddModelError(context.MemberName, validationResult.ErrorMessage);
});
}
}
}

There is a simple Solution for your problem
public class UserCreate
{
[Required(AllowEmptyStrings = false)]
public string Username { get; set; }
}
Here AllowEmptyStrings = false can be used for your validation

Try
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
in the startup.cs file's ConfigureServices()

What I did was to create an Attribute along with an ActionFilter and a Extension Method to avoid null models.
The extension method looks for parameters with the NotNull attribute and check if they are null, if true, they are instantiated and set in the ActionArguments property.
This solution can be found here: https://gist.github.com/arielmoraes/63a39a758026b47483c405b77c3e96b9

This "ModelState.IsValid returns true even when it should not" problem can also appear if you forgot to add getters and setters on your model (OP didn't forget, but I did which led me to this question). I hope it's ok to provide solutions that have the same symptoms but are slightly different than OP's code:
Wrong:
public class UserRegisterModel
{
[Required]
public string Login; // WRONG
[Required]
public string Password; // WRONG
}
Good:
public class UserRegisterModel
{
[Required]
public string Login { get; set; }
[Required]
public string Password { get; set; }
}

this issue happened to me .i do not know why but take it easy just change your action Object name(UserCreate User) by some other like (UserCreate User_create)

Related

Asp.Net MVC data annotation custom attribute is not working

I need implement a cusotm attribute then using asp.net data annotation to validate a class. Unfortunately, the attribute class is not called at run time. Please help me out. Many thanks. Below is the source code.
using System;
using System.Collections.Generic;
using CaseMgr.Model.Base;
using System.Linq;
using System.ComponentModel.DataAnnotations;
namespace CaseMgr.Model.BusinessObjects
{
public partial class PatLiverException : BusinessBase<decimal>, IComparable<PatLiverException>, IEquatable<PatLiverException>
{
private LiverExcepDisease _liverExcepDisease = null;
private DateTime _sccApprovalDate = new DateTime();
public PatLiverException() { }
public virtual LiverExcepDisease LiverExcepDisease
{
get { return _liverExcepDisease; }
set { _liverExcepDisease = value; }
}
[SccApprovalDateValidate("SccApprovalDate", "LiverExcepDisease")]
public virtual DateTime SccApprovalDate
{
get { return _sccApprovalDate; }
set { _sccApprovalDate = value; }
}
}
public class SccApprovalDateValidateAttribute : ValidationAttribute
{
public string m_SccApprovalDate { get; private set; }
public string m_LiverExcepDisease { get; private set; }
public SccApprovalDateValidateAttribute(string SccApprovalDate_PropertyName, string LiverExcepDisease_PropertyName)
{
this.m_SccApprovalDate = SccApprovalDate_PropertyName;
this.m_LiverExcepDisease = LiverExcepDisease_PropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var SccApprovalDate_Property = context.ObjectType.GetProperty(m_SccApprovalDate);
DateTime SccApprovalDate_Value = (DateTime)SccApprovalDate_Property.GetValue(context.ObjectInstance, null);
var LiverExcepDisease_Property = context.ObjectType.GetProperty(m_LiverExcepDisease);
LiverExcepDisease LiverExcepDisease_Value = (LiverExcepDisease)LiverExcepDisease_Property.GetValue(context.ObjectInstance, null);
if (SccApprovalDate_Value != null && SccApprovalDate_Value != DateTime.MinValue && SccApprovalDate_Value != DateTime.MaxValue)
{
return LiverExcepDisease_Value.Id == 10 ? ValidationResult.Success : new ValidationResult("When other, SccApprovalDate can not be null.");
}
else
{
return ValidationResult.Success;
}
}
}
}
If you are going to use the validation attribute on a class you need:
[AttributeUsage(AttributeTargets.Class)]
public class Sccxxxxxxxxx : ValidationAttribute
https://msdn.microsoft.com/en-us/library/tw5zxet9.aspx?f=255&MSPPError=-2147217396
Take a look at this post: ASP.NET MVC: Custom Validation by DataAnnotation
Also, if you put a breakpoint inside SccApprovalDateValidateAttribute.ValidationResult() does it get hit?
Your code looks okay, except I cannot see where you call or use the property,SccApprovalDate, because your validation attribute declared on the property will only be invoked if the property is called or used by the run-time. Can you post how you use the property SccApprovalDate?

Testing FluentValidation PropertyValidator

Is it possible to test a FluentValidation PropertyValidator in isolation?
I know I can test the Validator that's using the PropertyValidator for specific errors but I’d rather test true/false just on the property validator if possible.
Can this be done? If so, how?
I also wanted to test my true / false logic. It is a shame the IsValid method is protected. My work around was to create another IsValid method and have the protected IsValid call through to it.
public class MyValidator: PropertyValidator
{
public MyValidator(
string errorMessage = "default Message") : base(errorMessage)
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var stringToValidate = context.PropertyValue as String;
return IsValid(stringToValidate);
}
public bool IsValid(string stringToValidate)
{
if (stringToValidate == null)
{
return false;
}
//testing logic here
return true;
}
}
I know this has been a while, but I achieved this as follows:
Custom Validator:
public class MyValidator : PropertyValidator
{
public MyValidator ()
: base("Value must be null or between 0 and 3.")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
if (context.PropertyValue == null)
{
return true;
}
var value = (decimal)context.PropertyValue;
return value >= 0m && value <= 3m;
}
}
Test Validator:
public class TestValidator : InlineValidator<TestObject>
{
public TestValidator (params Action<TestValidator >[] actions)
{
foreach (var action in actions)
{
action(this);
}
}
}
Test Object:
public class TestObject
{
public TestObject(decimal? val)
{
this.GenericDecimal = val;
}
public decimal? GenericDecimal { get; set; }
}
Test:
[Test]
public void TestIt()
{
var validator = new TestValidator(v => v.RuleFor(obj => obj.GenericDecimal).SetValidator( new MyValidator() ));
Assert.IsTrue(validator.Validate(new TestObject(null)).IsValid);
Assert.IsTrue(validator.Validate(new TestObject(0m)).IsValid);
Assert.IsTrue(validator.Validate(new TestObject(3m)).IsValid);
Assert.IsFalse(validator.Validate(new TestObject(-1m)).IsValid);
Assert.IsFalse(validator.Validate(new TestObject(3.01m)).IsValid);
}
As for version 6.2 of FluentValidation it is possible to build the PropertyValidator.Validate() parameter due to making ValidatorSelectors globally configurable: https://github.com/JeremySkinner/FluentValidation/commit/95376c0519da1a06388be91a97fb5062fd4a162e
In the below example you see how I validate the 'puic' property of Track
Unit test:
public void ExistsInCollectionValidatorTest()
{
var track = new Track()
{
puic = "p1"
};
var sut = new ExistsInCollectionValidator<Track>();
// Build PropertyValidator.Validate() parameter
var selector = ValidatorOptions.ValidatorSelectors.DefaultValidatorSelectorFactory();
var context = new ValidationContext(track, new PropertyChain(), selector);
var propertyValidatorContext = new PropertyValidatorContext(context, PropertyRule.Create<Track,string>(t => t.puic), "puic");
var results = sut.Validate(propertyValidatorContext);
// Assertion..
}

Multiple authorization attributes that are aware of each other

I have a very simple scenario. I want to decorate my controllers/actions with a custom authorization attribute. Authorization should be granted if any of the attributes is valid. For example,
[MyAuth(1)]
[MyAuth(2)]
public class MyController : Controller
{
...
}
I cannot combine the parameters into a single authorization attribute. The above example is a simplified example, only.
If either attribute authorizes the user, I want the user to be authorized. I assumed that ActionFilterAttribute or AuthorizeAttribute would have the means to see what other filters have been executed and are waiting to be executed, but no such luck.
How can I accomplish this? Since the attributes don't seem to have any awareness, maybe an HttpModule? A custom ControllerActionInvoker?
I managed to get this to work last night. My solution is below. The attribute is pretty standard and I've trimmed the actual authorization parts. The interesting stuff happens in HasAssignedAcccessActionInvoker.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class RequiresAssignedAccess : AuthorizeAttribute
{
public int AccessType { get; private set; }
public int IdType { get; private set; }
public int IdValue { get; private set; }
public int Level { get; private set; }
public RequiresAssignedAccess(int accessType, int idType, int idValue, int level)
{
...
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (!base.AuthorizeCore(httpContext))
return false;
bool retval = ...
return retval;
}
}
HasAssignedAcccessActionInvoker inherits from the standard action invoker, but I overrode the InvokeAuthorizationFilters method to add the authorization logic we need. The standard invoker just spins through the authorization filters and if any of them returns a result, it breaks the loop.
public class HasAssignedAcccessActionInvoker : ControllerActionInvoker
{
protected override AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
AuthorizationContext authCtx = new AuthorizationContext(controllerContext, actionDescriptor);
/*
* If any of the filters are RequiresAssignedAccess, default this to false. One of them must authorize the user.
*/
bool hasAccess = !filters.Any(f => f is RequiresAssignedAccess);
foreach (IAuthorizationFilter current in filters)
{
/*
* This sets authorizationContext.Result, usually to an instance of HttpUnauthorizedResult
*/
current.OnAuthorization(authCtx);
if (current is RequiresAssignedAccess)
{
if (authCtx.Result == null)
{
hasAccess = true;
}
else if (authCtx.Result is HttpUnauthorizedResult)
{
authCtx.Result = null;
}
continue;
}
if (authCtx.Result != null)
break;
}
if (!hasAccess && authCtx.Result == null)
authCtx.Result = new HttpUnauthorizedResult();
return authCtx;
}
}
I had to look at MVC's internals with ILSpy to figure this out. For reference, this is the overridden version of that method:
protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter current in filters)
{
current.OnAuthorization(authorizationContext);
if (authorizationContext.Result != null)
{
break;
}
}
return authorizationContext;
}
Lastly, to wire this up and make everything possible, our controllers inherit from BaseController, which now returns the new invoker.
public class BaseController : Controller
{
protected override IActionInvoker CreateActionInvoker()
{
return new HasAssignedAcccessActionInvoker();
}
}
As far as I know, you cannot chain [Authorize] attributes in the manner that you want because they all have to pass (AND) and not (OR) behavior. However, the combining of the items into one does not cause you to have to do some magic string manipulation, regardless of the number of parameters that you need to pass to it. You can define your own set of parameters that are available to the Authorize attribute.
public class SuperCoolAuthorize : AuthorizationAttribute
{
public string Parameter1{get;set;}
public string Parameter2{get;set;}
public int Parameter3{get;set;}
public string Parameter4{get;set;}
public override void OnAuthorization(AuthorizationContext filterContext)
{
// your custom behaviour
}
}
And on your controller/action method
[Authorize(Parameter1 = "Foo", Parameter2 = "Bar", Parameter3 = 47, Parameter4 = string.Empty)
public ActionResult MyControllerAction(){
...
}
A great post on some other considerations on custom Authorizing attributes I came across in helping to formulate this answer.
public class AuthUserAttribute : AuthorizeAttribute {
public string[] SecurityGroups;
public string Groups { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext) {
bool valid = false;
var user = UserInformation.Current;
if (user.SecurityGroups.Select(x => x).Intersect(this.SecurityGroups).Any()) {
valid = true;
}
if (user.SecurityGroups.Select(x => x).Intersect(new string[] { "IT Administrators" }).Any()) {
valid = true;
}
return valid;
}
public override void OnAuthorization(AuthorizationContext filterContext) {
if (!this.AuthorizeCore(filterContext.HttpContext)) {
if (UserInformation.Current.SecurityGroups.Count == 0) {
filterContext.Result = new RedirectResult(string.Format("/oa?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
}
else {
filterContext.Result = new RedirectResult(string.Format("/oa/user/permissions?ReturnUrl={0}", filterContext.HttpContext.Request.RawUrl));
}
}
else {
base.OnAuthorization(filterContext);
}
}
}
then I decorate with
[AuthUser(SecurityGroups = new string[] { "Data1", "Data2" })]
public ActionResult ForYourEyesOnly() {
}
We'll see if anyone catches the Bond reference. LOL

Cannot step into MVC Custom RequiredAttribute

I cannot step into my custom RequiredAttribute.
I have followed this article How to: Debug .NET Framework Source
In Tools > Options > Debugging > General:
I have Enable .NET Framework source stepping ticked
I have Enable Just My Code unticked
I have created a basic example of a custom RequiredAttribute with unit test:
using System.ComponentModel.DataAnnotations;
public class CustomRequiredAttribute : RequiredAttribute
{
public bool IsValid(object value, object container)
{
if (value == null)
{
return false;
}
string str = value as string;
if (!string.IsNullOrWhiteSpace(str))
{
return true;
}
return false;
}
}
Used by this test model:
public class CustomRequiredAttributeModel
{
[CustomRequired]
public string Name { get; set; }
}
Here is the unit test (which passes the assert correctly):
[Fact]
public void custom_required_attribute_test()
{
// arrange
var model = new CustomRequiredAttributeModel();
var controller = AccountController();
// act
controller.ValidateModel(model);
// assert
Assert.False(controller.ModelState.IsValid);
}
The unit test uses this helper method:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Mvc;
public static class ModelHelper
{
public static void ValidateModel(this Controller controller, object viewModel)
{
controller.ModelState.Clear();
var validationContext = new ValidationContext(viewModel, null, null);
var validationResults = new List<ValidationResult>();
Validator.TryValidateObject(viewModel, validationContext, validationResults, true);
foreach (var result in validationResults)
{
if (result.MemberNames.Any())
{
foreach (var name in result.MemberNames)
{
controller.ModelState.AddModelError(name, result.ErrorMessage);
}
}
else
{
controller.ModelState.AddModelError("", result.ErrorMessage);
}
}
}
}
In your CustomRequiredAttribute change your method to use override,
public class CustomRequiredAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
string str = value as string;
if (!string.IsNullOrWhiteSpace(str))
{
return true;
}
return false;
}
}

FluentValidation rule for null object

I've been trying to work out how to create a FluentValidation rule that checks if the instance of an object it's validating is not null, prior to validating it's properties.
I'd rather encapsulate this null validation in the Validator rather then doing it in the calling code.
See example code below with comments where the required logic is needed:
namespace MyNamespace
{
using FluentValidation;
public class Customer
{
public string Surname { get; set; }
}
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
// Rule to check the customer instance is not null.
// Don't continue validating.
RuleFor(c => c.Surname).NotEmpty();
}
}
public class MyClass
{
public void DoCustomerWork(int id)
{
var customer = GetCustomer(id);
var validator = new CustomerValidator();
var results = validator.Validate(customer);
var validationSucceeded = results.IsValid;
}
public Customer GetCustomer(int id)
{
return null;
}
}
}
So my question is how do I check in the CustomerValidator() constructor that the current instance of customer is not null and abort further rule processing if it is null?
Thanks in advance.
EDIT 2022-07-19
As some commenters have pointed out, please check out answer https://stackoverflow.com/a/52784357/1943 for a newer implementation. I haven't personally vetted, but it's worth a try to give that a go first.
If you're using an older version, or you enjoy nostalgia, my original answer below is from 2013.
You should be able to override the Validate method in your CustomerValidator class.
public class CustomerValidator: AbstractValidator<Customer>
{
// constructor...
public override ValidationResult Validate(Customer instance)
{
return instance == null
? new ValidationResult(new [] { new ValidationFailure("Customer", "Customer cannot be null") })
: base.Validate(instance);
}
}
I can't really test that right now, but you can either try to override Validate, or include the rules in the When block:
public CustomerValidator()
{
When(x => x != null, () => {
RuleFor(x => x.Surname).NotEmpty();
//etc.
});
}
For those using version >6.2.1 you need to override this signature instead, in order to achieve the same as #chrispr:
public override ValidationResult Validate(ValidationContext<T> context)
{
return (context.InstanceToValidate == null)
? new ValidationResult(new[] { new ValidationFailure("Property", "Error Message") })
: base.Validate(context);
}
/// EXAMPLE FOR NETCORE-3.1
/// fluentvalidator-9.5.0
public class Organisation
{
public string Name { get; set; }
}
public class OrganisationValidator : AbstractValidator<Organisation>
{
public OrganisationValidator()
{
RuleFor(x => x.Name).NotNull().MaximumLength(50);
}
protected override bool PreValidate(ValidationContext<Organisation> context, ValidationResult result)
{
if (context.InstanceToValidate == null) {
result.Errors.Add(new ValidationFailure("", "org is null"));
return false;
}
return base.PreValidate(context, result);
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void ValidateWithNull()
{
var validator = new OrganisationValidator();
Organisation organisation = null;
var result = validator.Validate(organisation);
// result.Errors[0].ErrorMessage == "org is null";
}
}
This is an older post, but want to update the answers to include the following from the FluentValidation documentation:
Using PreValidate
If you need to run specific code every time a validator is invoked, you can do this by overriding the PreValidate method. This method takes a ValidationContext as well as a ValidationResult, which you can use to customise the validation process.
public class MyValidator : AbstractValidator<Person> {
public MyValidator() {
RuleFor(x => x.Name).NotNull();
}
protected override bool PreValidate(ValidationContext<Person> context, ValidationResult result) {
if (context.InstanceToValidate == null) {
result.Errors.Add(new ValidationFailure("", "Please ensure a model was supplied."));
return false;
}
return true;
}
}
https://docs.fluentvalidation.net/en/latest/advanced.html?#prevalidate
I inherited from the fluent AbstractValidator and created a NullReferenceAbstractValidator class instead:
public class NullReferenceAbstractValidator<T> : AbstractValidator<T>
{
public override ValidationResult Validate(T instance)
{
return instance == null
? new ValidationResult(new[] { new ValidationFailure(instance.ToString(), "response cannot be null","Error") })
: base.Validate(instance);
}
}
and then inherited from that class with each validator that needed a null reference check:
public class UserValidator : NullReferenceAbstractValidator<User>
As the above solutions didn't work for me (FluentValidation, Version=6.2.1.0 for Net45), I am posting what I did.
This is just a simple replacement/wrapper for ValidateAndThrow extension method.
public static class ValidatorExtensions
{
public static void ValidateAndThrowNotNull<T>(this IValidator<T> validator, T instance)
{
if (instance == null)
{
var validationResult = new ValidationResult(new[] { new ValidationFailure("", "Instance cannot be null") });
throw new ValidationException(validationResult.Errors);
}
validator.ValidateAndThrow(instance);
}
}
By means of Custom(). It can be also very helpful when validation of another field is based on validation of your current field.
ruleBuilder.Custom((obj, context) =>
{
if (obj != null)
{
var propertyName = <field where should be validation>;
context.AddFailure(propertyName, "'Your field name' Your validation message.");
}
});
Use the Cascade mode.
Here is the example from the documentation.
RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");
Also from the documentation:
If the NotNull validator fails then the NotEqual validator will not be
executed. This is particularly useful if you have a complex chain
where each validator depends on the previous validator to succeed.
Override EnsureInstanceNotNull as below
protected override void EnsureInstanceNotNull(object instanceToValidate)
{
if(instanceToValidate==null)
throw new ValidationException("Customer can not be null");
}
You can override a virtual method called EnsureInstanceNotNull as the author recommends here
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
// Rule to check the customer instance is not null.
RuleFor(c => c).NotNull();
// Don't continue validating.
RuleFor(c => c.Surname).NotEmpty();
}
protected override void EnsureInstanceNotNull(object instance) { }
}
The commonly accepted PreValidate answer will not work in this instance, as per here:
If you use SetValidator with an AbstractValidator derivative, then it
will not be run if the property value is null. This is intentional
behaviour as AbstractValidator derivatives are designed to validate
the properties of a complex type, which cannot be done if the instance
is null. AbstractValidator is not designed for use with
simple/primative types or object. Now, if you want to check for null
here, then you can precede the SetValidator call with a NotNull rule
(although it doesn't seem like this is what you want in this case).
For me the only thing that worked is (my example):
RuleForEach(command => command.ProductDto.ProductInfos).NotNull().WithMessage("Custom Message").SetValidator(new ProductInfoValidator());
Without the "NotNull()", null values will be skipped.

Categories