34 thoughts on “ASP.NET WEB API Validation (A One More Better Approach)

  1. This is good stuff! I wish more people thought about validation when they were giving demos or writing code. It’s not the most fun in the world but it is certainly a requirement that invariably shows up too late in the process as an afterthought and usually ends up a mess. This looks like a pretty painless way to keep your _possibly complicated_ validation very close to the objects that are being validated and avoid DataAnnotations which definitely falls apart for non trivial use cases.

    I’ll have to give this a shot next time I’m doing validation, much better than other ways I have tried (ranging from DataAnnotations to custom built ‘fluent validation helpers’)

  2. brettedotnet says:

    Thanks John,

    Validation for me, is a big pet peeve. As you said it always ends up being an afterthought. The sad part is, that in business applications, validation is one of the most important aspects of usability. This is why preventing the, ‘every screen is a new adventure’ experience is so important! A well thought out solution that allows developers a clear way to manage validation development is so important. All in all, it’s not the fun stuff, but worth mention and consideration.

    BDN

  3. … so, where is your GitHub project for this…? 😉

  4. If you try to follow our tutorial above with the latest FluentValidation from Nuget – it doesn’t work at all 😉

    • brettedotnet says:

      Really? I’ll have to try that. Thanks for letting me know.

    • brettedotnet says:

      Christian,

      Thanks so much for pointing this out. I have updated the project with the new version of Fluent. Hopefully that will work for you. I don’t think it was a version issue though. I actually copied the wrong version of the project out there, and there was some namespace issues that were causing the app to x-plode.

      I really appreciate the feedback.. How embarrassing.

  5. OK.
    As said, a Github repo would help most of your readers.

  6. Nice! 🙂

    BTW: do you think validating (outbound) objects makes sense when you do a GET…? 😉

    • brettedotnet says:

      I am sure there are some scenarios where that may make sense. However, I have never come across one. In a typical set up your outbound objects are usually ViewModels built up from your domain objects. So I suppose if you wanted to do such a thing, you would need to explicitly call the validation routines prior to the mapping/translation from domain to ViewModel. At that point you could return the same error response.

      Do you have a specific scenario in mind?

      • cweyer74 says:

        I actually think there is no such scenario.
        Usually I use DTOs everywhere in my API/Services. And sometimes the inbound and outbound DTOs are the same.

        SO, if I use the same DTO (like ReservationDTO) for e.g. GET and POST, then your validation approach gets into my way… makes sense?
        (I know that you will be proposing to use different DTOs…)

      • brettedotnet says:

        I generally will use the same DTO for puts and posts. However, for Gets I will generally use the resource identifier. That said, I am unclear how it would get in your way, if you used it the same DTO for a GET. Model binding would run, then model validation, the same way it does if you were to use data annotations as opposed to this approach. The model validation process of the Web API is what drives both approaches. There may be mechanisms you could use to prevent Model Validation from running on Get requests (would have to do some digging on this).

        I have a feeling I am not fully understanding you. Are you suggesting that this approach, somehow behaves differently than the more conventional use of data annotations?

        Thanks,
        B

  7. Also, the fact that Reservation has to know about ReservationValidator and vice versa does not feel right.

    public class ReservationValidator : AbstractValidator

    public Reservation()
    {
    _validator = new ReservationValidator();
    }

  8. j says:

    Hi Brett,

    Thanks for your efforts in getting github going. It has really helped a lot in getting the points across.

    However, there seems to be something I am missing.

    Running your ResIt example, I have left FirstName and Lastname blank, then clicked “Save”.

    I get the following JSON result back.

    {“Message”:”The request is invalid.”,”ModelState”:{“reservation”:[“‘First Name’ should not be empty.”,”‘Last Name’ should not be empty.”]}}

    I would have expected “reservation.firstname” and “reservation.lastname” in the modelstate instead of just “reservation”. Because of this, how am I going to know on the client which field this validation error was for?

    Thanks,

    j

  9. j says:

    thanks b,

    i just realized that this is fine for experimental builds but in my production code i would not/cannot use these pre-release nightly builds. what do you think is a workaround if you have to use the asp.net webapi release without this fix?

    i thought about:

    removing IValidatableObject from the reservation class, so that validation does not get called automatically by mvc, and create a custom model binder that calls our validation code, and fills our modelstate appropriately. our ValidationResponseFilter will remain in there to handle validation error responses as normal.

    what would you do?

    regards,

    j

    • brettedotnet says:

      Hey J,

      I feel your pain! It’s why I am so happy they have fixed this defect. That said, I do have a work around that I had to use when I was in the precise situation you are. I think you are on the right track. However, I would not remove the IValidatableObject interface, since this is a key part of the model binding/validation process. Here is what I would do to get around this.

      1. Alter the ToValidationResults() extension method so that it adds the property name into the error message such as “~FirstName~First Name is Required”. You can do this because the FluentValidation.Results.ValidationResult.Errors property has the property names, and in the extension method you have access to them. Make sure to prefix and suffix the property names with something that you can parse later. I used the ‘~’.
      2. In the ValidationResponseFilter parse clear model state, and parse out the property names and re-add the error message to model state wit their names.

      This is the best I could come up with when I needed to do it. I like this solution because it allows the framework to behave as it was intended to. But, as all things in dev, it depends on your situation!

      Hope this helps,
      Brette

      P.S. I may have some sample code this approach laying around if you want it email me.

      • j says:

        hi b,

        i ended up doing it a bit differently but in the same line as what i outlined above. however your way is better as the model validation still happens in the same place where it should be.

        i removed IValidatableObject, and called validate manually in the ValidationResponseFilter, then converted the result to some other form ready to be sent to the client with a “BadRequest” error. once on the client my angularjs code takes care of it easily.

        great stuff..

        j

      • brettedotnet says:

        Nice man! Glad you got it working!

  10. Nate says:

    what tool did you use to do the sequence diagram?

  11. Nate says:

    Is there no way to bootstrap fluent validation like you can in MVC so you dont have to implement the IValidatableObject interface?

  12. JAR says:

    Hi @Nate,

    Could you finally to bootstrap validation? I would like to avoid implement IValidableObject interface also.

    Thanks.

  13. pushker1511 says:

    Reblogged this on Javascript Point.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: