Sharing a custom model binder between MVC 5 and Web Api

429 views Asked by At

I'm hoping to get some advice about the best way to share a custom model-binder between MVC and WebAPI. The way I've done it, while functional, seems overly complex, and I'm fairly sure there must be a better way.

I have a hybrid application that's using MVC to serve up the application UI, and partial views for dynamic sections of the site, and API's serving the same models as JSON to be consumed by 3rd party apps. What I want is to be able to use the same code to filter my model lists for both Web API and MVC.

Currently I have a binder class that implements both Web.Mvc.IModelBinder (for MVC) and Http.ModelBinding.IModelBinder (for Web API). This class then defines the two BindModel functions for each of these interfaces...

        Public Function BindModel(actionContext As Http.Controllers.HttpActionContext, bindingContext As Http.ModelBinding.ModelBindingContext) As Boolean Implements Http.ModelBinding.IModelBinder.BindModel
            Dim request As System.Net.Http.HttpRequestMessage = actionContext.Request
            Dim qs = request.GetQueryNameValuePairs.ToLookup(Function(x) x.Key.ToLower, Function(x) x.Value)
            bindingContext.Model = GetFilter(qs)
            Return True
        End Function

        Public Function BindModel(controllerContext As Web.Mvc.ControllerContext, bindingContext As Web.Mvc.ModelBindingContext) As Object Implements Web.Mvc.IModelBinder.BindModel
            Dim request As HttpRequestBase = controllerContext.RequestContext.HttpContext.Request
            Dim qs = request.QueryString
            Return GetFilter(qs.ToLookup)
        End Function

These functions basically extract from their respective Request collections an ILookup, so that I can then get the filtering parameters using a single function. The GetFilter function is defined as...

         Public Function GetFilter(lookup As ILookup(Of String, String)) As MyCustomFilter
                Dim filter As New MyCustomFilter

                filter.Offset = ConvertToInteger(lookup("offset").FirstOrDefault())
                filter.Count = ConvertToInteger(lookup("count").FirstOrDefault())
                filter.Keyword = lookup("q").FirstOrDefault()
                filter.Country = ConvertToInteger(lookup("country").FirstOrDefault())
                filter.State = ConvertToInteger(lookup("state").FirstOrDefault())

                'etc...

                Return filter
            End Function

To attach the binder for WebAPI I have to then create a BinderProvider class, like so...

    Public Class MyCustomFilterBinderProvider
        Inherits Http.ModelBinding.ModelBinderProvider

        Public Overrides Function GetBinder(configuration As Http.HttpConfiguration, modelType As Type) As Http.ModelBinding.IModelBinder
            Return New MyCustomFilterBinder
        End Function
    End Class

... and decorate the filter class to make it stick...

<System.Web.Http.ModelBinding.ModelBinderAttribute(GetType(MyCustomFilterBinderProvider))>
Public Class MyCustomFilter

To attach the custom binder to MVC, I add the following to my startup code...

ModelBinders.Binders.Add(GetType(MyNamespace.MyCustomFilter), New MyNamespace.MyCustomFilterBinder)

While this all works correctly, it seems overly complex. Am I on wrong track somehow? Is there a 'right way' to do this that I've missed?

Any suggestions would be most appreciated.

0

There are 0 answers