I am using .NET Core 3.1 to develop REST API. This is the controller that I am using (stripped down to basics just to demonstrate the issue, it returns the same data as it received):
OrdersController.cs
[Route("Payments/[controller]")]
[ApiController]
public class OrdersController : Controller
{
[HttpPost]
[Route("AddOrder")]
public IActionResult AddOrder(Order order)
{
return Json(order);
}
}
Order.cs
public class Product
{
[Required]
public string Manufacturer { get; set; }
[Required]
public string Code { get; set; }
}
public class Order
{
[Required]
public string Recipient { get; set; }
[Required]
public Product Product { get; set; }
}
When I call Payments/Orders/AddOrder
with Postman with the following body (notice empty nested Code
field):
{
"Recipient": "John Doe",
"Product": {
"Manufacturer": "Company Inc.",
"Code": ""
}
}
... I get the following error which is expected since Code
is annotaded with [Required]
:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|7558e077-4f9147019767a0cf.",
"errors": {
"Product.Code": [
"The Code field is required."
]
}
}
However, if I try to validate the Order
object with the same data in one of the services, it doesn't detect that field Code
is empty.
// manually initialized order with same data as in Postman request
Order order = new Order()
{
Recipient = "John Doe",
Product = new Product()
{
Manufacturer = "Company Inc.",
Code = string.Empty
}
};
ValidationContext context = new ValidationContext(order, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(order, context, validationResults, true);
Here, isValid
is true
which means that object is valid. How can it be valid if Code
is empty? Why does controller automatically detect that nested property is invalid but Validator.TryValidateObject
doesn't? Does controller validation work recursively and Validator.TryValidateObject
does not? Can I use the same recursive (nested fields) validation that controller uses somewhere else in the code?
EDIT: Why do we even want to validate the object on the service layer?
We developed a shared project to be used in other solutions. It calls REST API with correctly formatted payload and headers. We want to validate object inside shared project's code (service) before it is even sent to REST API server. We want to avoid situations where request is sent out by the shared client but is then rejected at REST API's server.