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.

Creating the Web Service Class

As indicated in the previous blog entry on Modern Module Development we are opting to use modern development techniques in our module as much as possible. In this blog entry we will walk through building out our web service class for our tasks module. By the end of this blog we will be able to see data listed out at our web service’s end point.

Adding in New References
We started off using Chris Hammond’s module development template because it saves us a great deal of time when creating a module. The template has all the necessary references for normal module development already configured. However, Chris’s template doesn’t currently have the references we need to work with the DNN’s Web API Services Framework. It’s possible that Chris will update the template to include this at some point in the future, but for now we need to update it a little.

We need to add in 4 additional references and they are:

  • System.Net.Http.dll
  • System.Net.Http.Formatting.dll
  • System.Web.Http.dll
  • DotNetNuke.Web

In order to add the necessary references just right click on the “References” node and click “Add Reference”

Adding a reference in Visual Studio

Once you click to “Add a Reference” you will be presented with a pop-up window. In the bottom right hand of that window you will see a “Browse” button. Click the browse button. When you click the browse button you will most likely see the bin folder for your site. Regardless of what folder and files you initially see you need to ensure that you’re looking in the “Bin” folder of your site. For me that’s browsing to C:\inetpub\wwwroot\ModDev2\bin.  This “Bin” folder is where all of the dll’s (dynamic link libraries) that we need to reference in our module reside. Once you see the .dll files that we need to add then just select them one by one or multi-select them all and then add the references. When you’ve got all 4 new .dll’s added then click “Ok”.

Creating the Web Service Class
With the proper .dll’s referenced we now need to create our webservice class. Creating the class for our Webservice is the same process for creating any other class. Right click on the “Models” folder then hover over “Add” then “Class”. Here again you can name the class whatever you’d like to name it. I’ll name mine “Webservices.cs”.

Adding a web service class in Visual Studio

Just as we did with the TaskController class, we first added some additional “using” statements at the top. Again we do this so that we can leverage functionality built into other parts of the DNN API. Having these "using" statements will also give us really good intellisense as we code, which will make it easier for us to create the necessary code.

At the top add in these using statements:

using System.Net;
using System.Net.Http;
using System.Web.Http;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Users;
using DotNetNuke.Web.Api;

Now we should have all the proper references in our project and all necessary using statements and we can get into some code. By default Visual Studio creates a “stubbed out” class of public class Webservices, but in order to work with DNN’s Web API service framework all classes using web services must end in the word “Controller” and they must inherit from the “DnnAPIController” class. So what does that look like? Just update the public class Webservices to read:

public class ModuleTaskController : DnnApiController

Looking at the above code the ModuleTaskController is the name of this class. You can name it whatever you want so long as it ends with “Controller”.  Without the word “Controller” at the end of the class name it will not work with DNN’s implementation of Web API. The class name is followed by a colon and then “DnnApiController” and the colon indicates that this class we’re creating of ModuleTaskController inherits from the DnnApiController class. We inherit from this class so that we can have access to all the DNN goodness like authentication, security, and access to portal settings.

Our Hello World Web Service
Before we get too crazy here, in honor of developer tradition, let’s conduct a simple “Hello World” here with our first web service. I mean it’s almost like we’re breaking an unwritten rule if we don’t do a “Hello World” at some point right? So let’s make our first service return the text “Hello World” for good measure.

Inside the code block of our ModuleTaskController webservice class we need to update the code to look like the below:

public class ModuleTaskController : DnnApiController
        public HttpResponseMessage HelloWorld()
            return Request.CreateResponse(HttpStatusCode.OK, "Hello World!");

Breaking Down the Controller
Now let’s discuss this code so we can understand what’s going on here. At the top of the code we see 2 attributes… the [AllowAnonymous] and [HttpGet].

The [AllowAnonymous] attribute is related to security and essentially means that we are allowing anyone (anonymous users) to access the data exposed on this web service end point. We could update this attribute to other levels of permissions such as [RequireHost] which would mean that only host users could access this method. We can also enter in some code to let DNN’s security model handle it. In that case we could enter [DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Edit)]. When done this way this tells the service to let DNN’s security model determine if the user has access or is privileged enough to see the data returned from the method. If you can remember the permissions tab that we previously referenced in the first video this is what dictates the security access level.

DNN Permissions Grid

The [HttpGet] simply indicates that this method is able to be accessed from “Get” requests (more on that in the next blog entry). Just beneath the attributes we see “public HttpResponseMessage HelloWorld()” and  that method returns a response message of “Hello World” along with an HttpStatusCode of “ok”. This is simply the response that we are returning which will make the text of “Hello World” appear on the screen.

We Need a RouteMapper!
With our web service created the only thing left to do now is set up the route which will make the HelloWorld method route to a specific URL. Creating the route configures the location in which (the URL) the methods on our controller to be accessible on the web. We could indeed include this routing code in this class, but to be in order to keep things organized we’ll place this routing code in a different class.

So let’s create a new class again by right-clicking on the “Models” folder and adding a new class. You can name this class whatever you’d like. I’m going to name mine “RouteMapper.cs”. Once you have this class created be sure to include the “using” statements “using System” and “using DotNetNuke.Web.Api” up at the top of the class file.

Next update the stubbed out class to look like the following and click save:

namespace Christoc.Modules.MyFirstModule.Models
    public class RouteMapper : IServiceRouteMapper
        public void RegisterRoutes(IMapRoute mapRouteManager)
            mapRouteManager.MapHttpRoute("MyFirstModule", "default", "{controller}/{action}", new[] { “Christoc.Modules.MyFirstModule.Models" });

Notice that this class of “RouteMapper” inherits from the IServiceRouteMapper interface (the colon should have tipped you off  to the inheritance :-) ) which is needed in order to register routes to various URLs. In order to explain the following parameters I’m going to reference some text from a blog entry “Getting Started With the Services Framework”  below and I’ve already updated the 2 necessary parameters cited in this blog entry to make it work for our module.

  • "MyFirstModule" - string moduleFolderName: Unique to DNN this parameter should be a name unique to your module/service, and will typically match the name of the DesktopModules folder in which your module is installed. It is important to use a name unique to your service to avoid routing conflicts with other services that may be installed.
  • "default" - string routeName: A name for your route.  This makes up part of the total route name when routes are registered in ASP.Net.  The combination of moduleFolderName and routeName must always be unique.  This name is important when trying to use the UrlHelper extensions.  For most services that only use a single route, "default" is perfectly acceptable.
  • "{controller}/{action}" - string url: Standard WebAPI parameter that defines the unique characteristics of your route.
  • "new [] {"Christoc.Modules.MyFirstModule.Models"} = string[] namespaces: Unique to DNN, this is an array of namespaces to search for controllers for this route. The parameter helps prevent conflict between services built by different developers.

DNN RouteMapper

Now with everything saved we should be able to get the “Hello World” text returned to us from our route. In order to do this we must access the specific URL of this web service. Depending on what you named your site and your module your URL may differ from mine a little, but in order for me to access it I have to go to the following:


When I access that URL I see this:

Our Hello World Web Service

There she is in all her beauty staring right back at us saying “Hello World”. If you see the “Hello World” text then congratulations… you’ve just built your first web service!

Let’s talk about the path of that web service for a second though before we get too carried away. Notice that we first access the root URL of the site, then we go into the DesktopModules folder because this is where all modules reside. Next we go into our specific module’s folder… in this case it’s “MyFirstModule”. After that we look into API then we see “ModuleTask” and if you remember “ModuleTask” is the name of our controller minus the “Controller” ending. Then finally we see “HelloWorld” which is the name of our method. Hopefully that explanation provides you insight into how to arrive at various service routes.

Creating the GetTasks Method on our WebService
With our “Hello World” Service under our belt we have some confidence and are ready to move forward. Can you already guess how we will access our “dummy” data in the Tasks table? If you can then kudos to you and keep rocking out! There is one other thing that we must consider for our GetTasks web service though. In order to access our task table data we need to account for the ModuleID parameter. So let’s discuss how we can go about this.

Just below the HelloWorld() method copy and paste in this code:

public HttpResponseMessage GetTasks(int moduleId)
               var tasks = new TaskController().GetTasks(moduleId).ToJson();
                return Request.CreateResponse(HttpStatusCode.OK, tasks);
            catch (Exception exc)
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, exc);

Notice that we start off again with the [AllowAnonymous] and [HttpGet] attributes again. Again this means that anyone can access this data on this service route and also that it will respond to HttpGet requests. Then we created another public HttpResonseMessage called “GetTasks” which accepts a parameter of moduleId that is of type integer.

The “Try/Catch” Block
Inside the squigglies of this function you see something we haven’t seen yet… a “Try / Catch” block. Why is it there? Why are we using it? The Try/Catch block helps us to log exceptions in the scenario that our module throws an error. The code in the “Try” section is the code that will be executed when the method is called. The “Catch” section will log the error if in fact there is an exception thrown in our module. We pass in an Exception named “exc” and then in the ErrrorResponseMessage we add the exception as a parameter.

Let’s focus on the code inside of the “try” block now. Notice that we first create a variable called “tasks”. You can call the variable whatever you would like. We set the tasks variable equal to a new instance of the TaskController (remember we just created this controller class) that executes the GetTasks method which is scoped by the parameter of ModuleId. Once we get that information we convert it to JSON (JavaScript Object Notation) by adding .ToJson() to it. Now we have the information we need and we simply need to return it as a response. In the next line we do exactly that by returning the HttpStatusCode of “Ok” along with the variable “tasks” which, by this point in our method, is a JSON object holding our tasks from the tasks table!

With all of this information saved build your solution again and let’s see if we can get some data at our web service route URL! Though, you may be wondering… what will the URL for this web service since we are dealing with a parameter? Well we’ll access the URL that you’re probably thinking of already and add a parameter to it like this:


Oops We Found an Error!
When I accessed that URL I saw a JSON object which is a good thing, but the object had “null” descriptions in it for the “Name” and “Description” fields. It looked like this:

Null tasks

The fact that we’re getting a JSON object back is really good news, but I have definitely done something wrong for the name and description to be null. So my apologies, but I have previously lead you astray. Though, it’s good because it helps us learn to troubleshoot a little bit. After looking into my code for the Task table and my Task class I realized that I made an error very early on. When we created the Task Table in SQL I named the name “TaskName” and the description as “TaskDescription”, but in my Task class I named the name “Name” and description as “Description”. Obviously these don’t match up! Yes, fail on my part. It’s an easy fix though. We just need to update the class names to reflect the proper names. Open up the Task.cs class and update the name to “TaskName” and description to “TaskDescription”. 

Now the Task class should look like this:

The Updated Task Class

After you have fixed our/my error and updated the class save the file and then rebuild the solution. After you have re-built then go back to your web service URL and refresh… voila, now we have all the correct data from SQL showing up here on our screen as a large JSON object! It looks like the below:

Our JSON Task Object

If you notice the parameter for our module is 416 and when viewing the tasks for that module we see data returned. Go and update that id of 416… change it to something else, any number, and click enter. Did you see any data returned? You shouldn’t have and that’s because there are no tasks for any other module ID’s in our database. If you want to test it out you can go back into the database and insert some more test data, but use a different ModuleID, and then try to hit the URL for that ID.

Did you just successfully get data returned via your web service? If so, tweet to the DNN Community and let us know!

The below video walks through the concepts covered in this blog entry

Pause for Celebration

Here we are, we’ve come from not much knowledge to already having returned SQL data to our module through DNN’s Web API Service framework! I remember when I first took minor steps and something would work I would get excited. I’d be sitting here at my desk clinching my fist as if I’d just thrown a touchdown pass or something. Hey, nothing wrong with celebrating and matter of fact we should take a moment to celebrate how far we’ve come. If you’ve made it this far then you’ve thrown several Td’s by now. And Td’s are good, but the game isn’t over just yet. We have made great progress, but we’ve still got more to go… so congrats for hanging in there, but we've got to finish the game. You’ll be writing these tutorials before you know it ;-)

Using Web Services Beyond DNN
Now that we’ve got a web service out there I want us to pause and take a step back to consider something. Our web service can be accessed by anyone (because remember we used the “[AllowAnonymous]” attribute) and this opens up possibilities. What possibilities you ask? Well the web service framework allows us to ultimately expose any data that you choose from your DNN instance on an end-point and that data can then be consumed by “Get” requests regardless of where they come from. Developers use DNN’s web service capabilities to integrate DNN site data with external applications such as CRM’s, mobile apps, and other custom solutions. So, just because we have a service created within and living in DNN don’t let it limit your imagination to only using this data in a DNN module. You can use web service data in DNN and beyond!

Helpful DNN Web Services links:

Next Steps
Now with a web service that has data just dangling out there waiting to be consumed we are finally ready to return back to our View control and throw some JQuery and AJAX on it to pull this JSON data into our view. Exciting times… see you in the next blog!

Go to the next Blog Entry: Using jQuery and AJAX


Brian Dukes
I'm really enjoying your series, Clint, thank for putting this together.

One of the nice benefits that Web API gives to developers is Content Negotiation (a.k.a conneg), which allows browsers (or other clients) to indicate what format they want a response in. So, rather than returning a response with a JSON string, you can just return the object collection, and Web API will convert it to JSON for you (one less thing for your to do), and it will also allow a client to request it as XML if they'd prefer in the future. So, you should be able to remove that ToJson() call, and have everything still work the same, but be more flexible (and have your controller concerned about one less thing).
Brian Dukes Thursday, October 16, 2014 11:24 AM (link)
Clint Patterson
Thanks for the feedback, info, and insight Brian... I really appreciate it and am glad that you seasoned Devs are following along! I will give that a try here locally and see if I can get it to work.

Thanks again..
Clint Patterson Thursday, October 16, 2014 11:56 AM (link)
John Smedley
So far I think this is the best tutorial I found so far :) I'm currently on the Creating web service portion and I have followed all the steps to the T. However when I try to run the service in the browser I keep getting an HTTP Error 404.0 Not Found Error message. I tried with a new module thinking I missed a step but ended up with the same result. Any suggestions?
John Smedley Thursday, May 21, 2015 4:25 PM (link)
Brian Dukes
@Carlos, if you're trying to hit the URL in Clint's post, (i.e. http://moddev2.loc/DesktopModules/MyFirstModule/API/ModuleTask/GetTasks?moduleId=416), try adding tabId to it as well. Also make sure that you've compiled your code.

Hope it helps!
Brian Dukes Wednesday, May 27, 2015 12:03 PM (link)
James Brown
Link to the series introduction

Link to the previous Blog Entry:
James Brown Monday, June 15, 2015 11:42 AM (link)
Bogdan Ionescu
Hi, I'm not new in WebApi, but I didn't succed to acces the WebApi service: 404 the url is not found.
I check the namespaces, and name of WebApi controller, to be in accord with RouteMap. I didn't find the error.
I put the project in GDrive:

Any help will be appreciate.
Bogdan Ionescu Wednesday, June 24, 2015 12:22 PM (link)
Steve Chaba
If anybody is getting a 404 error when trying to access your HelloWorld webservice URL, you need to be sure to Build your solution beforehand. I think Clint forgot to mention that, as he just says, "Now with everything saved we should be able to get the “Hello World” text returned to us from our route."

Steve Chaba Wednesday, October 7, 2015 3:14 PM (link)
Adrian Correa Moreno
I was trying to do this in but when I tried to call the HelloWorld with http://moddev2.loc/DesktopModules/MyFirstModule/API/ModuleTask/HelloWorld it fails and I got this error message:

Unable to locate the controller http://moddev2.loc/DesktopModules/MyFirstModule/API/ModuleTask/HelloWorld. Search namespaces: Christoc.Modules.MyFirstModule.Models

Is there specific to that makes it fails?
Adrian Correa Moreno Sunday, February 21, 2016 3:31 AM (link)
Don DeVeux
This has been a great series. I got distracted so I'm just getting back to it now. One thing I noticed is that when you look at the "Permissions" you are setting (above), it shows the following tabs "Module Settings", "Permissions", "Page Settings", " Settings". You are showing the contents of the Permissions tab, which is a table. The header row says: "View Module", "Edit Module", and "Actions".

When I go to my Permissions tab I see "View", "Edit Contents", "Delete", "Export", "Import", "Manage Settings", "Full Control", and "Actions". Am I looking in the wrong place (I got here by editing the page and going to "Settings" (off the gear menu)).
Don DeVeux Monday, November 21, 2016 5:12 PM (link)
Kent Pettit
I am getting an error saying "Server error in '/desktopmodules/TaskModule' application could not load file or assembly 'DotNetNuke.Enterprise' or one of its dependencies," I've been stuck on this and thought I would reach out! Any suggestions? This is a great tutorial! I love how you explain even the small details. is my url
Kent Pettit Tuesday, December 11, 2018 7:07 AM (link)
Clint Patterson

Thanks for your kind words. I believe the DotNetNuke.Enterprise refers to the paid, Evoq version of DNN. Are you trying to develop against the Evoq install or the platform?
Clint Patterson Tuesday, December 11, 2018 8:41 AM (link)
Kent Pettit
I am developing against the Evoq install!
Kent Pettit Wednesday, December 12, 2018 8:37 AM (link)

Comment Form

Only registered users may post comments.


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Davies (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