If you have been following this blog for a while now (and if not, I really hope you will!), you’d know that I am a big fan of Web API. I have been blogging a lot about Web API, through its beta, RC stages and even about features that were only available through the Codeplex builds and I have to say that it is a terrific bridge between CLR and HTTP and fits really nicely into the existing landspace of web technologies.
With that said, today is a really big and important day, as Web API has been publicly released in its RTM version. This effectively means that you have binaries you can safely use in production scenarios and take advantage of all the great features of the framework. You can [download MVC4 (including Web API) RTM here][1].
Let’s go through the new features in RTM.
Per-route message handlers π
Per-route handlers provide you very nice granular control over the framework’s setup within the context of different (surprisingly) routes of your application. This feature was the most popular one at [Codeplex Web API issues repository][2] and is surely going to be welcomed by the community with open arms.
Consider a simple scenario - you have a free section of your API and parts of the API which are protected by an API key.
public class WebApiKeyHandler : DelegatingHandler
{
protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string apikey = HttpUtility.ParseQueryString(request.RequestUri.Query).Get("apikey");
if (string.IsNullOrWhiteSpace(apikey)) {
HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.Forbidden, "You can't use the API without the key.");
throw new HttpResponseException(response);
} else {
return base.SendAsync(request, cancellationToken);
}
}
This simple key-checker just checks if API key in querystring is not empty. Of course this is a dummy implementation, but it’s just for demo purposes. Now you might want to plug this into a specific route only. In order to use this on a per-route basis, you’d normally need to also set InnerHandler to the default HttpControllerDispatcher so that it continues to participate in the pipeline.
config.Routes.MapHttpRoute(
name: "FreeApi",
routeTemplate: "api/free/{controller}/{id}",
defaults: new { controller = "Free", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: null,
handler: new WebApiKeyHandler(InnerHandler = new HttpControllerDispatcher(config);)
);
In this case, you have a free route now: “/api/free/” and a regular paid route: “/api/[controller]/” which you have to access through an API key.
Per-controller configuration π
This is arguably the biggest change as it will save tons of time and help in a lot of scenarios. You are no longer tied to the GlobalConfiguration, but can provide your own configuration for a specific controller.
This is achieved through the use of IControllerConfiguration interface, which can be found [here][3].
namespace System.Web.Http.Controllers
{
/// <summary> /// If a controller is decorated with an attribute with this interface, then it gets invoked
/// to initialize the controller settings.
/// </summary>
public interface IControllerConfiguration
{
/// <summary> /// Callback invoked to set per-controller overrides for this controllerDescriptor.
/// </summary>
/// /// void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor);
}
}
What this allows you to do is for example have you own dependency resolution rules on a specific controller, or your own content negotiation or formatting on a specific controller. Similarly to per-route handlers, the possibilites here are very broad and the flexibility this brings is tremendous.
Mike Stall wrote [a nice blog post][4] a while ago, showing how you can wire up the per controller configuration using a simple configuration attribute, so I really recommend you check it out.
Changes to content negotiation to allow more flexibility π
I blogged about that in my [content negotiation post][5], but you can now very easily force your Web API to return 406 error if no media type match is found. By default, if none of the main 3 conneg conditions are met (please be referred to the above conneg post to find out more about these), Web API would return a response in the format in which the Type can be serialized, which is often not desireable.
To do this, just pass excludeMatchOnTypeOnly to the constructor of the DefaultContentNegotiator to true.
GlobalConfiguration.Configuration.Services.Replace(
typeof(IContentNegotiator),
new DefaultContentNegotiator(excludeMatchOnTypeOnly:true));
Additionally, you can now override a whole bunch of the default content negotiators methods (in RC the only one expose was the generic “Negotiate()”):
β protected virtual Collection of Mediatypeformattermatch ComputeFormatterMatches(Type type, HttpRequestMessage request, IEnumerable
β protected virtual MediaTypeFormatterMatch SelectResponseMediaTypeFormatter(ICollection
β protected virtual MediaTypeFormatterMatch MatchMediaTypeMapping(HttpRequestMessage request, MediaTypeFormatter formatter) used to determine match for MediaTypeMappings
β protected virtual MediaTypeFormatterMatch MatchAcceptHeader(IEnumerable sortedAcceptValues, MediaTypeFormatter formatter), used to determine match basedrequestβs accept headers
β protected virtual MediaTypeFormatterMatch MatchRequestMediaType(HttpRequestMessage request, MediaTypeFormatter formatter), used to determine match by inspecting the content type of the request
β protected virtual MediaTypeFormatterMatch MatchType(Type type, MediaTypeFormatter formatter), used to determine if the formatter can even write the selected Type
β protected virtual IEnumerable SortMediaTypeWithQualityHeaderValuesByQFactor(ICollection headerValues), used to sort charset, language and related headers by quality
β protected virtual IEnumerable SortStringWithQualityHeaderValuesByQFactor(ICollection headerValues), used to sort accept charset
β protected virtual Encoding SelectResponseCharacterEncoding(HttpRequestMessage request, MediaTypeFormatter formatter), used to determine encoding
This allows you to have granular control over how conneg is performed in your application. Again, if you wish to dig deeper, please have a look at [this commit][6] at Codeplex.
One other content negotiation related change is the removal of MediaRangeMapping with [this commit][7] - so if you are using it anywhere in your RC-driven code, you are unfrotunately out of luck. But since, it wasn’t working properly in the first place, chance are no one is using it anyway π
Introduction of ProgressMessageHandler π
Web API RTM introduces a special type of DelgatingHandler, called ProgressMessageHandler. As the name suggests, it can be used to track the progress of both the send operation and the download operation invoked on your Web API endpoint.
I have already mentioned this feature in [a blog post few weeks ago][8]. I recommend you revisit that post, but here is the gist of the progress handler - here in conjunction with WPF UI
private void UploadFiles(string[] files)
{
ProgressMessageHandler progress = new ProgressMessageHandler();
progress.HttpSendProgress += new EventHandler<HttpProgressEventArgs>(HttpSendProgress);
//continue with uploading (irrelevant for this demo)
}
private void HttpSendProgress(object sender, HttpProgressEventArgs e)
{
HttpRequestMessage request = sender as HttpRequestMessage;
ProgressBar.Dispatcher.BeginInvoke(
DispatcherPriority.Normal, new DispatcherOperationCallback(delegate
{
ProgressBar.Value = e.ProgressPercentage;
return null;
})
, null);
}
The ability to track upload and download progress just like that, is a wonderful addition to the ASP.NET Web API (or rather to the System.Net.Http, since as you can see here you can use it outside Web API as well). It exposes all kinds of useful information for up- and download of data when communicating with the HTTP endpoint.
In our case, we leverage on HttpProgressEventArgs.ProgressPercentage which can be directly assigned to WPFβs ProgressBar and utilized to visually track upload progress.
Multipart stream changes (yes, for the better) π
A while ago I blogged about a file uploader for Web API. It worked nicely, but was quite limited in terms of handling the uploaded files. For example, the files were arbitrairly getting saved you having any chance to manipulate the uploaded stream in memory - if you wanted to for example rename the files, you had to work around that by using System.IO API after the upload.
The reason for this is that the default MultipartStreamProvider only exposed the raw IEnumerable
Now, it’s much simpler, and Henrik has [a great post][9] explaining all the changes in deail. I will update the mentioned upload demo as well later on.
The change itself can be reviewed through [this commit][10].
Introduction of PushStreamContent π
PushstreamContent is a nice and sexy feature that, as the name suggests, allows you to push a stream of data to the client over HTTP. If you are a regular reader of this blog (and I hope you are!), you probably recall that [I wrote about that before][11], showing a simple chat built using Javascript SSE (server sent events) and PushstreamContent - the kicker there was that in order to use the functionality you needed the latest Web API source from Nuget or Codeplex - now you just need Web API RTM.
Example:
public class ChatController : ApiController
{
private static readonly ConcurrentQueue<StreamWriter> _streammessage = new ConcurrentQueue<StreamWriter>();
public HttpResponseMessage Get(HttpRequestMessage request)
{
HttpResponseMessage response = request.CreateResponse();
response.Content = new PushStreamContent(OnStreamAvailable, "text/event-stream");
return response;
}
public void Post(Message m)
{
m.dt = DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss");
MessageCallback(m);
}
public static void OnStreamAvailable(Stream stream, HttpContentHeaders headers, TransportContext context)
{
StreamWriter streamwriter = new StreamWriter(stream);
_streammessage.Enqueue(streamwriter);
}
private static void MessageCallback(Message m)
{
foreach (var subscriber in _streammessage) {
subscriber.WriteLine("data:" + JsonConvert.SerializeObject(m) + "n");
subscriber.Flush();
}
}
Quick and easy error response creation π
We actually already touched based on that in one of the previous examples. If you read my tutorial on message handlers a few months ago, I was using a custom helper to create an error response. Now it’s much simpler, and if you look back to our API key example from this article:
HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.Forbidden, "You can't use the API without the key.");
throw new HttpResponseException(response);
You can see how easily you can break the flow of code at any point and throw an HTTP response exception, by passing in a resposne code and some information.
Message handlers processing reversal π
This is a subtle yet important change, and if you do not cater for it, you might see unexpected results in your application.
The change was commited and is nicely explained [here by Henrik][12], and the gist of it is that the processing of message handlers in the request-response pipeline used to be bottom-up and now is reversed to top-down.
In other words, the “russian doll” model remains, it’s just been flipped upside down.
MediaTypeFormatter (breaking) changes π
The “read” signature of the base MediaTypeFormatter has been changed from
public override Task
<object>
ReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger) {}<br /> ```</p>
<p>
to
</p>
<p>
```csharp
<br /> public override Task
<object>
ReadFromStreamAsync(Type type, Stream stream, HttpContent content, IFormatterLogger formatterLogger) {}<br /> ```</p>
<p>
This exposes the entire <i>HttpContent</i> to the formatter, rather than just its headers, providing even greater deal of information to it. I mentioned this change last month in a <a href="/2012/07/bson-binary-json-and-how-your-web-api-can-be-even-faster/">media type formatter post</a>. What you'd need to do, if you have any custom formatter is to cater to this change, as the old version of the code won't compile any more.
</p>
<h3>
Reading form data
</h3>
<p>
If you don't want to or don't need to rely on Web API paramtere pinding, it is now possible (through <a href="http://aspnetwebstack.codeplex.com/SourceControl/changeset/bc62072118a6">this commit</a>) to read the data from the request body or from the querystring directly into <i>NameValueCollection</i>.
</p>
<p>
This is the new HttpContent extension method for reading into nv:<br /> ```csharp
<br /> public static Task<NameValueCollection> ReadAsFormDataAsync(this HttpContent content)<br /> {<br /> if (content == null)<br /> {<br /> throw Error.ArgumentNull("content");<br /> }
</p>
<p>
return content.ReadAsAsync<FormDataCollection>()<br /> .Then(formdata => formdata != null ? formdata.ReadAsNameValueCollection() : null);<br /> }<br /> ```
</p>
<p>
It is also possible to read directly into <i>JObject</i>, using the regular <i>ReadAsAsync<T></i> extension, where <i>T</i> would be <i>JObject</i>.
</p>
<h3>
Simple paramteres are now bound [FromUri] bydefault
</h3>
<p>
There has been a lot of consuion in the community about binidng simple parameters. Mike Stall posted a fantastic, in-depth explanation, of the paramater binding Web API style (because it is much different than what MVC does, to which people are used to).
</p>
<p>
One of the question most often asked is why my quersyting string/int/simple paramater is null. Well, usually the reason was your method was missing [FromUri].
</p>
<p>
Now it's been changed and the default is [FromUri] so you don't need any decorators to make this behavior work. On the contrary, if you create the default Web API project with RTM you'd see the explicit [FromBody] attributes instead - because that's what needed to fish them out of the body now.<br /> ```csharp
<br /> // POST api/values<br /> public void Post([FromBody]string value)<br /> {<br /> }
</p>
<p>
// PUT api/values/5<br /> public void Put(int id, [FromBody]string value)<br /> {<br /> }<br /> ```
</p>
<h3>
Few other minor changes
</h3>
<p>
I think I covered all the major things - there are several other minor changes. These include changes to <em>UrlHelper</em>, <em>HttpClientFactory</em> or <em>IHostBufferPolicySelector</em>.
</p>
<h3>
Summary
</h3>
<p>
I hope you will have fun with Web API RTM. If you were reluctant to put RC version "out there" on the wbe, this release now can now go safely to production and fuel your great new applications.
</p>
<p>
And you can be sure that I will continue to blog about Web API!
</p>
[1]: http://www.asp.net/web-api
[2]: http://aspnetwebstack.codeplex.com/workitem/52
[3]: http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/a5362969b4ce#src%2fSystem.Web.Http%2fControllers%2fIControllerConfiguration.cs
[4]: http://blogs.msdn.com/b/jmstall/archive/2012/05/11/per-controller-configuration-in-webapi.aspx
[5]: /2012/07/everything-you-want-to-know-about-asp-net-web-api-content-negotation/
[6]: http://aspnetwebstack.codeplex.com/SourceControl/changeset/changes/640a43cbaeb4
[7]: http://aspnetwebstack.codeplex.com/SourceControl/changeset/c13e9eb5e717
[8]: /2012/06/drag-and-drop-files-to-wpf-application-and-asynchronously-upload-to-asp-net-web-api/
[9]: http://blogs.msdn.com/b/henrikn/archive/2012/04/27/asp-net-web-api-updates-april-27.aspx
[10]: http://aspnetwebstack.codeplex.com/SourceControl/changeset/changes/8fda60945d49
[11]: /2012/05/native-html5-push-notifications-with-asp-net-web-api-and-knockout-js/
[12]: http://aspnetwebstack.codeplex.com/SourceControl/changeset/7768ecd08e84