ASP.NET MVC ModelBinder: Use ModelBinders For QueryStrings
ModelBinding is popular for sending form data back to the server, but did you know that you can use ModelBinders with GETS as well as POSTS? This quick tip will show you how to pass QueryString parameters to the server using a Model.
ModelBinders were introduced as a way to send your data from the page over to the server. When you post your data back to the server from a form, your POST method in your controller can use either a ViewModel or a FormCollection parameter.
But did you know that when you access a page using a GET instead of a POST, you can pass QueryStrings, cookies, or any other Request parameters into a Model and use them in your pages.
Let's go to our old friend the FaqController from our past post.
Paging Mr. FAQ
Let's have our FaqController Index page accept paging parameters.
We need to make our paging model so we'll create the following class:
Models\PagingModel.cs
namespace ModelBindingExample.Models { public class PagingModel { public int PageIndex { get; set; } public int PageSize { get; set; } } }
Now we need to tell MVC that our model attaches to a ModelBinder class. Makes sense, right? :-)
For each model you want to bind, you need the ModelBinderAttribute to point your model to your ModelBinder class as shown below.
using System.Web.Mvc; using ModelBindingExample.ModelBinders; namespace ModelBindingExample.Models { [ModelBinder(typeof(PagingBinder))] public class PagingModel { public int PageIndex { get; set; } public int PageSize { get; set; } } }
Now let's work on our PagingBinder ModelBinder class. You can either inherit from the DefaultModelBinder class or use the IModelBinder with the BindModel method.
When you use the DefaultModelBinder method, you need to override the BindModel method.
ModelBinder\PagingBinder.cs
using System.Web.Mvc; using ModelBindingExample.Models; namespace ModelBindingExample.ModelBinders { public class PagingBinder: DefaultModelBinder { public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var request = controllerContext.HttpContext.Request;
var index = request.QueryString.Get("Page"); var size = request.QueryString.Get("Size");
int pageIndex; if (!int.TryParse(index, out pageIndex)) pageIndex = 0; // Default Page
int pageSize; if (!int.TryParse(size, out pageSize)) pageSize = 20; // Default 20 FAQs
return new PagingModel { PageIndex = pageIndex, PageSize = pageSize }; } } }
See near the top of the method where we use QueryString? We can easily replace that with any type of data in the Request object. We could use Cookies, Form Data, UserAgent, IPAddresses, or even Referral Urls. Whatever we want to grab, we can store in a model.
Now that we have our modelbinder, we can use the model in our FaqController Index page.
Controllers\FaqController.cs
// GET: FAQ public ActionResult Index(PagingModel model) { return View(_factory.GetViewModel<FaqController, FaqViewModel>(this)); }
It's all set up as a parameter in our Index page/method. When we run it for the first time using the following URL:
http://localhost:4985
we see our model will have the default values: Page = 0 and Size = 20.
But when we use the following URL
http://localhost:4985?Page=2&Size=10
we see the values passed from the QueryString.
Conclusion
The ModelBinders are perfect for gathering specific data from HTTP Requests and passing them into your controllers in a neat, tidy little package instead of having HttpContext.Currents all over the place. When you receive this nice model in your controllers, it's kind of like Christmas!
Ok, maybe not Christmas, but it makes your code a lot cleaner and testable in the long run.
I hope this tip helps you out.
UPDATE: By request, I added a quick unit test for this Model Binder with a post called Unit Testing ASP.NET MVC Model Binders.