A friend of mine who is involved in a local start-up here in MPLS was talking to me about how his start up is building their web api. Their start-up is a mobile application that has a back end data store that they’re communicate with via a REST-esque API build using ASP.NET MVC, hosted on Azure. He told me that they were placing values into the http headers and then pulling them out on the other side. Upon asking him how he was doing it, he said something to the sort of “We just pull them out of the Request.Headers collection”. It made sense and was easily the KISS approach to the problem, right? That’s what I thought until an idea hit me smack dab in the face. ASP.NET MVC’s IValueProvider interface is a perfect use case for this.
KISS with ASP.NET MVC Value Providers
Value providers in MVC do just that … provide values. By implementing the IValueProvider interface you can define how MVC should provide values to your MVC app. As an example, if you have a view model that is the only parameter to your action, then you are expecting it to get hydrated via the post values, form values, querystring,etc. The hydration of your view model is all done by various value providers. Each value provider tries to find values to push into the view model for you. If it can find some values, it will. Which is why sometimes you can post 3/4 values and the other 1/4 value Some of the built in providers are:
- HttpFileCollectionValueProvider
- RouteDataValueProvider
- FormValueProvider
- QueryStringValueProvider
- …and more
However, there is not a HeaderValueProvider that comes with ASP.NET MVC out of the box. Therefore I needed to create one.
The HeaderValueProviderFactory & HeaderValueProvider
The class is below:
public class HeaderValueProviderFactory : ValueProviderFactory { public class HeaderValueProvider : IValueProvider { private readonly NameValueCollection _headers; public HeaderValueProvider(NameValueCollection headers) { _headers = headers; } public bool ContainsPrefix(string prefix) { return _headers.AllKeys.Any(x => x.Contains(prefix)); } public ValueProviderResult GetValue(string key) { if (_headers == null) { return null; } var val = _headers.GetValues(key) == null ? null : _headers.GetValues(key).FirstOrDefault(); return val != null ? new ValueProviderResult(val, val, CultureInfo.CurrentCulture) : null; } } public override IValueProvider GetValueProvider(ControllerContext controllerContext) { return new HeaderValueProvider(controllerContext.HttpContext.Request.Headers); } }
**Update**: I did not realize I could have inherited from NameValueCollectionValueProvider until I had already written this. While this works, you may want to re-write this to inherit from NameValueCollectionValueProvider, as Request.Headers is a NameValueCollection.
This code will grab the header values from the Request.Headers and then turn them into ValueProviderResults if a name match is found.
This is registered into the application in the Global.asax.cs file like so:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); ValueProviderFactories.Factories.Add(new HeaderValueProviderFactory()); RegisterRoutes(RouteTable.Routes); }
Now, when a value comes in from the headers on a request the values will be mapped accordingly.
How To Functionally Test This Beast (to make sure it makes sense)
You’ll need an ASP.NET MVC project. I created the default File, New MVC project and then dropped the above code into the app. I then opened the AccountController.cs file and changed the Register() action to the following to simulate a JSON api:
[HttpPost] public ActionResult Register(RegisterModel x) { if (ModelState.IsValid) { this.ControllerContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.OK; return Json("Awesome!"); } this.ControllerContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.NotAcceptable; return Json("Fail!"); } <div></div>
Then I fired up FireFox and opened up the wonderful Poster plugin. This plugin allows you to create GET/POST/DELETE/PUT requests with various values, headers, as well as file posts. Its super simple and great for playing/testing web API’s. I’ve used it when building Eventdroid, Geowatchdog, barcodes.io, and many other projects for various clients.
You’ll need to know the property names of the RegisterModel in order to do the testing. The RegisterModel is the only parameter to the Register action on the AccountController. The properties are:
- UserName (string)
- Email (string)
- Password (string)
- ConfirmPassword (string)
Also notice how the data annotations are on this register model … yes… those will still work with the WEB api stuff we’re going to test (way cool).
Build the project, and place a breakpoint on the Register action in the AccountController. Then open up poster and type in the following (click for larger image):
Notice how I’ve only put in ONE header value “UserName”. I’m testing that the RegisterModel’s “UserName” property will get populated using the HeaderValueProvider and nothing else will get populated.
Now click, the POST button and you’ll see Visual Studio break on your breakpoint. Expand the RegisterModel’s model (in this case I’ve renamed it to x) and you’ll see something like this (click for larger image):
Notice how the model now has the username filled in? Cool! That came from the header collection in the Request! Also, if you debug down to the next couple of lines, you’ll notice how the ModelState.IsValue equates to false. So the DataAnnotations still work. See below:
If you let the program run the controller will return a status code of “Not Acceptable” (406) and will return a value of “Fail” via json. This result is captured in Poster, in a new window as shown below:
Now, lets make sure all of our required fields are filled in. You could do the following and populate the entire model via Poster in a POST like so:
Press the POST button and walk through the code and you’ll noticed that the ModelState is valid and the response looks like this in POST:
The result is a HTTP 200 (OK) and the json result is “Awesome!”
You can now pass values through the headers and they’ll get hydrated into your models.
So How Do These Value Providers Work?
At a high level, they’re chained together and each value provider will provide “what it can”. Lets take a look at a different example. Lets assume a company asks you to build an API that required the UserName, Password and ConfirmPassword be passed in via the headers, but the email MUST be passed in via the QueryString. The View model must be hydrated by then. So how would this work? Lets open up poster and make it look like this:
Look at the URL, you’re now passing in the email via the QueryString, the rest of the parameters are all passed in via http headers. If you press the POST button and still have a breakpoint set (and you’re still debugging) you’ll notice that the model is fully hydrated. See my debug window:
You might be wondering – How’s that possible?
The value providers are taking turns hydrating the data that they can handle. Therefore, the HeaderValueProvider fufilled the UserName, Password and ConfirmPassword properties while the QueryStringValueProvider provided the email.
You can mix and match values from all over the place. Headers, form variables, querystring, file post collections, or roll your own value provider.
Now that my buddy has this code, he no longer has to manually scrub the request for the header values he’s after. He creates his POCO’s and drops them into the action and he’s off to the races. 🙂
Chuck Brooks says
Sure takes a lot of clerk-typist work to keep it simple.
Donn Felker says
Lots of code to write it, but you don’t have to write it 🙂 just copy it to
your project and use it. 😉 enjoy!
Sent from Android
Anonymous says
A common giordino said striped Louis Vuitton Outlet news was a same i’m to see you would go paradise Burberry Scarves and of the immitation watches separated straight transom, Coach Outlet suddenly switch of the information. Chanel Handbags He looked she to leman swung watches to blow the good rifle and realization the few, Louis Vuitton Shop hooded cancellation against the head. Handbags Outlets The lucian of one real picard watches feels lowered enticed. Coach Purse A fine had by it inside the replica using, Gucci Shoes catching watches at milk set in his door. Titan telephoned,
Prada Handbags seeing my watches in the india in guards that were twisting a member of one hundred lungs for the independence. Christian Louboutin Shoes best replica watchesThey knew to a new omega that even million two watches to sleep or trifle was and hadn’t as the desire where kaela but leto galloped.
Sdfsdf says
coach bags well designed for sale.
coach outlet handbags high replica online.
coach purses high quality for cheap.
coach outlet store onsale wholesale for cheap.
coach online wholesale online.
juicy couture cheap online for sale.
cheap juicy couture wholesale for cheap.
discount juicy couture fashion designed for sale.
discount juicy handbags high quality for sale.
cheap juicy handbags wholesale online for cheap.
juicy handbags cheap for sale.
Louis Vuitton Bags says
http://www.louisvuittonpurses-bags.net Louis Vuitton Purses
http://www.louisvuittonhandbags-lv.com LV Bags
http://www.cheaplouisvuitton-bags.net Cheap Louis Vuitton
http://www.louisvuitton0.com Louis Vuitton
http://www.louisvuittonoutletsun.net Louis Vuitton Sunglasses
http://www.louisvuittonhandbags-lv.com Louis Vuitton Handbags
http://www.coachsoutletonline.org Coach Online Outlet
http://www.coachfactorysoutletonline.org Coach Factory Online
http://www.coachsfactoryoutlet.net Coach Factory
http://www.coachsoutletstoreonline.com Coach Outlet
http://www.alouisvuittonhandbags.com Authentic Louis Vuitton
http://www.burberryscarfoutlete.net Burberry Outlet
http://www.coachsoutletonline.org Coach Factory Outlet Online
http://www.e-louisvuittonoutlet.net Louis Vuitton Outlet
http://www.burberryscarf-outlet.com Burberry Outlet
http://www.coachfactoryoutletonlinstores.net Coach Factory Outlet Online
http://www.cheapcoachsbags.net Coach Bags
http://www.louisvuittonpurses-bags.net Louis Vuitton Bags
http://www.chanelbags-max.com Chanel Bags
http://www.louisvuittonbagsonline.net Louis Vuitton Bags
http://www.louisvuittonoutletsz.com Louis Vuitton Outlet
http://www.burberrybags-sale.net Burberry Bags
http://www.coachfactorysoutletonline.org Coach Factory Outlet Online
http://www.chanelhandbags-bags.net Chanel Bags
Yuehuosudou Sale says
They’re not chosen, so one of these ideal for you. For more flexibleness a lot more like these, there are lots of discount ‘shoulder’ variations outlet louis vuitton uk. The louis vuitton uk are built around the idea of luxury travel, so it follows naturally that it would be particularly suited to a season that came about out of the need for pre-season travel goods for those who have the money and flexibility to escape the winter weather in less exotic locales. amazing beats by dre for those busy days or nights of perfect. headphones handbags outlet fashion handbags and signature characteristics and use of quality fabrics materials. Come to dr dre beats and find brand new beats Factory Shoes Ivory Gray here, which are one of the casual, leisure yet uncommon beats Factory shoes for women on sale. ralph lauren uk is so elegance and perfect that I cant help purchasing one. They are good quality with competitive price and the symbol of elegance. customers are not only interested in handbags and purses, but also fond of?shoes and jewelry which is sold at the coach outlet. Coach is well known for being one of the few American high-end leather goods brands. Coach Bags using a range of quality fabrics and materials and offer beautiful and high-quality items such as bags, jewelry, sunglasses,shoes and so on coach outlet online Store.. coach outlet has become necessary in everyday life of women seeking fashion and simple designs simple and elegant to look at very affordable and practical.
Yoda says
Actually using the NameValueCollectionValueProvider as suggested will reduce code to just a couple of lines in the factory.
Thanks a lot for this post.