Conditional required based on Controller in ASP.NET Core - c#

I would like to write conditional required, but condition depends on the controller where it is used.
I already have custom attribute MyRequiredIfNot. I just don't know how to get information about controller in IsValid method.
For example:
public class MyController1 : Controller
{
public ActionResult Method1(MyModel model)
{
//Name is required !!!
}
}
public class MyController2 : MyController1
{
public ActionResult SomeMethod(MyModel model)
{
//Name is NOT required !!!
}
}
public class MyModel
{
[MyRequiredIfNot(MyController2)
public string Name { get;set; }
}
And missing implementation:
public class MyRequiredIfNotAttribute : ValidationAttribute
{
public MyRequiredIfNotAttribute(Controller controller) { }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (/*came_from ?*/ is this.controller) //Missing !!!!
return ValidationResult.Success;
else
return base.IsValid(value, validationContext);
}
}

For retriving the Controller, you could try IActionContextAccessor.
Follow steps below:
Register IActionContextAccessor
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
//rest services
}
MyRequiredIfNotAttribute
public class MyRequiredIfNotAttribute : RequiredAttribute//ValidationAttribute
{
private Type _type;
public MyRequiredIfNotAttribute(Type type) {
_type = type;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var actionContext = validationContext.GetRequiredService<IActionContextAccessor>();
var controllerActionDescriptor = actionContext.ActionContext.ActionDescriptor as ControllerActionDescriptor;
var controllerTypeName = controllerActionDescriptor.ControllerTypeInfo.FullName;
if (_type.FullName == controllerTypeName)
{
return ValidationResult.Success;
}
else
{
return base.IsValid(value, validationContext);
}
}
}
Useage
public class MyModel
{
[MyRequiredIfNot(typeof(MyController))]
public string Name { get; set; }
}

Related

Auto API Validation with FluentValidation

I want to auto apply validation for some models on requests execution. E.g. if I have a fluent validator for model A it will apply, if I don't have validator for model B then nothing happens. I wrote code, but maybe somebody can advise a better solution.
Create base class and interface:
public interface IBaseValidationModel
{
public void Validate(object validator, IBaseValidationModel modelObj);
}
public abstract class BaseValidationModel<T> : IBaseValidationModel
{
public void Validate(object validator, IBaseValidationModel modelObj)
{
var instance = (IValidator<T>)validator;
var result = instance.Validate((T)modelObj);
if (!result.IsValid && result.Errors.Any())
{
throw new Exception("INVALID");
}
}
}
Create model and validator:
public class LogInModel : BaseValidationModel<LogInModel>
{
public string? Name { get; set; }
public string? Password { get; set; }
public string? Domain { get; set; }
}
public class LogInModelValidator : AbstractValidator<LogInModel>
{
public LogInModelValidator()
{
RuleLevelCascadeMode = CascadeMode.Stop;
RuleFor(x => x.Domain).NotNull().NotEmpty();
RuleFor(x => x.Name).NotNull().NotEmpty();
RuleFor(x => x.Password).NotNull().NotEmpty();
}
}
Create action filter attribute to apply it to controllers:
public class ModelValidatorAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
foreach (var actionArgument in context.ActionArguments)
{
//validate that model is having validator and resolve it
if (actionArgument.Value is IBaseValidationModel model)
{
var modelType = actionArgument.Value.GetType();
var genericType = typeof(IValidator<>).MakeGenericType(modelType);
var validator = context.HttpContext.RequestServices.GetService(genericType);
if (validator != null)
{
// execute validator to validate model
model.Validate(validator, model);
}
}
}
base.OnActionExecuting(context);
}
}
Controller:
[Route("account")]
[ModelValidator]
public class AccountController : ControllerBase
{
public IActionResult Post([FromBody]LogInModel model)
{
return Ok("ok);
}
}

How to validate document type using FileValidationAttribute?

I have this Dto class for the web api controller in .NET Core 2.2 MVC.
ApplicationDocumentType is an enum
public class DocumentUploadDto
{
[FileValidation]
public IFormFile File { get; set; }
public ApplicationDocumentType DocumentType { get; set; }
public Guid Id { get; set; }
}
and
public enum ApplicationDocumentType
{
BANKSTATEMENT,
NRIC
}
and the below class implements the [FileValidation]
public class FileValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var file = value as IFormFile;
// some code removed for brevity
if (!AllowMimeTypes.Contains(file.ContentType))
{
ErrorMessage = "Invalid file type.";
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
}
Now I need to validate based on DocumentType. How do I pass DocumentType into FileValidationAttribute to do some validation?
Currently all DocumentType is having the same validation. But now I need to customize the validation based on DocumentType.
Thanks Richard for the clue, but I just keep getting the first enum value.
public class FileValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var containerType = validationContext.ObjectType;
var documentType = containerType.GetProperty("DocumentType");
var file = value as IFormFile;
if (file == null)
return new ValidationResult("No file found.");
if (documentType != null)
{
var documentTypeValue = documentType.GetValue(validationContext.ObjectInstance, null);
if (documentTypeValue.ToString() == "NRIC"
&& file.ContentType == "application/pdf")
{
ErrorMessage = "Invalid file type. Pdf file type is not allowed for NRIC.";
return new ValidationResult(ErrorMessage);
}
}
// some code removed for brevity purpose.
}
}
If document type is static, you can simply add an attribute to your Attribute.
public class FileValidationAttribute : ValidationAttribute
{
public FileValidationAttribute(params string[] allowMimeTypes)
{
AllowMimeTypes = allowMimeTypes;
}
public string[] AllowMimeTypes { get; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var file = value as IFormFile;
// some code removed for brevity
if (!AllowMimeTypes.Contains(file.ContentType))
{
ErrorMessage = "Invalid file type.";
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
}
and then, in Dto, add allowed Mime Types
public class DocumentUploadDto
{
[FileValidation("text/javascript", "text/html")]
public IFormFile File { get; set; }
public ApplicationDocumentType DocumentType { get; set; }
public Guid Id { get; set; }
}
If you need to pass mime type dynamically, then I suggest looking at https://fluentvalidation.net/ which allows you to easily add data to validation context and write more fluent validators.

Pass Property of Class to ValidationAttribute

I am trying to write my own ValidationAttribute for which I want to pass the value of a parameter of my class to the ValidationAttribute. Very simple, if the boolean property is true, the property with the ValidationAttribute on top should not be null or empty.
My class:
public class Test
{
public bool Damage { get; set; }
[CheckForNullOrEmpty(Damage)]
public string DamageText { get; set; }
...
}
My Attribute:
public class CheckForNullOrEmpty: ValidationAttribute
{
private readonly bool _damage;
public RequiredForWanrnleuchte(bool damage)
{
_damage = damage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string damageText = validationContext.ObjectType.GetProperty(validationContext.MemberName).GetValue(validationContext.ObjectInstance).ToString();
if (_damage == true && string.IsNullOrEmpty(damageText))
return new ValidationResult(ErrorMessage);
return ValidationResult.Success;
}
}
However, I cannot simply pass the property inside the class to the ValidationAttribute like that. What would be a solution to pass the value of that property?
Instead of passing the bool value to the CheckForNullOrEmptyAttribute, you should pass the name of the corresponding property; within the attribute, you then can retrieve this bool value from the object instance being validated.
The CheckForNullOrEmptyAttribute below, can be applied on your model as shown here.
public class Test
{
public bool Damage { get; set; }
[CheckForNullOrEmpty(nameof(Damage))] // Pass the name of the property.
public string DamageText { get; set; }
}
public class CheckForNullOrEmptyAttribute : ValidationAttribute
{
public CheckForNullOrEmptyAttribute(string propertyName)
{
PropertyName = propertyName;
}
public string PropertyName { get; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var hasValue = !string.IsNullOrEmpty(value as string);
if (hasValue)
{
return ValidationResult.Success;
}
// Retrieve the boolean value.
var isRequired =
Convert.ToBoolean(
validationContext.ObjectInstance
.GetType()
.GetProperty(PropertyName)
.GetValue(validationContext.ObjectInstance)
);
if (isRequired)
{
return new ValidationResult(ErrorMessage);
}
return ValidationResult.Success;
}
}

c# conditional Required Attribute

how can i to put conditional Required Attribute into class? i tried the following code and it doesn't work.
public partial class Zone
{
[RequireCondition ]
public int LastCount { get; set; }
}
public class RequireCondition : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var Zone = (Zone)validationContext.ObjectInstance;
if (Zone.LastCount < 1)
{
return new ValidationResult("Last Count value must be greater than one.");
}
else
{
return ValidationResult.Success;
}
}
}
Try this?
public partial class Zone
{
[RequireCondition(1)]
public int LastCount { get; set; }
}
public class RequireConditionAttribute : ValidationAttribute
{
private int _comparisonValue;
public RequireCondition(int comparisonValue)
{
_comparisonValue = comparisonValue;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value is int && (int)value < comparisonValue)
{
return new ValidationResult($"{validationContext.DisplayName} value must be greater than one.");
}
return ValidationResult.Success;
}
}

Validation attribute doesn't work

I am working in asp.net web api project. I created a ValidationAttribute class like as
public class MyValidationAttribute: ValidationAttribute
{
private int _Id { get; set; }
public MyValidationAttribute(int Id)
{
_Id = Id;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
object abc = value;
if (_Id > 10)
return ValidationResult.Success;
return base.IsValid(value, validationContext);
}
}
After that I am using this on a function
[MyValidationAttribute(12)]
public object MyFunction(int Iden){
//Some code here
}
The issue is, on debug mode, I can see that control is not parsing the MyValidationAttribute class. Am I doing some thing wrong?
Data attributes are supposed to be used on properties.
Change it to the following instead:
[MyValidationAttribute(12)]
public int MyProperty { get; set; }

Categories