ASP.NET Core: Performing the Model Validations in ASP.NET Core using Action Filters
In this article, we will see how we can validate the model data in ASP.NET Core API. In ASP.NET Core Actions filters allows code to run before or after specific stages in the request processing pipeline. There build-in filters provided in ASP.NET Core for Authorization, Resources, Exception, etc. We can create the Custom filters those are used to handle cross-cutting concerns. For example, custom error handling, caching, configuration, authorization, and logging. Action Filters are used to avoid duplicating code.
Data Validation is the most important and mandatory part of the application development. This prevents the invalid data to be accepted and processed by the application system. In ASP.NET Core, we have data annotations to validate Model properties. We can apply data annotations on Model properties if we have access to Model classes, but what if we do not have access to it, in this case we need to implement validations using Fluent Validation techniques. In .NET Core we have FluentValidation package which provides AbstrctValidator<T> class. This is a generic class where T is the class on which validations are applied fluently.
Step 1: Open Visual Studio 2022 and create a new ASP.NET Core API project. Name this as Core_ModelValidation. In this project add FluentValidation NuGet Package.
Step 2: In this project, add a new folder named Models. In this folder, add a new class file named Employee.cs. In this class file add code for Employee class as shown in Listing 1.
public class Employee { public int EmpNo { get; set; } public string? EmpName { get; set; } public string? DeptName { get; set; } public string? Designation { get; set; } public int Salary { get; set; } }
Listing 1: The Employee class
Step 3: In this project, add a new folder named ValidationRules. In this folder add a class file named EmployeeValidator.cs. In this class file we will add the code for EmployeeValidator class. This class will be derived from AbstractValidator class. In the EmployeeValidator class we will add logic for the validation rules for Employee class properties. The code for the EmployeeValidator class is shown in Listing 2.
public class EmployeeValidator: AbstractValidator<Employee> { public EmployeeValidator() { RuleFor(p => p.EmpName).NotEmpty().WithMessage("EmpName must not be empty"); RuleFor(p => p.DeptName).NotEmpty().WithMessage("DeptName must not be empty"); RuleFor(p => p.Designation).NotEmpty().WithMessage("Designation must not be empty"); } }
Listing 2: The EmployeeValidator class
Listing 2 shows how the validations are defined on various properties of the Employee class.
Step 4: In the project, add a new folder named CustomFilters. In this folder, add a new class file named ValidatorFilterAttribute.cs. In this class file, we will add code for ValidatorFilterAttribute class. This class is the Action Filter class. We will derive the ValidatorFilterAttribute class from ActionFilterAttribute class. In this class, we will add the ModelName property, this property will accept the Model class name which will be validated. The ValidatorFilterAttribute class overrides the OnActionExecuting() method. This method will create an instance of EmployeeValidator class. Since this class is derived from AbstractValidator class, we have access to the Validate() method of the AbstractValidator class. To this Validate() method we will be passing the ModelName property value which is read using the ActionArguments Dictionary type property. This is the property of the ActionExecutingContext class. The ActionExecutingContext class is the parameter of the OnActionExecuing() method. The ActionArguments property represents the argument passed to the action method in HTTP Request. In the OnActionExecuing() method the logic for validating the Model object. The code for ValidatorFilterAttribute class is shown in Listing 3
public class ValidatorFilterAttribute : ActionFilterAttribute { public string? ModelName { get; set; } = null!; public override void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { context.Result = new BadRequestObjectResult(context.ModelState); } var modelValidator = new EmployeeValidator(); var model = (Employee)context.ActionArguments[ModelName!]!; var validationErrors = modelValidator.Validate(model); if (!validationErrors.IsValid) { validationErrors.Errors.ForEach(err=> { context.ModelState.Remove(err.PropertyName); context.ModelState.AddModelError(err.PropertyName, err.ErrorMessage); }); } context.Result = new BadRequestObjectResult(context.ModelState); } }
Listing 3: The code for ValidatorFilterAttribute class
Step 5: In the Controllers folder, add a new Empty API Controller named EmployeeController.cs. In this controller we will have the Post() action method. In this method we will apply ValidatorFilter action filer which we have created in Step 4. The code the EmployeeController is shown in Listing 4
[HttpPost] [ValidatorFilter(ModelName ="Employee")] public IActionResult Post(Employee employee) { return Ok(employee); }
Listing 4: The EmployeeController
As shown in Listing 4, the ModelName property of the action filter named ValidationFilter is set to Employee. This means that the ValidationFilter will validate the Employee class.
Run the application and test the HTTP Post method, when we are not sending values from EmpName and DeptName properties of the Employee class, the validation errors will be returned using the Action Filter ad shown in Figure 1
Figure 1: The Result for Validation
The code for this article can be downloaded from this link.
Conclusion: In ASP.NET Core the model validation is one of the most important steps. To make sure that we have the high reusability for validations, it is better to use the Custom Action Filter.