There is a philosophical aspect to request validation. When receiving user input you must make sure it is safe to process, and multiple libraries and frameworks exist to help you with this. Also, when creating instances in your domain you must make sure they can never be in an invalid state. Determining whether a – let us say – SalesQuote object can be instantiated in a valid state can require fairly elaborate checks, cross-referencing against caches or even a data store. Surely – writing custom validators and plugging them into the ASP.NET Core validation pipeline should be the perfect fit here? Few lines of code to set up the plumbing, and then leaving your controller actions to work with valid domain objects after invalid requests have been automatically refused. Nirvana?
When reading the Twitter timelines of members the team behind ASP.NET Core, you notice a focus on cross-platform performance and benchmarks. The framework does not provide training wheels like synchronisation contexts and request buffering, meaning that if you do things right you can have blazing performance in the millions of requests per second, vs tens or hundreds in ASP.NET of old – but it also means if you do it wrong your site can be knocked offline because you use up all your threads in the thread pool and you need to restart the app.
The Right Thing is non-blocking I/O, which in C# means async/await all the things.
So – back to our validation.
The validation pipeline in ASP.NET Core is synchronous, and the team – with the zeal of somebody who knows they are wrong but is in too deep to back down – is saying nobody needs async validators. Another reason could be that benchmarks do not cover validators, so there is no glory to be had by validating faster than Play on Jetty.
FluentValidation, my current fave in validation frameworks, does offer async validators, and means of calling them asynchronously – but there is no way of using any asynchronous methods in the traditional Mvc validation pipeline. Well, there are – as they have created synchronous wrappers around the asynchronous code – but that type of async-under-sync and fake async consume extra threads and puts the stability of your app at risk.
What is wrong with the ASP.NET Core guys you ask? Well – it’s the philosophy that differs. They would probably even think they are right. They want the validation to be synchronous because they don’t think you should do syscalls in a validator. Why would you put business logic in a http request validator? That’s too close to the metal they might say. Just check that an expected number actually is a number and that data are the sizes you expect, things you can do without blocking code. Domain validation is a domain concern – not a networking concern. I can see the validity – no pun intended – in that line of reasoning.
But things like FluentValidation make such readable code!
RuleFor(q => q.Quantity).GreaterThan(0).MustAsync((q.Quantity, a) => MeetMinimumOrderQty(a));
Simply looking at a validator class makes the full requirements very clear, well, depending on how you name your helper methods.
Having your cake and eating it
There are asynchronous Action Filters in ASP.NET Core. As long as you register your validators in DI you can use an action filter to asynchronously call your validators manually and optionally short-circuit the request if any of the action parameters fail validation, by implementing the OnActionExecuting method, not forgetting to await the next() delegate if you want the call to proceed.