Second Life of a Hungarian SharePoint Geek

May 25, 2011

Interpreting network traffic generated by the SharePoint client object model

Filed under: Fiddler, Managed Client OM, SP 2010 — Tags: , , — Peter Holpar @ 00:05

Although the developer documentation of SP 2010 is far better than it was for WSS 3.0, I found that new components, like the client OM, are still rather undocumented.

Last summer I spent a few days with researching the internal working of SP 2010 client OM and past week I was back to this exploration, so this time I try to blog some of the results.

In lack of space and time, in this post my goal is simply to help you to understand some of the basic concepts about SP client OM client / server communication to be able to start your own investigation. In forthcoming posts I plan to dig deeper in theory and practice as well. I found that client OM is not a simple part of SP 2010, and although I feel I’m just scratching its surface, I think there are some interesting results so far.

As you may know (or at least should assume) the client OM talks with the server side components. Analyzing the network traffic is extremely useful to get some info about the client object model internals, and troubleshooting client applications. To learn more about the content of the network communication I used Fiddler that is great toy to capture HTTP based traffic.

First, I started with a simple client application, like this one:

  1. ClientContext clientContext =
  2.   new ClientContext("http://pholparlaptop");
  3. Web web = clientContext.Web;
  4. clientContext.Load(web, w => w.Title);
  5. clientContext.ExecuteQuery();
  6. String title = web.Title;

If you step through the code in Visual Studio, you find there is no traffic until the ExecuteQuery method is called. At that point Fiddler captures this sessions:

image

First the GetUpdatedFormDigestInformation method of the Sites web service is called (the first two sessions are about security negotiation), you can see the token received in the XML view of the response from session 3, including the expiration time in seconds (see full response format here).

image

This token is used on forthcoming requests, see it in the HTTP header of the request from session 4 in its raw format:

image

Otherwise, I suggest you to use the XML view for client object model request, since client OM sends the request in XML format when calling the ProcessQuery method of the client.svc WCF service. 

image

In contrast to the XML format of the request, the response is a JSON (JavaScript Object Notation) serialized object tree, so it is probably best to view in the raw mode.

image

OK, it’s nice, but how does the request XML relate to the original code and what is this JSON response?

In the request above you can see that there are two main parts of XML, first is Actions node, second is ObjectPaths node. Each subnode has a unique integer identifier in their Id attribute. This Id is common for both Actions and ObjectPaths subnodes, and if there are multiple ExecuteQuery calls from the same context, the Id will be incremented further in subsequent requests. These Ids are for referencing other XML elements within the request and to refer back to the request from the response.

The following figure shows the same requests, but this time I’ve added some colors to the content to help the interpretation.

image

The request processor on the server side works like this:

Take the first subnode from Actions node. It is an ObjectPath element having its ObjectPathId attribute is set to 1 (marked with a blue circle). This instructs the request processor to lookup the subnode of the ObjectPaths that has an Id attribute with value 1. This is a StaticProperty element (see the other blue circle) with a TypeId attribute having a specific GUID value of {3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a} and Name attribute with value “Current”.

This GUID identifies the Microsoft.SharePoint.SPContext class in the Microsoft.SharePoint assembly, as it is decorated with the following ClientCallableTypeAttribute:

ClientCallableType(Name="RequestContext", ServerTypeId="{3747ADCD-A3C3-41b9-BFAB-4A64DD2F1E0A}", Internal=true)

And the Name = "Current" means the static Current property of SPContext:

[ClientCallable]
public static SPContext Current

The server side class and method have their pairs on the client side as well. The class (as signed by the value of the Name property of the ClientCallableTypeAttribute above) is the Microsoft.SharePoint.Client.RequestContext defined in the Microsoft.SharePoint.Client assembly. This class is internal, as value of the Internal property of the ClientCallableTypeAttribute predicted:

[ScriptType("SP.RequestContext", ServerTypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}")]
internal class RequestContext : ClientObject

The RequestContext class has a GetCurrent method that reads the static Current property of SPContext on the server side.

[Remote]
public static RequestContext GetCurrent(ClientRuntimeContext Context)
{
    object obj2 = null;
    if (!Context.StaticObjects.TryGetValue("Microsoft$SharePoint$SPContext$Current", out obj2))
    {
        obj2 = new RequestContext(Context, new ObjectPathStaticProperty(Context, "{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}", "Current"));
        Context.StaticObjects["Microsoft$SharePoint$SPContext$Current"] = obj2;
    }
    return (RequestContext) obj2;
}

So the first subnodes of Actions and ObjectPaths is about getting the current context for forthcoming actions. How the response looks like will we see a bit later.

Now let’s see the second subnode of Actions. It is again an ObjectPath element but its ObjectPathId attribute is set to 3 (marked with a yellow circle). This instructs the request processor to lookup the subnode of the ObjectPaths that has an Id attribute with value 3. This time it  is a Property element (see the other yellow circle) with a ParentId = "1" and Name attribute with value “Web”. It means, the processor should read the Web property of the object that has Id = "1". As you remember it is the RequestContext (SPContext on the server side) object from the first step.

The last subnode of the Actions node is a bit more complicated one. Its ObjectPathId attribute is set to 3 (marked with a yellow circle again), meaning it operates on the object received in the former step as part of the property read (see Property subnode of the ObjectPaths with Id = "3") that is the Web property of the context. This action is a Query, with SelectAllProperties = "false", so we don’t get all properties, only the one(s) listed in the Properties node collection, this time the property Title.

Subnodes of the Actions node with Id 2, 4 and 5 are marked with rose, brown and green colored rectangles in the request above. Let’s see the response and identify the items corresponding to these subnodes.

I marked the response items with the same color as their request action item counterparts.

image

(Note, that there is no error, the ErrorInfo is null.)

For the first two actions (these are the ObjectPath subnodes) the response is a simple IsNull = false, so the objects retrieved as expected.

The response for the Query action has an _ObjectType_ property with value SP.Web (it is the ScriptType of the object, it is called Web in the managed client OM, and SPWeb in the server side API).

The complex value of _ObjectIdentity_ means:

740c6a0b-85e2-48a0-a494-e0f1759d4aa7 stands for the Microsoft.SharePoint.SPObjectFactory class (Microsoft.SharePoint assembly):

[ClientCallableObjectFactory(ServerTypeId="740c6a0b-85e2-48a0-a494-e0f1759d4aa7")]
internal class SPObjectFactory : ClientCallableObjectFactory

In SPObjectFactory there are methods to get the so-called canonical IDs for specific object type instances. For example, the format for SPWeb instances generated by the GetWebCanonicalId method:

internal static string GetWebCanonicalId(SPWeb web)
{
    return CombineId(new string[] { "web", web.ID.ToString("D") });
}

So the second part of the object identity (web:dd9749b4-bdc1-46a7-b703-c5b796945205) is the canonical ID of my web instance (the ID of the SPWeb is dd9749b4-bdc1-46a7-b703-c5b796945205).

Last, but not least, there is the Title property of the web we requested and it is “Home”.

In the next example we get a reference for the Links list.

  1. ClientContext clientContext =
  2.   new ClientContext("http://pholparlaptop");
  3. Web web = clientContext.Web;
  4. // this would generate the same request
  5. //List list = web.Lists.GetByTitle("Links");
  6. ListCollection lists = web.Lists;
  7. List list = lists.GetByTitle("Links");
  8. clientContext.ExecuteQuery();

This is the request sent to the server:

image

We have more Actions and ObjectPaths subnodes due to the more referenced objects in the code. Instead of getting the Property we call the method GetByTitle with the parameter value Links.

And it is the response from the server:

image 

The last item is the reference to the list, its object identity (740c6a0b-85e2-48a0-a494-e0f1759d4aa7:web:dd9749b4-bdc1-46a7-b703-c5b796945205:list:dd2271a6-9217-4d8d-88ec-861eafb5bed2) is built up again from the ServerTypeId of the SPObjectFactory and the canonical ID of the list. The latter one is constructed by these methods of SPObjectFactory:

internal static string GetListCanonicalId(SPList list)
{
    return GetListCanonicalId(list.ID, list.ParentWeb.ID);
}

internal static string GetListCanonicalId(Guid listId, Guid inWebId)
{
    Guid iD;
    if (listId == SPContext.Current.Web.UserInfoListId)
    {
        iD = SPContext.Current.Web.ID;
    }
    else
    {
        iD = inWebId;
    }
    return CombineId(new string[] { "web", iD.ToString("D"), "list", listId.ToString("D") });
}

Finally, let’s see what happens on the wire when there is an error during code execution. The following example tries to get a list that does not exist on the server.

  1. ClientContext clientContext =
  2.   new ClientContext("http://pholparlaptop");
  3. Web web = clientContext.Web;
  4. ListCollection lists = web.Lists;
  5. List list = lists.GetByTitle("DummyList");
  6. clientContext.ExecuteQuery();

The request is the same as in the former example, only there is a different parameter value when calling the GetByTitle method.

The response is the following:

image

Now the ErrorInfo property is not null, instead it contains the ErrorMessage, ErrorCode, ErrorTypeName and ErrorValue (this one is null) properties.

The value of the the ErrorMessage property is displayed in Visual Studio when calling the ExecuteQuery method:

List ‘DummyList’ does not exist at site with URL ‘http://pholparlaptop’.

That’s all for now. Although illustrated only with some really basic examples, I hope this post can serve as a starting point to your journey into the client OM internals. I suggest you to build your own client OM applications from simplest to the more advanced ones and monitor the network traffic to see the effects of code changes.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: