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 = | RouteFailure | ValidationFailure of string | IntegrationFailure 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.