15

I have a asp.net Core web API with the following structure:

View Layer: API endpoints
    |
    V
Controller Layer: Controller classes implementing endpoints
    |
    V
Business Logic Layer: Services
    |
    V
Data Access Layer: Proxy classes to our backend

There are a series of endpoints that all follow this structure. Most of the APIs are purely data queries to a backend, but some of the APIs do allow for the calling client to submit data, as well.

What I'm struggling with is finding a clean way to consolidate the various validations that must occur on this submitted data.

My original intent was that the Controller layer would be very simple and hand off all real "work" (including validation) to the Services layer, and the Controller layer would only be responsible for sending the appropriate HTTP response. My issue, though, is that with the service doing all the real work, how best to communicate back up to the Controller what it should be returning.

I can capture some of the basic validations using data annotations, and in most cases that might actually be enough in terms of data validation. But that doesn't cover other issues like more complex validations, or failures in the service/data access layer WRT read/writes, etc.

Some ideas I've mulled over:

  • Make the services layer aware of the IActionResult interface and make it responsible for determining what to return to the calling client. This then mixes the Controller and Service layer, but makes the Controller layer pretty lean.

  • Create various "OperationResults" objects for the various Services calls which would encapsulate any error messages, exceptions, error codes, etc, to be interpreted by the Controller layer to determine what http response to send back to the client. This is a cleaner separation since the Service layer wouldn't mix with the http code at all, but does then turn the Controller classes into a big set of switches to determine what to send back.

  • Split up the validation based on what makes sense to capture at the Controller vs. Services level. For example, a controller being passed a null object to save should clearly just reject it and return BadRequest(), but it seems redundant to add the check there when the services layer would have its own reasons for wanting to check that, too.

I'm admittedly somewhat new to .net web API and .net core in general, so if there's some obvious functionality available I'm not taking advantage of, I'd love to know.

Any advice would be appreciated, thanks.

Josh Kitchens
  • 1,080
  • 11
  • 18
  • Exceptions should be raised ASAP you find something wrong according to your model so if it could be catch at controller don't leave it to business model. If some functionality is necessary but redundant then it rings the bell for inheritance. – Xaqron Apr 11 '17 at 20:54
  • 3
    The short answer is put it in your controller actions. No matter what the architecture, the controller is the first line of defense. The service layer should not have any form validation whatsoever. In fact your service layer should not even know what an IActionResult is. Check out [FluentValidation](https://github.com/JeremySkinner/FluentValidation). It is a mature, stable, and wildly popular validation library. If you narrow down your question a bit, I would be happy to give some examples but in it's current form its a bit broad. – trevorc Apr 12 '17 at 03:24
  • 1
    I somewhat agree, but the service layer should have validation too, especially for a public service or one that is consumed on multiple fronts (unless you don't care about stability and security). Remember, it can be called from other sources that you have no control over. – MC9000 Jan 24 '23 at 06:38

1 Answers1

6

The main idea is correct - you need to separate normal and error flows in your code. One of the main approaches is to use the .NET Exceptions to indicate that normal flow is not possible.

  • For input validation use ActionFilter. You may have global filters for all controllers or define specific per action. See Filters section in documentation.

  • During controller action execution you should raise exceptions as soon as possible and stop further execution. And yes, the Exception may be raised on any of the levels (Service/Business layer, DA layer, etc).

How to handle raised exception?

Using provided by ASP.NET Core error handling approaches (like ExceptionHandler, or Exception Filters), you may to analyze exceptions and generate appropriate/different responses accordingly. See related SO Error handling in ASP.NET Core 1.0 Web API (Sending ex.Message to the client) question for example. There is also the error-handling section in documentation.

Community
  • 1
  • 1
Set
  • 47,577
  • 22
  • 132
  • 150
  • What shall I do, if I need localized error messages? How about types of exceptions, do we need to create specific exceptions for specific validation rules? – Yurii N. Jul 06 '17 at 12:01