Welcome dear reader
In this article I want to dive a bit into helping you decide (or at least share some thoughts) regarding quite common problem that we had on last Sitecore project and that was : there was a need to have some sort of API (yes, I am under influence of ASP.NET MVC WEB API đ and I like it) or some kind of endpoint that we could access from browser (or perhaps some device in future) and use it to query data, best nicely packaged as JSON objects, ready to use. Not that big deal you say, Sitecore is WebForms ASP.NET app, we have support for exposing JSON, it is possible. Yes it is and we do have the support. But we can have a bit trouble with fact, we are using Sitecore (since it is a platform, with many things added in web.config file, etc.) and if you are lazy as me.
Sitecore from version 6.6 (as described here) supports MVC (at last) and this can help us, but I was in a situation when we didnât decided to use MVC so introducing this change for just one thing (my API) only would be overkill and too much work. So I searched a bit and I found out that the best solution for me would be a dedicated Web Service .asmx file (oh my, I took part on bigger MVC project before and going back to files from MVC point of view just doesnât seems right).
But there are more options, why this asmx way. Let me elaborate more on them, but please first read this GREAT article about same problem but from ASP.NET point of view :
http://johnnycode.com/2012/07/16/getting-json-from-asp-net-beautiful-good-bad-ugly/
Done with the article?
Good, so here are my thoughts
- Web API would be the best solution here, if you look at how the code clean is and with fact in my mind â it is built for this very thing you need right now, but sry, no MVC this time, so there also goes my evil plan for dedicated MVC controller, well⊠But I would advice you go this way, it has its benefits,
- Dedicated handler? Would work, but too much low level work (setting content type??? in 2013?? now come on),
- Static method on some dedicated page (it works, I used this approach as a âservice APIâ for one big Web Form but that was 3y back) would also do the work, but its really not elegant and since Sitecore has something like a routing (you donât really access files in their physical folders, but traverse the logical tree in DB more or less), I had feeling it is not the right solution to my problem,
- So here we are with my dedicated Web Service , some .asmx file that would do the job. IMO this was just what I needed, adding one dedicated file as a endpoint we can call will not ruin anything (ehm, there will be more files as I will show you later), we have support to just return something that will ASP.NET runtime serialize to JSON without much work (I will show you some tweaks I found on the web to optimize load and more), perfect fit for my needs.
Sitecore Item Web API 1.0.0
I have to interrupt this story to make it more complete here. Before I investigated how to send some items down the drain as JSON I had a talk with guys from Sitecore about what to use, they also said that there is one thing I could use besides Web API and that is their own product : Sitecore Item Web API, that was and still is in version 1.0.0 with date 28.08.2012. This is a good thing but has itâs weaknesses (but maybe it was just me, although I have studied also documentation but maybe you can prove me wrong and this tool is what I needed) and strengths. More on them here :
- You can (and should) use Advanced Security where you explicitly have to tell what fields you are exposing and then either query particular ones or donât specify any and you should get all,
- I wasnât able to specify what branches of my Sitecore can I query so I was able to query everything. Also system parts of it. This I really regard as a security issue (or it was just me and my mistake with setting things up),
- Not needed data were sent down the wire like ID of record, whether is has children, what database I am accessing and more. Stuff that just wasnât needed and again was a security issue (from my point of view),
- Desired properties were stuffed to one property of item and if property had space in the name, I had to use this approach : http://stackoverflow.com/questions/4042646/how-to-access-json-object-which-has-space-in-its-name,
- Regarding using dedicated account with own name and pwd, that might me the way for some âhiddenâ app that would need API access via web, but if I would write JS code in some lib and use it, that would be the first thing where I would look if I would like to try to hack my app,
- The final thing I had problem with was XPath query with some parameter that I had to URL encode to get it work and it worked, but then suddenly a business requirement came that would need me to query API, assemble some information, do something with it and re-query for final results. But this is business logic that isnât supposed to be on the client (another example could be some simple JS validation logic that would make sense, but not this type + one more HTTP request = more possible delays, more hits to DBâŠ. that just wasnât good)
I just wasnât fully in the control of this thing. So I decided to investigate on possibility that give enable me full control of what I send down the wire. And encapsulate some business logic that could be reused somewhere else, in code behind, etc⊠Client doesnât and should have to know about relationships between objects, how I can get what from this and that. APIs are the encapsulation of some business logic, in this particular case exposing it to HTTP access via Web Service. Or at least this is my understanding of how things should work.
OK now some good advices I learned while working on the project, these are more or less generic to ASP.NET as such. JS code
JS code could look like this
$(â#value-buttonâ).click(function () {
$.ajax({
type: âPOSTâ,
url: â/Service/Service/CustomAPI.asmx/ComputeMeValueâ,
data: â{ "value" : "â + $(â#value-inputâ).val() + â" }â,
//data: JSON.stringify(data), â use this if you have custom json object with propertiesâŠ
contentType: âapplication/json; charset=utf-8â,
dataType: âjsonâ,
success: function (data) {
$(â#outcome-paragraphâ).text(data.d);
},
error: function (data) {
alert("something bad happened, we are sorryâŠ");
}
});
});
This is a small snippet from sample project I will post with this article.
I would advice you to have also error part in the JS code so once your code throws some exception, user will be notified.
If you are not posting parameters, you should still send empty {} in JS code, or send values you want on the server. Names and types have to match obviously.
Web Service code
At first, there are more approaches here. You can layer/separate you code in own project if it gets big, but for my small API I was able to get away with subdirectory for Model, where I stored POCO classes (in MVC project this could go to separate Class Library) and with separate subdirectory for service as such.
The service could be divided to
- BLL layer that could store business logic (how to combine objects, how to create VMs â view models, what to query for what),
- Mapping between domain objects (object we have from Sitecore API, or EF or some ORM mapper in case of MVC) and VMs we have in Model subdirectory (this could be in BLL or as separate library, or in MVC in the model project, if you donât have BLL project),
- MVs â View Model classes in Model subdirectory.
We could also use DI â dependency injection as we do in MVC but all this and above really depends on, if it makes sense for you or not, if it helps you or its overkill on smaller project. If you will/need unit test or not. Your project, your rules. (but unit tests are good, for you :))
Small tweaks to the code (the price to pay for Web Service .asmx approach)
Declare the type of what you return as object (I found out this on the web after few hours of pain with dynamic, that I was used to from MVCâŠ.). This has some nice side effects. First of all you can return either string, or int if something goes wrong with the query and there is nothing to send back, if you have some data to send, then just send List<User>
for example. Neat thing to save bandwidth is that you can send down objects of say User and SuperUser and AdminUser that can be extended with some new additional properties and these will be just on the items that have them. You have to declare the object that will be stuffed with these different objects as List<object>
and then you can add to it what you want/need. After you are finished, just return it. But you have to deal with this (different types of objects) in your JS code of course. The last thing why I would advice you to use object as return type is, that plumbing in .asmx file will send down in JSON for each object also its type, like this :
Just by declaring return type as object, with the very same code you will have this response :
Magical, right? The best time to switch to ASP.NET MVC đ
Another thing I wasnât able to make work was something like ASP.NET MVC model binding. MVC has nice functionality that will turn parameters from cookies, query string, posted values, all to one object that action on controller has as parameter (matching names of values and properties on object). So you donât end up with 30 parameters per one action. Here you have to use older approach with no binding, I am sorry.
OK so hopefully I have not forgot something, if you have any questions, just write to the comments, and here is the promissed link to small sample page for download : http://sdrv.ms/WZmVq5 enjoy.
Hope this helps.