Beyond Basics: Maximizing Data Integrity with Field Validation in REST Requests with .NET 7 and FluentValidation

Victor Magalhães
5 min readAug 14, 2023

Field validation in REST application requests plays a pivotal role in ensuring data integrity and security as data is transmitted to the server. This practice encompasses the meticulous examination and appropriate handling of data submitted by clients, often through HTTP requests, prior to the processing or storage of said data on the server. The act of validation serves as a robust defense against errors, security vulnerabilities, and data inconsistencies that have the potential to adversely impact the application’s performance and reliability.

Validation procedures predominantly transpire on the server side, where data reception and processing transpire. This strategic placement thwarts any attempts at data manipulation or tampering by clients, effectively mitigating the risk of unauthorized modifications perpetrated by malicious entities.

Here are some fundamental aspects integral to comprehensive field validation within REST applications:

Format validation

This facet assures the correctness of data format, encompassing valid email structures, correctly formatted integers or decimals, accurate date representation, and more.

Limit validation

By assessing whether values fall within acceptable ranges, such as numeric values adhering to specific parameters or strings conforming to predetermined minimum and maximum lengths, limit validation fortifies data quality.

Presence validation

This crucial dimension warrants that obligatory fields remain devoid of emptiness or absence, ensuring that vital data components are consistently provided.

When a validation fails

It is crucial to provide clear and informative error messages to the client. This aids users in comprehending the issue and rectifying the data before resubmitting the request.

The server must adeptly manage validation errors. This entails responding to validation errors with appropriate HTTP status codes (such as 400 Bad Request) and furnishing detailed information regarding the encountered error.

Block!

Handling this complexity demands careful planning and a systematic approach. Frameworks and libraries that provide validation and error-handling capabilities can be invaluable in streamlining this task. Nevertheless, it’s important to remember that, while intricate, field validation is a critical component in ensuring the reliability and security of data manipulated by a REST application.

The FluentValidation library is a popular tool for data validation in C#/.NET applications. It offers a more flexible and declarative approach to validating objects, fields, and properties compared to the traditional attribute-based approach. The primary purpose of the FluentValidation library is to simplify and enhance the data validation process in your applications, making it more readable, reusable, and configurable.

Setting up a scenario

In our scenario, our application will receive the following request model and will need to validate each of the fields.

{
"name": "Victor",
"birthedAt": "2023-08-14",
"email": "valid@email.com",
"gender": "male",
"phoneNumber": "+558299999999"
}

To map this, we will have a similar class as follows:

public class CreateEmployeeInput
{
public string Name { get; init; }
public DateTime? BirthedAt { get; init; }
public string Email { get; init; }
public string Gender { get; init; }
public string PhoneNumber { get; init; }
}

Now, let’s create a class that extends the AbstractValidator class so that we can make use of FluentValidation.

public class CreateEmployeeInputValidate : AbstractValidator<CreateEmployeeInput>
{
public CreateEmployeeInputValidate()
{
RuleFor(x => x.Name)
.NotEmpty()
.MinimumLength(5)
.MaximumLength(50)
.Matches("^[A-Za-z ]+$")
.WithMessage("'{PropertyName}' invalid format.");

RuleFor(x => x.BirthedAt)
.NotNull()
.GreaterThan(DateTime.MinValue)
.LessThan(DateTime.UtcNow);

RuleFor(x => x.Email)
.NotEmpty()
.EmailAddress();

RuleFor(x => x.Gender)
.NotEmpty()
.IsEnumName(typeof(Gender));

RuleFor(x => x.PhoneNumber)
.NotEmpty()
.Matches("^\\+55\\d{10,11}$").WithMessage("'{PropertyName}' invalid format.");
}
}

Understanding the Code

RuleFor: This is a fundamental method for defining validation rules for a specific property. It accepts a lambda expression that specifies the property to be validated and allows chaining various methods to configure different types of validations, such as size validation, presence, format, among others.

WithMessage: Method allows customization of error messages associated with specific validation rules.

NotEmpty: This method is used to validate if a property is not empty. It’s applied to strings, collections, or other structures that have a concept of being empty or not.

NotNull: It’s used to validate if a property is not null. It’s employed for reference objects.

GreaterThan and LessThan: These methods are used to compare if the value of a property is greater or less than a specific value, respectively.

MinimumLength and MaximumLength: validate the length of string properties.

EmailAddress: It’s used to validate if a string is a valid email address.

IsEnumName: It’s used to validate if a string corresponds to the name of an enumeration value.

Matches: It’s used to validate if a string matches a pattern defined by a regular expression.

FluentValidation offers a wide range of methods to address various validation needs. In addition to the ones mentioned earlier:

  • CreditCard: Validates if a string represents a valid credit card number.
  • CreditCard (with card type): Validates if a credit card number is valid based on a specified card type (Visa, MasterCard, etc.).
  • ScalePrecision: Validates if a numeric property has a specific scale and precision.
  • MustNot: Validates if a condition is not met by a property value.
  • Equal (with comparison type): Validates if a value is equal to another value based on a specified comparison type (ordinal, case-insensitive, etc.).
  • NotEqual (with comparison type): Validates if a value is not equal to another value based on a specified comparison type.
  • Length (with exact length): Validates if a string property has an exact length.

Checking if the fields are valid

var validate = new CreateEmployeeInputValidate().Validate(createEmployeeInput);

if (!validate.IsValid)
//BadRequest?

//Continue

Building a good invalid response

We can customize a class to simplify the response. We can achieve this by utilizing a class that takes the FluentValidation’s ValidationResult as a constructor parameter.

public record FieldValue(string Field, string Value);
public class CustomValidationResult
{
public IList<FieldValue> Errors { get; init; }

public CustomValidationResult(string field, string value)
{
Errors = new List<FieldValue>() { new FieldValue(field, value) };
}

public CustomValidationResult(FluentValidation.Results.ValidationResult result)
{
if (result is not null)
Errors = result.Errors.Select(e => new FieldValue(e.PropertyName, e.ErrorMessage)).ToList();
}

public void Add(string field, string value)
{
Errors.Add(new FieldValue(field, value));
}
}

Now we can return this CustomValidationResult

Below is a template of how a response would look when some invalid data has been submitted:

{
"errors": [
{
"field": "Name",
"value": "The length of 'Name' must be at least 5 characters. You entered 3 characters."
},
{
"field": "BirthedAt",
"value": "'Birthed At' must be less than '8/14/2023 6:04:42 PM'."
},
{
"field": "Email",
"value": "'Email' is not a valid email address."
},
{
"field": "Gender",
"value": "'Gender' has a range of values which does not include 'ABC'."
},
{
"field": "PhoneNumber",
"value": "'Phone Number' invalid format."
}
]
}

Conclusion

In the ever-evolving landscape of software development, field validation within REST applications emerges as a cornerstone, safeguarding data integrity and user security. By meticulously validating data formats, enforcing presence, and setting limits, applications become resilient against errors and unauthorized access. The FluentValidation library, a beacon of efficiency in C#/.NET, simplifies this complex process. Through this practice, we embrace the commitment to quality, trust, and the fortification of modern software ecosystems, ensuring reliable and secure interactions between clients and servers.

Seems valid to me

Social Media

LinkedIn: https://www.linkedin.com/in/ovictormagalhaes/

Github: https://github.com/ovictormagalhaes

--

--