Products

Solutions

Resources

Partners

Community

About

New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.


Converting Services Framework MVC to WebAPI

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:

  • System.Web.MVC

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.

Comments

Christopher Schoeder
Where does the DotNetNuke.Web.Services namespace exist? What .dll file holds it?
Christopher Schoeder Tuesday, March 22, 2016 4:48 PM (link)

Comment Form

Only registered users may post comments.

NewsArchives


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Daniels (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out