.NET 8 — New Data Annotations
.NET 8 brings new DataAnnotations
that you can use to validate minimum and maximum string lengths, minimum and maximum range for numeric values, specify allowed and denied values, and validate Base64 strings. In this article, I present how to use these new attributes.
For demonstration purposes, I created a .NET 8 Web API project, which contains a Product
model class and a controller with a POST
endpoint. In this project, there is a .HTTP file with examples of the request with different request body (for valid and invalid requests). To send the requests, you can make use of Swagger, Postman, or if you are using Visual Studio you can use the .HTTP file that is in the solution.
If you want to know more about the .HTTP file, check my article “.http Files in Visual Studio”.
I’m going to explain each of the new Data Annotations in the next topic, but below you can see the complete Product
class with all the new attributes:
public class Product
{
[Length(2, 20)]
public string Name { get; set; }
[Range(1, 1000, MinimumIsExclusive = true, MaximumIsExclusive = false)]
public double Price { get; set; }
[AllowedValues("Computers", "Video Games")]
public string Category { get; set; }
[DeniedValues("Printers")]
public string SubCategory { get; set; }
[Base64String]
public string Description { get; set; }
}
This is the endpoint which I’m going to send the HTTP request to demonstrate the validations:
[ApiController]
[Route("[controller]")]
public class ProductsController : ControllerBase
{
[HttpPost]
public IActionResult Post(Product product)
{
if (!ModelState.IsValid) return BadRequest();
return Ok("Valid ModelState");
}
}
LengthAttribute
The System.ComponentModel.DataAnnotations.LengthAttribute
, specifies the lowest and upper bounds for strings or collections. For example, for the property Name
, the minimum value should be 2, and the maximum value should be 20:
[Length(2, 20)]
public string Name { get; set; }
This means that the Name
must contain between 2 and 20 characters, otherwise will not be valid. In the example below, there are two requests with invalid values in the name
property, the first one with only a single character, and the second request with 21 characters:
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "a",
"price": 1000,
"category": "Computers",
"subCategory": "Laptops",
"description": "UHJvZHVjdCBkZXNjcmlwdGlvbg=="
}
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "aaaaaaaaaaaaaaaaaaaaa",
"price": 1000,
"category": "Computers",
"subCategory": "Laptops",
"description": "UHJvZHVjdCBkZXNjcmlwdGlvbg=="
}
The result will be:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Name": [
"The field Name must be a string or collection type with a minimum length of '2' and maximum length of '20'."
]
},
"traceId": "00-58c98391eb6f9120395914371234dca5-687e31c0e69b5339-00"
}
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Name": [
"The field Name must be a string or collection type with a minimum length of '2' and maximum length of '20'."
]
},
"traceId": "00-feced6c3c3be0a1d80246f316ec87e59-2fef27b78e9443e5-00"
}
MinimumIsExclusive & MaximumIsExclusive
The RangeAttribute.MinimumIsExclusive
and RangeAttribute.MaximumIsExclusive
, specifies whether a number is included in the allowable range.
For the Price
property, there is the Range
attribute, which specifies that the minimum value of 1 is exclusive, which means that the minimum acceptable value is 2, and the maximum value of 100 is not exclusive, which means that the maximum value is 100:
[Range(1, 100, MinimumIsExclusive = true, MaximumIsExclusive = false)]
public double Price { get; set; }
Below there are two requests with invalid values in the price
property. The first request contains the value 1, and the second contains the value 101:
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "Surface Pro",
"price": 1,
"category": "Computers",
"subCategory": "Laptops",
"description": "UHJvZHVjdCBkZXNjcmlwdGlvbg=="
}
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "Surface Pro",
"price": 1001,
"category": "Computers",
"subCategory": "Laptops",
"description": "UHJvZHVjdCBkZXNjcmlwdGlvbg=="
}
The result will be:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Price": [
"The field Price must be between 1 exclusive and 1000."
]
},
"traceId": "00-de08f4da83c21e5a16055372d2f45464-ab1c5ed37db857b1-00"
}
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Price": [
"The field Price must be between 1 exclusive and 1000."
]
},
"traceId": "00-de08f4da83c21e5a16055372d2f45464-5927434ebb6c06af-00"
}
Base64StringAttribute
The Base64StringAttribute
validates that a string is a valid Base64 representation.
For the Description
property, there is the Base64String
DataAnnotation
, which means that the value for Description
should be a base64 string:
[Base64String]
public string Description { get; set; }
In the request below there are two requests with an invalid base64 string in the description
:
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "Surface Pro",
"price": 1000,
"category": "Computers",
"subCategory": "Laptops",
"description": "abc#"
}
The result will be:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Description": [
"The Description field is not a valid Base64 encoding."
]
},
"traceId": "00-de08f4da83c21e5a16055372d2f45464-95bc9914949640a3-00"
}
AllowedValuesAttribute & DeniedValuesAttribute
The AllowedValuesAttribute
and DeniedValuesAttribute
specifies allowed and denied values.
For the Category
property, the AllowedValues
are only “Laptop” and “Smartphone”, which means that any value different than these two, will not be valid:
[AllowedValues("Computers", "Video Games")]
public string Category { get; set; }
Below there is a request with an invalid value in the category
property:
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "Surface Pro",
"price": 1000,
"category": "Smartwatch",
"subCategory": "Laptops",
"description": "UHJvZHVjdCBkZXNjcmlwdGlvbg=="
}
The result will be:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Category": [
"The Category field does not equal any of the values specified in AllowedValuesAttribute."
]
},
"traceId": "00-de08f4da83c21e5a16055372d2f45464-226065e5054bd14b-00"
}
For the SubCategory
property, the DeniedValues
are only “Printers”, which means that any value different than that will be valid:
[DeniedValues("Printers")]
public string SubCategory { get; set; }
Below there is a request with an invalid value in the subCategory
property:
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "Surface Pro",
"price": 1000,
"category": "Computers",
"subCategory": "Printers",
"description": "UHJvZHVjdCBkZXNjcmlwdGlvbg=="
}
The result will be:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"SubCategory": [
"The SubCategory field equals one of the values specified in DeniedValuesAttribute."
]
},
"traceId": "00-de08f4da83c21e5a16055372d2f45464-c5c49c88ea3291b5-00"
}
Valid Request Example
Below there is an example of a valid request body, with data that attends all the specified configurations used for the Product:
POST {{DotNet8NewDataAnnotations_HostAddress}}/products
Content-Type: application/json
{
"name": "Surface Pro",
"price": 1000,
"category": "Computers",
"subCategory": "Laptops",
"description": "UHJvZHVjdCBkZXNjcmlwdGlvbg=="
}
This is the result:
Conclusion
The latest DataAnnotations
in .NET allows you to have useful validation capabilities, enabling developers to easily have validations such as minimum and maximum string lengths, minimum and maximum range for numeric values, specify allowed or denied values, and validate Base64 strings. These enhancements provide a useful set of attributes for meeting specific validation requirements in applications.
This is the link for the project in GitHub: https://github.com/henriquesd/DotNet8NewDataAnnotations
If you like this demo, I kindly ask you to give a ⭐️ in the repository.
Thanks for reading!
References