Introduction
When building an ASP.NET Core Web API, one of the most important things is making sure that the data coming into your API is correct and safe. If invalid or incomplete data reaches your business logic, it can cause bugs, unexpected behavior, or even security issues.
This is where request validation becomes important. Instead of writing validation code again and again inside every controller, ASP.NET Core provides a clean solution using filters.
A request validation filter allows you to validate incoming API requests before they reach your controller logic. This helps you build clean, scalable, and production-ready Web APIs.
In this article, you will learn step by step how to implement a request validation filter in ASP.NET Core Web API using simple words, real examples, and best practices.
What is a Request Validation Filter?
A request validation filter is a custom component in ASP.NET Core that runs before your API action method executes.
In simple terms, it checks the request data first. If the data is valid, the request continues to the controller. If not, it stops the request and returns an error response.
This approach is very useful because:
You don’t need to repeat validation logic in multiple controllers
Your controller code stays clean and focused on business logic
Validation becomes reusable and easy to manage
For example, if your API expects a user object, the filter can check if the name, email, and age are valid before processing the request.
Understanding Filters in ASP.NET Core Web API
ASP.NET Core provides different types of filters that run at different stages of the request pipeline.
Here are the main types in simple words:
Authorization Filters → Used for checking authentication and permissions
Resource Filters → Run before and after request processing
Action Filters → Run before and after controller actions (most useful for validation)
Exception Filters → Handle errors globally
Result Filters → Run before and after sending the response
For request validation in ASP.NET Core Web API, we mainly use Action Filters because they run just before the controller action executes.
Step 1: Create a Request Model
First, we create a model that represents the data coming from the client.
public class UserRequest
{
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
This model represents the request body of your API. Now we will validate this data using a custom filter.
Step 2: Create a Custom Request Validation Filter
Now we create a custom validation filter using IActionFilter.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class ValidateUserRequestFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ActionArguments.ContainsKey("user"))
{
context.Result = new BadRequestObjectResult(new
{
Message = "Request body is missing",
Status = 400
});
return;
}
var user = context.ActionArguments["user"] as UserRequest;
if (user == null)
{
context.Result = new BadRequestObjectResult(new
{
Message = "Invalid request format",
Status = 400
});
return;
}
if (string.IsNullOrWhiteSpace(user.Name))
{
context.Result = new BadRequestObjectResult(new
{
Message = "Name is required",
Status = 400
});
return;
}
if (string.IsNullOrWhiteSpace(user.Email))
{
context.Result = new BadRequestObjectResult(new
{
Message = "Email is required",
Status = 400
});
return;
}
if (user.Age <= 0)
{
context.Result = new BadRequestObjectResult(new
{
Message = "Age must be greater than 0",
Status = 400
});
return;
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// No logic required after execution
}
}
In simple words, this filter checks:
Whether the request body exists
Whether the object is valid
Whether required fields are filled
Whether values are correct
If any condition fails, it immediately returns a BadRequest response.
Step 3: Register the Filter in Dependency Injection
Now we need to register this filter so ASP.NET Core can use it.
builder.Services.AddScoped<ValidateUserRequestFilter>();
This step is important because it allows the filter to be injected and used across the application.
Step 4: Apply the Filter to Controller or Action
You can apply the validation filter in different ways depending on your requirement.
Apply at action level:
[HttpPost]
[ServiceFilter(typeof(ValidateUserRequestFilter))]
public IActionResult CreateUser(UserRequest user)
{
return Ok("User created successfully");
}
Apply at controller level:
[ApiController]
[Route("api/[controller]")]
[ServiceFilter(typeof(ValidateUserRequestFilter))]
public class UsersController : ControllerBase
{
[HttpPost]
public IActionResult CreateUser(UserRequest user)
{
return Ok("User created successfully");
}
}
In simple words:
Step 5: Apply Filter Globally (Optional)
If you want to apply validation across all APIs, you can register it globally.
builder.Services.AddControllers(options =>
{
options.Filters.Add<ValidateUserRequestFilter>();
});
This is useful in large ASP.NET Core Web API applications where you want consistent validation everywhere.
Step 6: Test the API with Example Requests
Valid request:
{
"name": "Rahul",
"email": "[email protected]",
"age": 25
}
Invalid request:
{
"name": "",
"email": "",
"age": 0
}
Response:
{
"message": "Name is required",
"status": 400
}
This shows how the filter stops invalid requests before reaching the controller.
Cleaner Approach Using ActionFilterAttribute
Instead of implementing IActionFilter, you can use ActionFilterAttribute to make the code shorter and cleaner.
public class ValidateUserRequestFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var user = context.ActionArguments["user"] as UserRequest;
if (user == null || string.IsNullOrWhiteSpace(user.Name))
{
context.Result = new BadRequestObjectResult(new
{
Message = "Invalid request data",
Status = 400
});
}
}
}
This approach is easier to read and commonly used in real-world projects.
Filters vs Model Validation
| Feature | Filters | Model Validation |
|---|
| Reusability | High | Medium |
| Complexity Handling | Good for complex logic | Best for simple rules |
| Centralized Validation | Yes | Limited |
| Use Case | Cross-cutting validation | Field-level validation |
In simple terms:
Best Practices for ASP.NET Core Web API Request Validation
Keep validation logic separate from controller logic
Use filters to reuse validation across APIs
Combine filters with Data Annotations for better results
Always return structured error responses
Avoid putting business logic inside validation filters
Use logging for debugging validation issues in production
Real-World Use Cases
Request validation filters are widely used in real-world ASP.NET Core Web API applications:
Validating request payloads in microservices architecture
Ensuring required fields before database operations
Standardizing API error responses
Protecting APIs from invalid or malicious input
Summary
In ASP.NET Core Web API, implementing a request validation filter is a clean and scalable way to ensure that only valid data reaches your application logic. It helps you centralize validation, reduce duplicate code, and keep your controllers simple and maintainable. By using action filters, you can validate requests efficiently, improve API reliability, and follow best practices used in real-world production applications.