4. Consuming ADO.NET Data Services in JavaScript
Perhaps the biggest advantage of the simplicity of
the ADO.NET services REST -based API is the possibility to consume it in
any platform. The formatted data is simply utf-8, so it can be consumed
in Java (or JavaScript for that matter).
Let's look at a few examples of consuming the service
in JavaScript. You can choose to
drag and drop the TestPage.aspx using SharePoint designer into the
content database, or you can choose to wrap it and deploy it as a
solution.
Let's look at the example of querying data using
JavaScript first. The necessary JavaScript required to fetch data over
the REST -based API can be seen in Listing 7.
Example 7. Querying Data using JavaScript
function load()
{
var request = new Sys.Net.WebRequest();
request.set_httpVerb("GET");
request.set_url(
"http://sp2010/_vti_bin/ListData.svc/Songs?$filter=Artist/Title eq 'George Michael'");
request.get_headers()['Accept'] = 'application/json';
request.add_completed(handleRequestComplete);
request.invoke();
}
function handleRequestComplete(response, userContext)
{
var songs = response.get_object().d;
var outputHtml = "" ;
for (var i = 0; i < songs.results.length ; i++)
{
outputHtml = outputHtml + "<br/>" + songs.results[i].Title ;
}
$get("results").innerHTML = outputHtml;
}
Sys.Application.add_load(load);
|
I'm using the System.Net.WebRequest class to
formulate my HTTP request. This is a simple GET request using the
$filter action to find all songs by George Michael. After I invoke that
request asynchronously, the results are handled in the
handleRequestComplete method. Using the response.get_object method, I am
able to get a handle to the actual object returned by SharePoint. Using
Fiddler you can also verify that the return is in JSON. I can then
easily parse the results and stick them in a DIV for the user to see.
The results can be seen in Figure 11.
Next is an example of inserting new entities into SharePoint lists using REST -based API and JavaScript. As is evident from Figure 5-17,
pressing the "Click to add an artist" button will call a JavaScript
method that will insert a new artist. This is where the simplicity of
JSON over ATOM shines. The code required to insert a new artist into the
list can be seen in Listing 8.
Example 8. Code Required to Insert a New Artist Using JSON
function AddNewArtist()
{
var request = new Sys.Net.WebRequest();
request.set_httpVerb("POST");
request.set_url("/_vti_bin/listdata.svc/Artists");
request.get_headers()['Accept'] = 'application/json';
request.get_headers()['Content-Type'] = 'application/json';
request.set_body("{Title: \"Aerosmith\"}")
request.add_completed(handleInsertCompleted);
request.invoke();
}
function handleInsertCompleted(response, userContext)
{
alert('New artist created') ;
}
|
I am sending a POST request, which is consistent with
the desire to be able to insert a new artist. Also pay attention to the
URL and calling, "/vti_bin/listdata.svc/Artists". I have set the format
and content-type to application/json, which allows me to specify a
rather terse and simple string identifying the data I wish to insert in
the following line:
request.set_body("{Title: \"Aerosmith\"}")
Now pressing the Click to add an artist button will
cause the Aerosmith artist to get inserted into the list. If you were to
open Fiddler, you would also note that the response to your POST HTTP
command is a JSON -formatted representation of the newly inserted
entity. This can be seen in Listing 9.
Example 9. Response of the POST Command
HTTP/1.1 201 Created
Cache-Control: no-cache
Content-Type: application/json;charset=utf-8
ETag: W/"1"
Location: http://sp2010/_vti_bin/listdata.svc/Artists(15)
Server: Microsoft-IIS/7.0
SPRequestGuid: bb765472-1671-4d60-bd7c-8ab7c96d30f0
Set-Cookie: WSS_KeepSessionAuthenticated={c19cc504-e161-4a5b-9dbc-f29d1cd1733d}; path=/
X-SharePointHealthScore: 0
DataServiceVersion: 1.0
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
MicrosoftSharePointTeamServices: 14.0.0.4536
Date: Fri, 25 Dec 2009 13:05:32 GMT
Content-Length: 575
{
"d" : {
"__metadata": {
"uri": "http://sp2010/_vti_bin/listdata.svc/Artists(15)", "etag": "W/\"1\"", "type":
"Microsoft.SharePoint.DataService.ArtistsItem"
}, "ID": 15, "ContentTypeID": "0x010055431F18E21FD240854A20B8B30D9FA0", "ContentType": "Item",
"Title": "Aerosmith", "Modified": "\/Date(1261728332000)\/", "Created":
"\/Date(1261728332000)\/", "CreatedByID": 1, "ModifiedByID": 1, "Owshiddenversion": 1,
"Version": "1.0", "Attachments": {
"__deferred": {
"uri": "http://sp2010/_vti_bin/listdata.svc/Artists(15)/Attachments"
}
}, "Path": "/Lists/Artists"
}
}
|
Next let us look of an example of deleting an artist
from the SharePoint list. Deletion requires you to query the entity you
wish to delete first and then pass in the appropriate ID to be deleted.
The code required to delete an artist from the SharePoint list can be
seen in Listing 10.
Example 10. Code Required to Delete Using JavaScript and JSON
function DeleteArtist()
{
var request = new Sys.Net.WebRequest();
request.set_httpVerb("GET");
request.set_url("http://sp2010/_vti_bin/ListData.svc/Artists?$filter=Title eq
'Aerosmith'");
request.get_headers()['Accept'] = 'application/json';
request.add_completed(handleDeleteQueryCompleted);
request.invoke();
}
function handleDeleteQueryCompleted(response, userContext)
{
var artists = response.get_object().d;
var aerosmithID = "" ;
for (var i = 0; i < artists.results.length ; i++)
{
aerosmithID = artists.results[i].ID ;
}
var request = new Sys.Net.WebRequest();
request.set_httpVerb("DELETE");
request.set_url("/_vti_bin/listdata.svc/Artists(" + aerosmithID + ")");
request.get_headers()['Accept'] = 'application/json';
request.get_headers()['Content-Type'] = 'application/json';
request.add_completed(handleDeleteCompleted);
request.invoke();
}
function handleDeleteCompleted(response, userContext)
{
alert('Artist Deleted') ;
}
|
Again, there are two asynchronous calls here. The
first call is to fetch data matching the Aerosmith artist. Like before,
because I have access to the entire object in JavaScript, I can easily
extract the ID of Aerosmith. Once I have that ID, I can execute another
HTTP command asynchronously, except in this case my HTTP verb is DELETE,
which causes the Aerosmith artist to be deleted from the SharePoint
list. Also because we are working in JavaScript we do not have lambda
expressions such as c#. So my code has been broken down into three
separate functions. You can however use inline functions in Javascript
and in effect achieve the same simplicity of code as lambda expressions.
5. Some Architectural Thoughts Before We Move On
I like the REST API. The simplicity of it simply
amazes me. But what is evident is that in querying data from SharePoint
you have numerous possibilities. You can use the server-side object
model; you can use LINQ to SharePoint. Or if you intend to work on the
client side, you can choose to use the client object model, or you can
use the REST API. Neither of these approaches is perfect, so you will
find yourself picking the right tool for the right job.
The server object model and a LINQ to SharePoint
mechanisms are restricted to server-side operations only. Specifically,
LINQ to SharePoint is somewhat inflexible because it restricts the
further schema changes you can make on your SharePoint site. However, it
is much more productive for the end developer as compared with working
with the server-side API.
I would venture to guess that most of the code you
will write going forward will require some sort of client-side
interaction. Client-side interaction means you will most definitely find
yourself using the client object model or the REST -based APIs. In
working with the client object model, I find my code to be very
maintainable. It does not suffer from schema changes on the web server,
breaking my code too often. In REST -based APIs, however, if I choose to
generate a reference, my generated reference is very closely tied to
the schema of my SharePoint site. I do not like that. This is primarily
because it results in fragile code that will break more often as the
schema changes. The beauty of REST -based APIs is that you don't have to
use the strongly typed data context. As you saw in the JavaScript
example, nothing really stops you from sending your own hand-crafted
HTTP WebRequests. And this is where the REST -based API is so amazing
all the work is done simply over URL using simple HTTP requests. Which
means your functionality can be consumed from any platform; it is
platform-independent.
I was saving the best part for last: the client
object model and the REST -based APIs are built on a singular .NET
technology: WCF. The advantage of the client object model and REST
-based APIs is that they are out of the box you didn't have to write
them; Microsoft wrote them for you. But when Microsoft writes something
for you; they create a one-size-fits-all approach. They don't author
business objects that map the structure of your own business needs, so
the data going over the wire does not have a hierarchical structure that
maps your business needs, vocabulary, or even data validations. This
reminds me very much of DataSets in ADO.NET 2.0. There used to be a big
discussion around whether DataSets or business objects are better. The
reality is that there are pros and cons of each. You can read my views
on DataSets vs. Business Objects here: http://blah.winsmarts.com/2006/06/02/are-you-a-business-object-person-or-a-dataset-person.aspx.
The client object model and the out-of-the-box REST
-based APIs are the equivalent of datasets in SharePoint. They are dumb,
they do not have the structure of your business data, they do not
reflect the right vocabulary, and they do not have custom validations.
Their payload is what is commonly referred to as data transfer objects
(DTOs). Also, you have a limitation on which clients or technologies can
be used to consume these services! The entire server-side API is not
open for use using the client object model and the REST -based APIs. The
advantage of course is that you don't have to write the client object
model - it is already written for you.
The beauty of SharePoint 2010, however, is that you
can author your own custom WCF services. These custom WCF services can
then choose to expose custom business objects, which buys you all the
advantages of using custom business objects. Also you have the ability
to switch bindings at will and support different technologies and WS-*
standards on your services.
Think of the client object model and REST -based
APIs, as a one-size-fits-all approach. And think of WCF as a
custom-tailored suit. One is cheaper, but not as architecturally good.
The other is more expensive, but also architecturally superior.
Now look at your wardrobe! How many custom-tailored
suits do you have? If you are anything like me, I'm guessing you have
more one size fits all T-shirts than you have custom-tailored suits.
For larger and more complex projects, you will find
yourself writing more WCF services, but for a large percentage of
projects, the out-of-the-box client object model and REST -based APIs
are just fine.
Another instance when I personally find myself
writing WCF services is when I need to break out of the limits of a
sandbox solution. One of the possible
ways of breaking out of the sandbox solution boundaries is to write a
full trust proxy. The full trust proxy architecture however is very
rigid, it requires you to shoe-horn everything you need into a few
classes you have to inherit from. That makes it very inflexible. Also
deployment requires you to touch the actual API. The full trust proxy
has to be registered with SharePoint by calling an API method. Thus
instead of using a full trust proxy, I find myself writing custom WCF
services instead. This is simply because it is better architecture.
So let's next learn about writing custom WCF services in SharePoint 2010.