This is the third post in the series of my posts describing approach for asynchronous development with Kentico:
first one shows you how to configure Kentico REST service and consume it on a client and
second one describes how to consume it on the server.
As you ay know, Kentico REST service provides you with an access to literally all objects or pages within the system. But as any other technologies or approach along with pros it has some cons. What they are? The main one, in my opinion, is that you can request the object(s) of the one type only and you can't join several objects together, e.g. you can't get CMS.User joined with CMS.UserSettings or Page (Document) with a coupled data. I do not like an idea of sending two sequential request for those two objects and joining them on the client with javascript. Also, when talking about mobile application as a client, we should keep in mind the amount of the requests we can simultaneously send to a server is limited. So what are our options in this situation? The option is to join those objects on the server and return in a single response. To achieve we can either add a web service to the application or Web API.
Web service is a good when we need to establish server-to-server communication, as it provides strongly typed objects, so we do not need to worry about serialization and deserialization things.
But when we are talking about client-server communication we would rather preffer to receive a JSON object in a response from server and this is exactly what Web API can do for us. Actually the responce type could be easily changed to whatever we might think of. By default Web API offers JsonMediaTypeFormatter and XmlMediaTypeFormatter, but you could easily implement your custom one. This is what I like about Web API and this somewhat reminds me Kentico: it consists from separate, absolutely decoupled, pieces and you can substitute almost anything in the pipeline using either out of the box components or implement one, which will meet your exclusive requirements. Another huge advantage is that Web API is extremely lightweight and, as a result, very fast.
Adding Web API to a Kentico project
To be honest with, I didn't expect adding Web API to my Kentico project to be that much easy. So my first step was adding Web API controller, which was as easy as clicking with right button on the project in Visual Studio, selecting Add New Item, selecting Web API Controller, naming it and saving. Visual Studio prompted me about saving it into App_Code folder, which is absolutely fine. By the way I added it to web site project, which I was not sure this is possible... So I can say for sure it will work with web application project.
Web API requires some configuration to be done before running it. The least configuration is routing. This could be done by adding just a couple of lines of code to the CMS.Base.ApplicationEvents.Initialized. Execute event handler:
private void Initialized_Execute(object sender, EventArgs e)
{
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}"
);
}
That's it! You are ready to utilize great features of Web API to build awesome user experience with client side application.
Live example
Just recently we've got a task to build document tree on the front end, which will repeat almost 100% of what we have in CMS Tree. Of course we built it with hierarchical viewer which worked fine at the beginning, but once amount of document grew up it came slower and slower. Even with caching everything and everywhere first page visit for each user was really slow. So this was a starting point for us for searching some nice workarounds. We ended up with loading only first two levels by default and loading any other branch on demand utilizing AJAX on the client and, as you might already guessed, Web API on the server side.
Our goal was selection of documents from underneath of specified page, those belong to any of 9 page types, including some fields from coupled tables, those are published and current user is authorized to access to. I know this might sound a bit confusing, but this is easy to achieve with awesome Document Query, that Kentico implemented in the 8th version.
So I added Web API controller and configured routing for it, like described above. Later on I implemented just a single method within controller:
public IEnumerable<TreeNodeDTO> Get(int id)
{
Func<TreeNode, string> safeValue = x => ValidationHelper.GetString(x.GetValue(Constant.DOCUMENT_PDF_FIELD_NAME), "");
WhereCondition where = new WhereCondition();
where.WhereEquals("NodeParentID", id);
var childNodes = DocumentHelper.GetDocuments()
.Published()
.Where(where)
.Columns(new List<string>() {"NodeID","NodeAliasPath","NodeHasChildren","DocumentID","DocumentName","ClassName", "PdfFile"})
.Type("Type_1", q => q.Columns("PdfFile"))
.Type("Type_2", q => q.Columns("PdfFile"))
.Type("Type_3", q => q.Columns("PdfFile"))
.Type("Type_4", q => q.Columns("PdfFile"))
.Type("Type_5", q => q.Columns("PdfFile"))
.Type("Type_6", q => q.Columns("PdfFile"))
.Type("Type_7", q => q.Columns("PdfFile"))
.Type("Type_8", q => q.Columns("PdfFile"))
.Type("Type_9")
.CheckPermissions();
var res = childNodes.ToList()
.Select(d => new TreeNodeDTO()
{
NodeID = d.NodeID,
DocumentID = d.DocumentID,
NodeHasChildren = d.NodeHasChildren,
DocumentName = d.DocumentName,
ClassName = d.ClassName,
FileGuid = safeValue(d)
});
return res;
}
The first statement in my method literaly joins VIew_CMS_Tree_Joined with 9 other coupled tables, filters only published, only children of particular node and checks the permissions - it sounds ridiculous and I've expected this to be awkward from performance stand point, but I was absolutely wrong - it is lighting fast.
TreeNodeDTO is just a container to transport needed columns to the client and contains just those 6 fields you can see in the second statement.
Summary
As you can see ASP.NET Web API works perfect in conjunction with Kentico API and results are even more amazing: the response time is minimum, page loads very fast as well as reacts very quickly on tree expanding. So we met our goal - client is happy with his product.
In general I want to stress once again, that you can easily build modern client page apps or SPAs providing your users with the great UX using Kentico CMS. What is important here that you do not need (actually you do not even have!) to change, modify or, in worth case, screw up Kentico functionality, you can politely extend it with whatever you need.
Please feel free to provide your thoughts on this topic or share your experience.