REST? What is REST?
I’ll admit that I was clueless about REST when I first head the term – it was actually whilst working on a project and someone asked me to fix the REST API. ‘Oh no’, I thought, ‘not another industry-coined acronym for pushing more standards upon us all’. As I often say to a friend of mine who eagerly laps up every new standard/style/acronym dished out from Redmond – ‘just because Microsoft packs you a crack pipe, you don’t have to smoke it’. (it means you don't have to take up every technical offering MS serves up, in case you took me literally)
In the early days of SOAP I eagerly delved into it, rightfully thinking that web services were what we all needed – but anyone who has tried to read and understand the SOAP specifications will quickly start wishing to read ‘War and Peace’ or some other light tome. Then you just install the SOAP development kit, write some code and hope for the best, without really understanding what is going on.
REST stands for ‘Representational State Transfer’, and was coined by Roy Fieldings paper, which can be read here :http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. But I know you’re probably not that interested, so just want the top-level summary. REST is neither standard, nor protocol, but just an ‘Architectural Style’ built upon the existing standards and protocols the web runs on every day. This very web page you are reading was delivered REST-style, to you. How so?
Your browser made a request to the web server, something like this :
GET /Blog/Technical_Blog/EntryId/35
HTTP/ 1.1
User Agent : Some Browser
It was a GET response, so the server knew what to do : Get something
It was a Http call, so the server knew what protocol to communicate in
The Url was /Blog/Technical_Blog/EntryId/35/, which told the server which particular piece of information to serve up. In this case, it was a Html document, but could well have been a PDF, Jpeg file, or Xml file.
Note that all of this existed long before the term REST was coined, so what’s new about REST?
More specifically, when it comes to web services, all sorts of protocols and complications have crept into the whole business. And there’s a very good reason for it – some business-to-business communications need a very detailed protocol to ensure that everything in the data exchange is documented and covered. I’d imagine this sort of thing is terribly useful if you’re building a web-based purchase ordering system to supply Defense Department contracts or some such thing.
But a lot of us just want to serve up a simple response to a simple request, like, perhaps, an Xml feed of the data in a third party module. REST describes a style where the Http protocols and a simple, noun-based Url is all you need to know to access an API.
Wait! You say, isn’t that what RSS is? Well, yes and no. RSS itself is really a REST style in many ways – you supply a server with a GET request to an RSS Url, and it gives you back a pre-formatted well-known Xml document. But the RSS format is built as a syndication format, and is inflexible about being used for other types of API.
So the REST style really is useful is when building API’s. The ‘actions’ for an API are already built-in to the Http protocol : GET, POST, PUT, DELETE. The rest is just in the layers of the REST call – and should be intuitive as possible : if you requested www.ifinity.com.au/REST/API/Users/bchapman, you’d expect to see some sort of information about the user ‘bchapman’. (just an example, it’s not implemented, so don’t try)
DotNetNuke , API and REST
It’s true that there is someone building a SOAP API for DotNetNuke. It’s probably a good thing – I haven’t really looked at it. I find SOAP far too heavy for a simple API, and it requires SOAP toolkits for the consuming application. Instead, I wanted a REST API for DotNetNuke, so, in one frenzied burst of coding, I built one : the iFinity.DNN.Rest API was created.
Here’s how it is setup:
- A rest.axd handler is defined, and all rest calls are to the resource /rest.axd
- There is an inbuilt URL rewriter HttpModule, so that you can get more RESTful urls like REST/Users/bchapman/. That particular Url would be rewritten so that the parameters are forwarded rest.axd in this manner : rest.axd?module=users&p1=bchapman
- The request is processed by a pre-defined ASP.NET provider, and there is a base RestProvider module which can serve up late-bound calls to existing DNN core objects and existing DNN module objects. For instance, our hypothetical users/bchapman/ call will forward the request to DotNetNuke.Entities.Users.UserController.GetUser(int portalId, string username)
- All of the supported modules/methods are predefined in the web.config call, so only those calls strictly defined will work through the API (I’m not about to open up the entire DNN code base to exploratory calling by anonymous third parties)
The architecture could be described something like this:
Http request -> GET /REST/Users/bchapman/
- Rewritten by the RestUrlRewriter, resulting in rest.axd?module=users&p1=bchapman
- Handled by rest.axd, which inspects the currently loaded providers and matches one with the name ‘users’
- When the correct provider is found, the correct method within the defined DNN module is located, using the request type (GET) , the destination type (UserController.GetUsers), and the parameters (bchapman). The REST API has the smarts to ‘autofill’ things like ‘portalId’ which are already known to the framework via the requested domain name.
- The underlying module is called, and the results are Serialized using the built-in DotNetNuke serialization module, and are returned as Xml.
Here is the web.config entries for our hypothetical ‘Users’ module:
<restproviders defaultProvider="RestModuleProvider">
<providers>
<clear/>
<add name="Users" type="iFinity.DNN.Rest.RestModuleProvider" />
providers>
<restModules>
<restModule providerName="Users">
<restMethod methodName="GetUserByUsername" httpAction="GET" restPath="" type="DotNetNuke.Entities.Users.UserController, DotNetNuke">
<parameter parameterName="portalId" type="Int32" autoFill="true"/>
<parameter parameterName="userName" type="String" autoFill="false"/>
restMethod>
<restMethod methodName="GetUsers" httpAction="GET" restPath="List" type="DotNetNuke.Entities.Users.UserController, DotNetNuke">
<parameter parameterName="portalId" type="Int32" autoFill="true" />
restMethod>
restModule>
restModules>
restproviders>
What this set of entries says is this : we have a module called ‘Users’, which is handled by the base RestModuleProvider (the builtin provider). There are two methods available : GetUserByUserName, and GetUsers. GetUserByUserName is the ‘base’ method, indiciated by the fact that the ‘restPath’ is empty. GetUsers is available using the path of ‘List’ – like this : REST/Users/List. The destination methods are both in the DotNetNuke.Enties.User.UserController class.
The parameter types are defined for each of the methods, and these parameters will either be auto-filled (autoFill=true), or obtained from the query string in the defined order.
This architecture is loosely based on the lessons I learnt from the Google Sitemap Providers that I have built, namely:
- provider based modules are good in that you can infinitely extend the functionality without changing the original code or relying completely on late-bound calls using reflection
- using http handlers is a good way to extend DNN without interfering with the below-the-decks Core functionality.
Extending the API
So, lets assume you have a third party module called ‘Cities’, which displays a list of cities per geographic region.
If you aren’t the original developer, and don’t have the source code, you can just inspect the Cities.dll module using the object browser – in there you see the ‘Cities.CityController’ class, and it has the following method: ‘GetCitiesByCountry(string countryName)’. All you need to do is define a new module and method, like this:
<providers>
<clear/>
<add name="Cities" type="iFinity.DNN.Rest.RestModuleProvider" />
providers>
<restModules>
<restModule providerName="Cities">
<restMethod methodName="GetCitiesByCountry" httpAction="GET" restPath="" type="Cities.CityController, Cities">
<parameter parameterName="countryName" type="string" autoFill="false"/>
restMethod>
restModule>
restModules>
As long as the return value from the ‘GetCitiesByCountry’ call can be serialized OK, or is a value type (string, int, etc) then you’ve just added a new REST method to your API : domain.com/REST/Cities/Australia/
You would expect this to return a list something like this:
<iFinity.DNN.Rest.Result type='Cities.Entities.CityInfo'>
<CityInfo>
<Name>Sydney</Name>
<State>NSW></State>
</CityInfo>
<CityInfo>
<Name>Brisbane</Name>
<State>QLD</State>
</CityInfo>
Etc..
</iFinity.DNN.Rest.Result>
If you are the original developer of the Cities module, and want to use the REST API to give simple access to the data contained within, then you could build your own REST Module Provider, using early-bound calls and custom formatting of the reply XML (in fact, there is no requirement to use Xml in the response - you might want to return a jpeg of the city in question). All you need to do is define a new class for the Rest Provider, and inherit from the base RestProvider class. You then override a single call : GenerateRestResult(RestCall thisRequest).
The ‘GenerateRestResult’ call simply provides the details of the current request through the ‘RestCall’ object, and it’s entirely up to the developer how to handle that call. The only requirement is that the RestCall.result property is set when processing returns to the rest handler.
So why do it this way? Well, as the third party developer you could create custom Rest requests, specify the format of your return value in the Xml way you’d like, and many, many other options.
As I’ve found with the Google sitemap providers, the set of people who want things you’ve never even thought about is unbounded, so a simple inherited provider with all the basic plumbing in place allows custom development to happen very quickly. With this architecture, you can create API calls for any type of DNN code
Throwing an API Call together
While writing this, I got the inspiration to define a new API call in my fledgling REST Api. I hunted through the DotNetNuke library until I could find something interesting, and came across the LogController.GetLog call – the definition, provided by Visual Studio Object Browser is this:
public virtual DotNetNuke.Services.Log.EventLog.LogInfoArray GetLog(int PageSize, int PageIndex, ref int TotalRecords)
Member of DotNetNuke.Services.Log.EventLog.LogController
I thought that might vaguely be useful – it would be trivial to build a small GUI application that read the feed of Log exceptions in your portal, and perhaps gave you a desktop notification when a new log entry appeared.
I threw together the definition for the call, and put it into my web.config:
<restproviders defaultProvider="RestModuleProvider">
<providers>
<clear/>
<add name="Users" type="iFinity.DNN.Rest.RestModuleProvider" moduleType="DotNetNuke.Entities.Users.UserController, DotNetNuke" />
<add name="Log" type="iFinity.DNN.Rest.RestModuleProvider" moduleType="DotNetNuke.Services.Log.EventLog.LogController" />
providers>
<restModules>
<restModule providerName="Log">
<restMethod methodName="GetLog" httpAction="GET" restPath="" type="DotNetNuke.Services.Log.EventLog.LogController">
<parameter parameterName="pageSize" type="Int32" autoFill="false" />
<parameter parameterName="pageIndex" type="Int32" autoFill="false" />
<parameter parameterName="totalRecords" type="Int32&" autoFill="false" />
restMethod>
restModule>
restModules>
restproviders>
I then crafted up a Http request using fiddler :
GET http://localhost/DotNetNuke/Log/1/1/0/rest.axd
the parameters, are, in order : page size : 1, pageIndex : 1, totalRecords : 0 (this is a byref parameter, which currently isn’t returned by the API call, probably something that needs looking into)
And the results back were this:
<iFinity.DNN.Rest.Result type='DotNetNuke.Services.Log.EventLog.LogInfoArray' itemType='DotNetNuke.Services.Log.EventLog.LogInfo'>
<item>
<b>LogGUID:</b> c5c3e394-8c92-4e17-a7a6-a344cafe7668<br>
<b>LogType:</b> GENERAL_EXCEPTION<br><b>UserID:</b> -1<br>
<b>Username:</b> <br><b>PortalID:</b> -1<br>
etc….
</item></iFinity.DNN.Rest.Result>
Total time taken to create an API call to read the exception log via REST API: about 5 minutes! Total lines of code written : 0 !
(excluding web.config lines, but that’s not coding in my book)
I’m not suggesting that complete non-programmer types could define API calls, you wouldn’t really want that. The idea is that people who know what they are doing can quickly configure API calls based on a knowledge of the underlying core code, which can easily be discovered by inspect the libraries using any type of reflection tool, like the Visual Studio Object browser.
That’s not a true REST Call!
REST disciples will inform me that /Log/1/1/0/rest.axd isn’t a truly RESTful Url. I’m not one to take on the standards-religion crowds at any time, but I will say that if you enable IIS to process wildcard calls (the same modification that produces extension-less Urls like the ones in use on this site) you can get a more Restful call. Perhaps I need to look into doing something like /Log/size/1/page/1/ and generate the byref parameter so it doesn’t have to be initialized in the Url. That’s pretty trivial to do – it can all be done in the method parameters definition. Or you could define the default value of ‘size’ so you just get /Log/Page/1 which would be even better.
However, for the purposes of a quick’n’dirty API definition that, while not having the most RESTful Url, certainly fulfils the criteria of being simple.
Not just GET
The shown examples are just simple ‘GET’ requests, which just call an object and return a value. But APIs are rarely useful when they are one-way traffic. What is really needed is the ability to modify data on the server – and this requires processing with PUT, POST and DELETE.
The plumbing is all there in my 0.0.0.1 version of the REST API to define and call PUT requests, but I haven’t coded up the necessary ways of handling more complex input data in the Http request headers. Typically you would define data in the PUT/POST request like this (let’s image an API where you can create DNN users from another application)
PUT /Users/
Host www.ifinity.com.au
Name :bob smith
Username : bobs
You would expect this to create the user ‘bobs’ on the DNN install, according to the current portal security settings, and perhaps return the object that was created, as well as process all the other things that happen, including generating a password and sending a welcome email.
I’ll build this functionality when I have worked the kinks from the current GET functionality.
Security
Anyone with any sense of application architecture should have raised the red flags by now : wait a minute! Isn’t he just allowing wholescale access to anything in the framework? Well, yes and no. Firstly, you would only define an open ‘GET’ function to information which is not sensitive – and there are plenty of places to do this. With PUT/POST you wouldn’t find as many cases, so you do need some type of security access to stop anyone and everyone using the API.
Security comes in two levels with API’s: Application and User.
Application level security is something you should be familiar with if you’ve ever written an application to work with a popular API like the Flickr API or the Facebook API. You have to sign up for an application key, and then provide that key with each request that is made. Often you have to have the user allow that application to access data that is considered ‘private’ to them.
User level security is authenticating the user that the Application is ‘signing on’ with. This needs to be the same userid/password as for a regular DNN signon, and needs to happen in a secure and authenticated way.
Neither of these are in my early version of the REST API, but eventually I’ll build them in, because an API is pretty lightweight until you get into this type of area. Building in Application Keys also means providing some type of DNN module which allows users to sign up their application for access to the API. This is all pretty easily done, but there is a fair amount of work in it, so it could be a while off.
The uses of a REST API
I’m not sure how many people are building websites that offer external API’s to their user base, but even if they don’t, some administrator-level tools would be great – how about these for an example:
- drag-and-drop desktop file upload tool for copying files up to your DNN site
- remote user administration in a thick-client GUI interface
- building custom modules to process actions from other services, like snowcovered sales notification or PayPal order notification. It would be possible to build a generic module that does specific actions when a PayPal instant payment notification occurred, such as create users, run actions on your website, etc. (I’m aware that third party modules to do these actions exist, I’m talking about a generic API interface where any module could access, rather than having to re-invent the wheel each time.)
So, where’s the code!
I haven’t released the code as of yet – it’s still a bit green for public consumption - I don't think I'd have enough time to answer all the support requests! Once I’ve implemented it for a couple of intended uses and worked some kinks out I’ll release the code as open-source for anyone to download, use, extend and try. I’ll also publish a REST API for the iFinity.com.au site – as soon as I work out something useful for it to do!
The reason for the blog post is to get people thinking and talking – what are the challenges for a REST API for DNN? What sort of things would website owners and third-party developers like to get from it? Post your comments and let me know – I’d really like to get some feedback from the community as to what direction the development should head in.