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.


Digging Deep into Services Framework

This post is intended for advanced developers who are looking to dig deeply into the guts of Services Framework, with an eye to extending the core features of Services Framework. I will be pointing out the main classes involved in each feature of the Services Framework, and mentioning any unobvious details. I will not be mentioning every class, or expalaining in detail the function of every piece of Services Framework. This document should quickly point you to the relevant piece of code. From that point the code is truly the best documentation.

The original motivation for writing this was to share my knowledge of the Services Framework internals with the rest of the DNN Corp. engineering team. In the process of putting this together I realized that it may be useful to some folks outside of DNN Corp. and decided to publish it as a blog. Much of what is discussed here is either marked internal or included in Internal namespaces, and is subject to change at any time. Dragons live here, tread carefully.

Initializing Services Framework

Startup

The DotNetNuke application startup is centered around DotNetNuke.Common.Initialize.InitializeApp(). There is a call to DotNetNuke.Common.Internal.ServicesRoutingManager.RegisterServiceRoutes(). This method merely calls DotNetNuke.Web.Api.Internal.ServicesRoutingManager.RegisterRoutes() via reflection. The reflection is neccesary to break what would otherwise by a cyclic dependancy between DotNetNuke.Library and DotNetNuke.Web.

DotNetNuke.Web.Api.Internal.ServicesRoutingManager.RegisterRoutes() performs two important functions. It sets up all the GlobalConfiguration of the WebAPI portion of Services Framework, and then it runs the process of reflecting for all the route mappers and calling them to register routes for all the installed services. Once all the services have been registered the Services Framework is ready to process requests.

HttpModule

Requests to services will not "naturally" start the DotNetNuke application. If a website shuts down and the first request received is for a service, we need to ensure that the Services Framework is initialized. This is done by an HttpModule implemented in DotNetNuke.HttpModules.Services.ServicesModule.

Routes

Services Framework follows a simple format for all routes:

DesktopModules/<ModuleFolderName>/API/

This format is straight forward and documented in several places. What is not immediately obvious is that depending on the sites and aliases configured in a DNN installation, it may be neccesary to include prefixes on that path. If an installation only has an alias for foo.com, then a single route starting at /DesktopModules... is sufficient. But, if an installation also includes a child portal like foo.com/child it will be neccesary to map every route twice, once for /DesktopModules... and once for {prefix}/DesktopModules... . All the logic for mapping the correct routes can be easily found by following the flow of the RegisterRoutes method.

Adding an alias

When a new portal alias is added to the system, it is possible that the routes will need to be updated to start supporting a new level of prefix on the route. PortalController.CreatePortal and PortalAliasController.AddPortalAlias both call DotNetNuke.Common.Internal.ServicesRoutingManager.ReRegisterServiceRoutesWhileSiteIsRunning to take care of this.

Re-Registering routes while the site is running actually clears a cache key, the cache provider eventually makes the calls to re-register the routes. This hack is done to take advantage of the web farm cache provider and propogate the route changes to all servers within a web farm.

Processing a Request

Services Framework makes several additions to the standard flow of a WebAPI request in order to integrate properly with DNN, and to add features not available in standard WebAPI. Much of the additional processing is done in Http Mesage Handlers, you should be familiar with how they work, here is a good starting point.

Dnn Context

The first Services Framework specific step is establshing the DNN context of the request. The context is needed before attempting authentication. This includes locating the correct portal settings, setting the culture for the request, validating tab and module ids,and determining the active module if any. This is all handled by the DnnContextMessageHandler.

ITabAndModuleInfoProvider

Anyone who needs to override the way that Tab and Module info is passed to a service call can implement their own ITabAndModuleInfoProvider, and register it using the extension methods in HttpConfigurationExtensions. This approach is neccesary because the context needs to be determined from a raw request before we know what controller will handle the request.

Authentication

Immediately after the context is determined the various authentication message handlers run. If the request is already authenticated the subsequent handlers each short circuit and do nothiing. If a valid web forms cookie is present in the request then ASP.Net will have already authenticated the request before any of the WebAPI handlers runs.

The order of the handlers is not particularly important as long as the WebFormsAuthMessageHandler always runs last. - ASP.Net authenticates forms cookie (not a message handler)

  1. BasicAuthMessageHandler
  2. DigestAuthMessageHandler
  3. WebFormsAuthMessageHandler
    • This messaage handler does not actually perform the authentication, rather it completes the authentication within the DNN framework. There is no short circuit for this handler and it will always run.

Controller Selection

WebAPI no longer supports filtering controllers that apply to a route by namespace. Services Framework restores this functionality by implementing it's own IHttpControllerSelector. The Services Frameowrk implementation is in DnnHttpControllerSelector. Most of the code was copied from the DefaultControllerSelector in WebAPI, unfortunately while the WebAPI class does provide virtual methods, the way the code works makes it very difficult to effectively override those methods.

Dynamic Authorization Filters

WebAPI defaults to anonymous access to all web methods. Services Framework defaults to requiring host permissions to access a method. This is implemented by dynamically inserting a RequiresHost auth filter in the list of action filters run on a method. Services Framework implements it's own IFilterProvider in DnnActionFilterProvider to manage the dynamic insertion of the RequiresHost auth filter.

Optional Features

There are a whole series of features that can be applied to services as required.

Auth Attributes

Services Framework implements serveral auth filters that can be applied to classes and methods as needed. Each of the Auth filters is quite straight forward and well documented in the wiki and my other blog posts.

  • DnnAuthorize
  • DnnModuleAuthorize
  • RequireHost
  • SupportedModules
  • DnnPageEditor
    • This is an internal attribute introduced at the last second in 7.0. It will likely be replaced by a comprehensive and public approach to authorizing based on page permissions.
  • ValidateAntiForgeryToken
IOverrideDefaultAuthLevel

Any auth attribute that wants to remove the default authorization level of RequiresHost must implement IOverrideDefaultAuthLevel interface. Most of the above attributes implement this inteface but for example the SupportedModules does not as passing this auth filter says nothing about personal permissions.

StringPassThroughMediaTypeFormatter

StringPassThroughMediaTypeFormatter adds support for formatting strings as text/html or text/plain to the default set of media type formatters provided by WebAPI. It is also easy to support additional media types from strings by adding a new instance of StringPassThroughMediaTypeFormatter to the configuration.

Tracing

The TraceWriter class implements the ITraceWriter interface and writes all trace output to the the DNN log4net logs. There is an app setting named EnableServicesFrameworkTracing that controls the logging.

UrlHelper

Because DNN may use more than one physical route for each logical route defined by a service's route mapper, it is not easy to know the true name of a route. WebAPI's UrlHelper requires the name of a route. The UrlHelperExtensions class provides helper methods that wrap the standard UrlHelper methods and determine the correct route name from the context of the request.

Registering Features in a Page

The DotNetNuke.Framework.ServicesFramework class provides methods that modules, skins etc. can use to ensure that ajax script support and/or anti-forgery support is included in a page.

jQuery plugin

The /js/dnn.servicesframework.js file includes a jQuery plugin with various javascript helper functions to assist in making AJAX calls to Services Framework services. The methods themselves are all documented in the Wiki.

Comments

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