In DotNetNuke 6.2 the Services Framework was based on ASP.Net MVC. During the development of 6.2 Microsoft announced they would be releasing WebAPI for ASP.Net. WebAPI is built from the ground up for the purpose of building services on HTTP. It quickly became obvious that WebAPI was a better approach to Services Framework than MVC. Since 6.2 was scheduled to release well before WebAPI, we made the hard choice to release an MVC based Services Framework, and then make the breaking change to WebAPI with the next major release.
To be clear, any services that were written for 6.2 will not work in 7. All existing services must be converted to work with the new Services Framework. The effort to convert a service is not great, I estimate it will take an hour or two to convert a typical service and it should be get faster after having completed one or two.
This content of this post is based on my experience converting all of the core services as well as several services in test and internal projects. I expect following the directions provided in this post will suffice to succesfully convert most services, there are probably some use cases that I have not covered. If you find a situation that is not covered, please add a comment with a question or a solution to the situation. I will update this post with additional information as appropriate. The goal of this post is to create a WebAPI verion of the service with as few changes as is practical. WebAPI makes HTTP in general and REST in particular much more approachable than MVC did. Some services may benefit greatly from additional enhancement made practical by WebAPI, but that is beyond the scope of this post.
Namespace
The first thing to note is that the namespace for the Services Framework has changed from DotNetNuke.Web.Services to DotNetNuke.Web.Api. All using statement should be adjusted accordingly.
Project
Ensure that your project is targetted for at least .Net 4. WebAPI and DNN 7.0 require a minimum of .Net 4.
Remove the MVC specific references and add the WebAPI references.
Remove:
Add:
- System.Net.Http
- System.Net.Http.Formatting
- System.Web.Http
All the new .dlls can be found in the /bin folder of you DotNetNuke installation.
Routes
Defining routes is almost the same with only a couple minor changes. The biggest is that the IMapRoute.MapRoute method has become IMapRoute.MapHttpRoute. The new name is the same as the WebAPI method name.
(This does not apply to CTP1, but will apply to CTP2 and later releases) The second change to route mapping is the addition of a routeName parameter as the second parameter of the MapHttpRoute method. The combination of moduleFolderName and routeName must always be unique. The routeName becomes important when trying to use the UrlHelper to generate links to other WebAPI services. Many RPC services will never need to use URLHelper, but any service trying to make use of hypermedia will find it essential.
Finally, to ensure compatibility with classic pipeline installations it was common to include ".ashx" in Service Framework routes. Since classic pipeline is no longer supported in DotNetNuke 7, there is no longer any need for a registered extension.This is an optional change, but does result in prettier URLs. If you do remove the .ashx from your route, remember to update your client(s) to use the new URL.
A typical example of route mapper changes:
MVC:
using DotNetNuke.Web.Services;
namespace ExploreSettings
{
public class ExploreSettingsRouteMapper : IServiceRouteMapper
{
public void RegisterRoutes(IMapRoute mapRouteManager)
{
mapRouteManager.MapRoute("ExploreSettings", "{controller}.ashx/{action}", new[] {"ExploreSettings"});
}
}
}
WebAPI:
using DotNetNuke.Web.Api;
namespace ExploreSettings
{
public class ExploreSettingsRouteMapper : IServiceRouteMapper
{
public void RegisterRoutes(IMapRoute mapRouteManager)
{
mapRouteManager.MapHttpRoute("ExploreSettings", "default", "{controller}/{action}", new[] {"ExploreSettings"});
}
}
}
Auth Attributes
There have only been a couple small changes to the Auth Attributes to make them beter align with WebAPI. WebAPI introduced a new AllowAnonymous attribute. This new attrbiute replaces the AllowAnonymous property on the DnnAuthorize attribute. To maintain symmetry in the API, the RequireHost property has also been removed and replaced with a RequireHost attribute.
An example:
[DnnAuthorize(AllowAnonymous=true)]
public Foo MyAction(int i)...
becomes:
[AllowAnonymous]
public Foo MyAction(int i)...
and:
[DnnAuthorize(RequireHost=true)]
public Foo MyAction(int i)...
becomes:
[RequireHost]
public Foo MyAction(int i)...
Controllers
The DotNetNuke aspects of Controllers remain almost unchanged, instead of inheriting from DNNController, you now inherit from DnnApiController.
Action
Action methods are where the biggest changes are required. Most of the changes stem from fundamental differences between MVC and WebAPI.
The biggest differences stem from the way WebAPI does parameter binding. Mike Stall has a good post on the subject. The short version is the body of a message will all be converted into a single object. So where you may have had a method that expected two ints and a string, you will now need an object with properties that hold those two ints and a string.
MVC:
[AcceptVerbs(HttpVerbs.Post)]
public string UpdateHostSetting(string key, string value)
{
HostController.Instance.Update(key, value);
return "OK";
}
WebAPI:
public class UpdateSettingDTO
{
public string Key { get; set; }
public string Value { get; set; }
}
[HttpPost]
public HttpResponseMessage UpdateHostSetting(UpdateSettingDTO submitted)
{
HostController.Instance.Update(submitted.Key, submitted.Value);
return Request.CreateResponse(HttpStatusCode.OK);
}
Another big change in WebAPI is built in content negotiation. It is now much easier to write services which are format agnostic. WebAPI will examine the headers of each request to determine the format of the incoming data, and the preferred format or the response, and autmoatically format data appropriately. This means you should simply write your actions to expect POCO's and not messages encoded specifically as JSON or another format, and they should create responses from POCOs and not specifically return JSON or another type. It is possible to force specific formats but that is generally not a good idea.
I recommend making all your action methods return HttpResponseMessage, I have found this encourages me to respect HTTP and return proper status codes. It is possible to have return types for any POCO, and it can make unit testing a bit easier if you return a model from your action instead of an HttpResponseMessage. Any method that was returning an MVC ActionResult or any of it's siblings certainly needs to have it's return type changed.
I have also found WebAPI to be a little more picky about applying HTTP verb attributes to actions. When converting from MVC I recommend applying appropriate verb attributes to all your actions.
Anti-Forgery
The AJAX anti-forgery feature has been ported from MVC to WebAPI. It has been enhanced to work with media types other than just form-urlencoded. This has required a small change in the ServicesFramework jQuery plugin. The getAntiForgeryProperty method has been removed, and the anti-forgery token is now set by the setModuleHeaders method.
An example:
MVC:
$.ajax({
type: "POST",
url: sf.getServiceRoot('ExploreSettings') + "Settings.ashx/" + action,
data: sf.getAntiForgeryProperty(postData),
beforeSend: sf.setModuleHeaders
}).done(function() {
self.loadSettings();
}).fail(function (xhr, result, status) {
alert("Uh-oh, something broke: " + status);
});
WebAPI:
$.ajax({
type: "POST",
url: sf.getServiceRoot('ExploreSettings') + "Settings.ashx/" + action,
data: postData,
beforeSend: sf.setModuleHeaders
}).done(function() {
self.loadSettings();
}).fail(function (xhr, result, status) {
alert("Uh-oh, something broke: " + status);
});
Example Project
I have converted one of my sample WebAPI projects from MVC to WebAPI. You can find the code on github. If you are looking for more examples, all of the core services in DotNetNuke have been converted to WebAPI and can be found in the source packages.