We will learn multiple ways to validate input requests in .NET core Api.
Validating API input is crucial for ensuring data integrity, security, and the overall functionality of your application. When designing a RESTful API (or any kind of web service), it’s important to validate the inputs before processing them

We will implement validation request using Custom Middleware
- Custom Middleware for input validation
Additionally, we will also implement with below approaches
2 . Model Validation with Data Annotations
3. Validation Requests using Action Attributes
1. Custom Middleware for Validating Input
We will be creating custom middleware to add validation for all input requests and that will be centralize code for all api endpoints.
Above will be flow diagram for .NET core Api request processing and we added middleware which will work like if Validation fails then middleware will send response back to User.
Steps for implementations
a. Create RequestValidationMiddleware class
b. Inject RequestDelegate object in contructor : this will take care to pass request to next middleware in pipeline.
c. Add logic in InvokeAsync method which will take Httpcontext as input request and now we will have all data which input request should have.
d. Register middleware in Startup.cs.
Code below
public class RequestValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestValidationMiddleware> _logger;
public RequestValidationMiddleware(RequestDelegate next, ILogger<RequestValidationMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext httpContext)
{
// Check if the request is JSON (Content-Type: application/json)
if (httpContext.Request.ContentType?.ToLower() == "application/json")
{
// Read the body content
var body = await new StreamReader(httpContext.Request.Body).ReadToEndAsync();
if (string.IsNullOrWhiteSpace(body))
{
httpContext.Response.StatusCode = 400; // Bad Request
await httpContext.Response.WriteAsync("Request body cannot be empty.");
return;
}
// Here you would usually deserialize the body into a specific object (e.g., User model)
// Let's simulate checking for a required field (for example, Name is required)
if (!body.Contains("Name"))
{
httpContext.Response.StatusCode = 400; // Bad Request
await httpContext.Response.WriteAsync("Name is required.");
return;
}
}
// Proceed to the next middleware or controller if validation passes
await _next(httpContext);
}
}
Endpoint code
[HttpPost]
public IActionResult CreateUser([FromBody] User user)
{
return Ok($"User {user.Name} created successfully!");
}
///
///Model class
public class User
{
public string Name { get; set; }
public string Email { get; set; }
}
Testing
Input : Valid Data.
{
"name" :"Deveshomar"
"email": "sanjay@gmail.com"
}
When we execute this request middleware will call and below code will execute: we can see we are getting JSON data which we are passing from user input.

we can add all required validations and sending back with 400 statuses if data is not good.
Testing with Invalid Data :
We are not passing name and getting validation error.
{
"email": "sanjay@gmail.com"
}

2. Model Validation with Data Annotations
One of the most common and clean ways to validate input in ASP.NET Core is using data annotations. This technique uses attributes to define validation rules on model properties.
Let’s have a code below, we are going to create user model and will add some validation attributes.
public class UserModel
{
[Required]
[StringLength(50, MinimumLength = 5)]
public string Name { get; set; }
[EmailAddress]
public string Email { get; set; }
[Range(18, 100)]
public int Age { get; set; }
}
We have defined Range for Age, Emailaddresss and Name and mandatory.
Validation Code at API end point
public class UserController : Controller
{
[HttpPost]
public IActionResult Post(UserModel user)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState); // Returns validation errors to the client
}
return Ok();
}
}
We have used Modelstate which is defined on controller base class and it take care for all validations.

Testing: below is running screen of API and we have added valid input.

Negative scenario Testing.
a. I have removed name from input, now we should get error message as we made it required.
{
"email": "user@example.com",
"age": 34
}
Error as expected.

b. Let’s make Age as out of range like 150.
{
"name": "devesh",
"email": "user@example.com",
"age": 150
}
Output

we can see we are getting expected validation messsage when we pass invalid input to API.
3. Validation Requests using Action Attributes
we have one problem in above approach, we have to add code to validate input inside controller action.
so if we have 100 of controllers then we need to add similar code on each endpoint or function which some time difficult to manage.
in this case we will create centralize place from where request get validate.
We are going to create Actionfilter class via below way and will validate all input data inside OnActionExecuting method.
public class ValidateInputActionFilter : ActionFilterAttribute
{
public ValidateInputActionFilter()
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// Check if the model state is valid
if (!context.ModelState.IsValid)
{
// If the model state is invalid, log the error and return a bad request response
context.Result = new BadRequestObjectResult(context.ModelState);
return; // Prevents the action method from being called
}
base.OnActionExecuting(context);
}
}
Apply Attribute.
[ValidateInputActionFilter]
[HttpPost]
public IActionResult Post(UserModel user)
{
return Ok();
}
}
Testing
a. Invalid Email
{
"name": "string",
"email": "usera@@@@@@example.com",
"age": 100
}
Result. we can see this is working and getting validation message.

Conclusion:
We have learnt 3 approaches but best approaches will be custom middleware because by using middleware for validation, you can centralize all your validation logic in one place, making it easier to maintain and update.
Thanks
#coding, #bestpractice , #dotnetcore, #api
Comments
Post a Comment