0

Has anyone used FluentValidation in conjunction with a Refit Client?

As part of a system I'm working on, I call out to various REST services and I'd like to use FluentValidation to validate the typed responses in a uniform manner.

Obviously I could manually validate after each call to the Refit client inline but I was wondering whether there was any scope to automatically find relevant validators and invoke them.

I guess I'm looking at something similar to the validation behavior pattern in Mediatr.

A couple of thoughts I had were:

1. Decorate each client interface with a validating wrapper

This would require using an IoC container which supports decoration and would mean I'd have to create an implementation of each client interface which would then validate the responses coming back from refit.

2. Add a validating DelegatingHandler to the pipeline

This would probably end up having to deserialize the content - perform the validation - and then return the response to the next handler in the chain. This is probably OK where the content is small...

3. Use a custom validating JsonConverter

Here I would wrap a Json.Net converter and in the Deserialize perform the validation.

Or is there some feature of Refit I've missed which would help with this, or does what I'm suggesting just seem crazy?!

Thanks,

Nick.

Nick
  • 25,026
  • 7
  • 51
  • 83
  • 1
    I'd use a custom ContentSerializer, which wraps `NewtonsoftJsonContentSerializer` but also does your validation, then returns a validated deserialized object. I wouldn't do anything in the `DelegatingHandler`, as that's before e.g. the response code is checked – canton7 Jan 08 '21 at 12:00
  • @canton7 yeah I'm beginning to think that's the best way to go. I was concerned that it was going to force JSON on the user however, IContentSerializer is format-agnostic so it could just as easily wrap an XML serializer. I guess I just figured it might be a useful feature of Refit to be able to intercept typed requests and responses within the library. This feels like abusing the SRP a bit. – Nick Jan 08 '21 at 12:05
  • 1
    Eh, as the author of a competing library which takes a similar approach here, I see the role of the deserialization layer to return a deserialized, *valid* object representation of the response. Json.net will do some level of validation here (invalid json strings are rejected for instance), but additional model-level validation isn't really much different, and lives at the same layer. I'd avoid having multiple injection points at the same layer with only semantic differences between them -- that way lies confusion – canton7 Jan 08 '21 at 12:08
  • Yeah, validation is definitely something related to deserialization so you're right. I guess no-one else has had any other cross-cutting concerns which would warrant adding a behavior-like pattern to Refit. – Nick Jan 08 '21 at 12:17
  • PS. What's your competing library called? – Nick Jan 08 '21 at 12:19
  • 1
    [RestEase](https://github.com/canton7/RestEase) – canton7 Jan 08 '21 at 13:14

1 Answers1

1

I have used mediatr and refit and we used this sort of pattern:

  • (In a generic pipeline handler) Validate the command/query into the handler(s)

    If fail => 400 with standard validation result(s)

  • Call handler .....

    Make the http call in the handler

    Response from api come back.

    If refit apiResponse bad throw 400 with validation errors ==>

    (In a generic pipeline handler) check the apiResponse. If 400 look for validation errors (was our own stuff so we could deal with it) and pass them back to the caller with 400 and standard validation result(s)

All of this would flow through the api layers with minimal (no) dev input and the UI would display the results in its own way.

And if you want to use a "standard" error response maybe look at this: https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails?view=aspnetcore-5.0 Used in my latest project!

Worked like a charm!

PaulD
  • 206
  • 1
  • 5