Why Validation?
Validation in APIs is crucial to protect your server from receiving bad data from the users. Your server only want to get data that comply with the rules we define in validation. In case the data doesn’t meet the requirement, then that data will be rejected and server will send back a HTTP 400 bad request instead of 500 Internal Server error.
There are few approaches that can be taken into account using Data Annotations or Fluent Validation.

Data Annotations

Considering this example above, we specify attributes above the class properties that we want to validate against. Most common attributes are: Required (must have value), StringLength (Set maximum length), Range (value must be in between the range), DataType… For more strict rules, you can apply RegularExpression to limit what users can input (ex: must use letters, no special characters).
Fluent Validation
An alternative way to validate your data and it applies for .NET Core, .NET MVC, .NET Web API. First you add the FluentValidation Nuget package by typing command dotnet add package FluentValidation
then do some configurations in startup.cs. Read more here.
Below is an example (implementing CQRS pattern), using FluentValidation to validate your command. The approach here is to create a CommandValidator class that inherits from AbstractValidator (a class provided by FluentValidation).
Hence, whenever a movie is passed to a Create Command, the CommandValidator will validate against those properties inside that movie. If the command fails during validation process, that command is rejected and will not proceed further to the Handler. Users then get back a 400 Bad Request.
public class Create { public class Command : IRequest { public Guid Id { get; set; } public string Title { get; set; } public string Description { get; set; } public int Score { get; set; } public string Category { get; set; } } public class CommandValidator: AbstractValidator<Command> { public CommandValidator() { RuleFor(x => x.Title).NotEmpty().Length(3, 30); RuleFor(x => x.Description).NotEmpty().Length(3, 500); RuleFor(x => x.Score).NotEmpty().InclusiveBetween(1,10); RuleFor(x => x.Category).NotEmpty().Length(3, 20); } } public class Handler : IRequestHandler<Command> { private readonly DataContext _context; public Handler(DataContext context) { _context = context; } public async Task<Unit> Handle(Command request, CancellationToken cancellationToken) { var movie= new Movie { Id = request.Id, Title = request.Title, Description = request.Description, Category = request.Category, }; _context.Movies.Add(movie); var success = await _context.SaveChangesAsync() > 0; if(success) return Unit.Value; throw new Exception("Problem occurs"); } } }
It’s more of personal preference in which method you choose to use to validate your data. There are pros and cons in both approaches. However, in my opinion, Data Annotations is suitable when you work with small projects. For bigger ones, FluentValidation gives you a better option to keep your validation logic in separated concern.
