Refactor ApiController Adapters to a single, reusable base class.

Regular readers of this blog will know that I write many RESTful APIs in F#, using ASP.NET Web API. Since I like to write functional F#, but ASP.NET Web API is an object-oriented framework, I prefer to escape the object-oriented framework as soon as possible. (In general, it makes good architectural sense to write most of your code as framework-independent as possible.)

(Regular readers will also have seen the above paragraph before.)

To bridge the gap between the object-oriented framework and my functional code, I implement Controller classes like this:

type CreditCardController (imp) =
    inherit ApiController ()
 
    member this.Post (portalId : string, req : PaymentDtr) : IHttpActionResult =
        match imp portalId req with
        | Success (resp : PaymentDtr-> this.Ok resp :> _
        | Failure RouteFailure -> this.NotFound () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _

The above example is a Controller that handles incoming payment data. It immediately delegates all work to an injected imp function, and pattern matches on the return value in order to return correct responses.

This CreditCardController extends ApiController in order to work nicely with ASP.NET Web API, while the injected imp function is written using functional programming. If you want to be charitable, you could say that the Controller is an Adapter between ASP.NET Web API and the functional F# API that actually implements the service. If you want to be cynical, you could also call it an anti-corruption layer.

This works well, but tends to become repetitive:

open System
open System.Web.Http
 
type BoundaryFailure =
| RouteFailureValidationFailure of stringIntegrationFailure of string
 
type HomeController (imp) =
    inherit ApiController ()
 
    [<AllowAnonymous>]
    member this.Get () : IHttpActionResult =
        match imp () with
        | Success (resp : HomeDtr-> this.Ok resp :> _
        | Failure RouteFailure -> this.NotFound () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _
 
type CreditCardController (imp) =
    inherit ApiController ()
 
    member this.Post (portalId : string, req : PaymentDtr) : IHttpActionResult =
        match imp portalId req with
        | Success (resp : PaymentDtr-> this.Ok resp :> _
        | Failure RouteFailure -> this.NotFound () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _
 
type CreditCardRecurrentStartController (imp) =
    inherit ApiController ()
 
    member this.Post (portalId : string, req : PaymentDtr) : IHttpActionResult =
        match imp portalId req with
        | Success (resp : PaymentDtr-> this.Ok resp :> _
        | Failure RouteFailure -> this.NotFound () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _
 
type CreditCardRecurrentController (imp) =
    inherit ApiController ()
 
    member this.Post (portalId : string, transactionKey : string, req : PaymentDtr) : 
            IHttpActionResult =
        match imp portalId transactionKey req with
        | Success (resp : PaymentDtr-> this.Ok resp :> _
        | Failure RouteFailure -> this.NotFound () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _
 
type PushController (imp) =
    inherit ApiController ()
 
    member this.Post (portalId : string, req : PushRequestDtr) : IHttpActionResult =
        match imp portalId req with
        | Success () -> this.Ok () :> _
        | Failure RouteFailure -> this.NotFound () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _

At this point in our code base, we had five Controllers, and they all had similar implementations. They all pass their input parameters to their injected imp functions, and they all pattern match in exactly the same way. There are, however, small variations. Notice that one of the Controllers expose an HTTP GET operation, whereas the other four expose a POST operation. Conceivably, there could also be Controllers that allow both GET and POST, and so on, but this isn't the case here.

The parameter list for each of the action methods also vary. Some take two arguments, one takes three, and the GET method takes none.

Another variation is that HomeController.Get is annotated with an [<AllowAnonymous>] attribute, while the other action methods aren't.

Despite these variations, it's possible to make the code less repetitive.

You'd think you could easily refactor the code by turning the identical pattern matches into a reusable function. Unfortunately, it's not so simple, because the methods used to return HTTP responses are all protected (to use a C# term for something that F# doesn't even have). You can call this.Ok () or this.NotFound () from a derived class, but not from a 'free' let-bound function.

After much trial and error, I finally arrived at this reusable base class:

type private Imp<'inp, 'out> = 'inp -> Result<'out, BoundaryFailure>
 
[<AbstractClass>]
type FunctionController () =
    inherit ApiController ()
 
    // Imp<'a,'b> -> 'a -> IHttpActionResult
    member this.Execute imp req : IHttpActionResult =
        match imp req with
        | Success resp -> this.Ok resp :> _
        | Failure RouteFailure -> this.NotFound () :> _
        | Failure (ValidationFailure msg) -> this.BadRequest msg :> _
        | Failure (IntegrationFailure msg) ->
            this.InternalServerError (InvalidOperationException msg) :> _

It's been more than a decade, I think, since I last used inheritance to enable reuse, but in this case I could find no other way because of the design of ApiController. It gets the job done, though:

type HomeController (imp : Imp<_, HomeDtr>) =
    inherit FunctionController ()
 
    [<AllowAnonymous>]
    member this.Get () = this.Execute imp ()
 
type CreditCardController (imp : Imp<_, PaymentDtr>) =
    inherit FunctionController ()
 
    member this.Post (portalId : string, req : PaymentDtr) =
        this.Execute imp (portalId, req)
 
type CreditCardRecurrentStartController (imp : Imp<_, PaymentDtr>) =
    inherit FunctionController ()
 
    member this.Post (portalId : string, req : PaymentDtr) =
        this.Execute imp (portalId, req)
 
type CreditCardRecurrentController (imp : Imp<_, PaymentDtr>) =
    inherit FunctionController ()
 
    member this.Post (portalId : string, transactionKey : string, req : PaymentDtr) =
        this.Execute imp (portalId, transactionKey, req)
 
type PushController (imp : Imp<_, unit>) =
    inherit FunctionController ()
 
    member this.Post (portalId : string, req : PushRequestDtr) =
        this.Execute imp (portalId, req)

Notice that all five Controllers now derive from FunctionController instead of ApiController. The HomeController.Get method is still annotated with the [<AllowAnonymous>] attribute, and it's still a GET operation, whereas all the other methods still implement POST operations. You'll also notice that the various Post methods have retained their varying number of parameters.

In order to make this work, I had to make one trivial change: previously, all the imp functions were curried, but that doesn't fit into a single reusable base implementation. If you consider the type of FunctionController.Execute, you can see that the imp function is expected to take a single input value of the type 'a (and return a value of the type Result<'b, BoundaryFailure>). Since any imp function can only take a single input value, I had to uncurry them all. You can see that now all the Post methods pass their input parameters as a single tuple to their injected imp function.

You may be wondering about the Imp<'inp, 'out> type alias. It's not strictly necessary, but helps keep the code clear. As I've attempted to indicate with the code comment above the Execute method, it's generic. When used in a derived Controller, the compiler can infer the type of 'a because it already knows the types of the input parameters. For example, in CreditCardController.Post, the input parameters are already annotated as string and PaymentDtr, so the compiler can easily infer the type of (portalId, req).

On the other hand, the compiler can't infer the type of 'b, because that value doesn't relate to IHttpActionResult. To help the compiler, I introduced the Imp type, because it enabled me to concisely annotate the type of the output value, while using a wild-card type for the input type.

I wouldn't mind getting rid of Controllers altogether, but this is as close as I've been able to get with ASP.NET Web API.


Comments

I find a few issues with your blog post. Technically, I don't think anything you write in it is wrong, but there are a few of the statements I find problematic in the sense that people will probably read the blog post and conclude that "this is the only solution because of X" when in fact X is trivial to circumvent.

The main issue I have with your post is the following:

You'd think you could easily refactor the code by turning the identical pattern matches into a reusable function. Unfortunately, it's not so simple, because the methods used to return HTTP responses are all protected (to use a C# term for something that F# doesn't even have). You can call this.Ok () or this.NotFound () from a derived class, but not from a 'free' let-bound function.

As stated earlier, there is nothing in your statement that is technically wrong, but I'd argue that you've reached the wrong conclusion due to lack of data. Or in this case familiarity with Web API and it's source code. If you go look at the source for ApiController, you will notice that while true, the methods are protected, they are also trivial one-liners (helper methods) calling public APIs. It is completely possible to create a helper ala:

// NOTE: Untested
let handle (ctrl: ApiController) = function
  | Success of result -> OkNegotiatedContentResult (result, ctrl)
  | Failure RouteFailure -> NotFoundResult (ctrl)
  | Failure (ValidationFailure msg) -> BadRequestErrorMessageResult (msg, ctrl)
  | Failure (IntegrationFailure msg) -> ExceptionResult ((InvalidOperationException msg), ctrl)

Another thing you can do is implement IHttpActionResult on your discriminate union, so you can just return it directly from you controller, in which case you'd end up with code like this:

member this.Post (portalId : string, req : PaymentDtr) = imp portalId req

It's definitely a bit more work, but in no way is it undoable. Thirdly, Web API (and especially the new MVC Core which has taken over for it) is incredibly pluggable through it's DI. You don't have to use the base ApiController class if you don't want to. You can override the resolution logic, the handing of return types, routing, you name it. It should for instance be entirely doable to write a handler for "controllers" that look like this:

[<Controller>]
module MyController =
  [<AllowAnonymous>]
  // assume impl is imported and in scope here - this controller has no DI
  let post (portalId : string) (req : PaymentDtr) = impl portalId req

This however would probably be a bit of work requiring quite a bit of reflection voodoo. But somebody could definitely do it and put it in a library, and it would be nuget-installable for all.

Anyways, I hope this shows you that there are more options to solve the problem. And who knows, you may have considered all of them and concluded them unviable. I've personally dug through the source code of both MVC and Web API a few times which is why I know a bunch of this stuff, and I just figured you might want to know some of it too :).

2017-03-30 22:06 UTC

Aleksander, thank you for writing. I clearly have some scars from working with ASP.NET Web API since version 0.6 (or some value in that neighbourhood). In general, if a method is defined on ApiController, I've learned the hard way that such methods tend to be tightly coupled to the Controller. Often, such methods even look into the untyped context dictionary in the request message object, so I've gotten used to use them whenever they're present.

These scars have prevented me from pushing the envelope on the 'new' methods that return various IHttpActionResult objects, but as you write, they truly are thin wrappers over constructors.

This does, indeed, enable us to write the mapping of result values as a let-bound function. Thank you for pointing that out!

Mine ended up looking like the following. We've added a few more success and error cases since I originally wrote this article, so it looks more complex than the initial example.

let toHttpResult (controller : ApiController) result : IHttpActionResult = 
    match result with
    | Success (OK resp) -> OkNegotiatedContentResult (resp, controller) :> _
    | Success (Created (resp, location)) ->
        CreatedNegotiatedContentResult (location, resp, controller) :> _
    | Failure RouteFailure -> NotFoundResult controller :> _
    | Failure (ValidationFailure msg) ->
        BadRequestErrorMessageResult (msg, controller) :> _
    | Failure (IntegrationFailure msg) ->
        let resp =
            controller.Request.CreateErrorResponse (
                HttpStatusCode.InternalServerError,
                msg)
        ResponseMessageResult resp :> _
    | Failure StabilityFailure ->
        let resp =
            new HttpResponseMessage (HttpStatusCode.ServiceUnavailable)
        resp.Headers.RetryAfter <-
            RetryConditionHeaderValue (TimeSpan.FromMinutes 5.)
        ResponseMessageResult resp :> _

Letting my BoundaryFailure discriminated union implement IHttpActionResult sounds promising, but how would that be possible?

The interface has a single method with the type CancellationToken -> Task<HttpResponseMessage>. In order to create e.g. NotFoundResult, you need either an HttpRequestMessage or an ApiController, and none of those are available when IHttpActionResult.ExecuteAsync is invoked.

When it comes to not having to derive from ApiController, I'm aware that even on the full .NET framework (we're not on .NET Core), 'all' the framework needs are IHttpController instances. As you imply, plugging into the framework is likely to be non-trivial, and so far, I haven't found that such an effort would be warranted. I might use it if someone else wrote it, though :)

2017-03-31 13:13 UTC


Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

Published

Thursday, 30 March 2017 10:17:00 UTC

Tags



"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Thursday, 30 March 2017 10:17:00 UTC