>
Blog
Book
Portfolio
Search

11/26/2013

8706 Views // 0 Comments // Not Rated

Consuming The SharePoint 2013 REST API From Managed Code

Introduction

As I get deeper and deeper into CSOM for SharePoint 2013, I've begun to discover some things that just don't work. That's not to say that they aren't supported by the client library or that they error out; they just silently fail and do nothing. Naturally, as with virtually every SharePoint 2013 CSOM issue I've ever had, I always first look into this being a security issue or an App Model misconfiguration.

But the setting of my findings of these issues wasn't through the App Model; it was actually a farm solution that used "conventional" CSOM to communicate with a remote web application. When I refer to "conventional" CSOM, I mean C# code using Microsoft.SharePoint.Client that just so happens to be running on a server. It's not SharePoint Server Object Model code; rather it's part of a job, a service, or a console or mobile app that's talking to a remote farm...or even the local farm.

Since the entry point into our REST API discussion will be within the paradigm of C# CSOM code, I want to begin by showing the logic that news up a "conventional" ClientContext, since we don't have TokenHelper sitting atop the security mountain of the App Model to do all the black box initialization work for us:

Code Listing 1

  1. public static ClientContext GetContextFromWebDotConfig()
  2. {
  3. //initialization
  4. ClientContext context = new ClientContext(ConfigurationManager.AppSettings["CSOM_URL"]);
  5. //get csom credentials
  6. context.Credentials= new NetworkCredential(
  7. ConfigurationManager.AppSettings["CSOM_User"],
  8. ConfigurationManager.AppSettings["CSOM_Password"],
  9. ConfigurationManager.AppSettings["CSOM_Domain"]);
  10. //no timeout (doesn't quite seem to work)
  11. context.RequestTimeout= int.MaxValue;
  12. //return
  13. return context;
  14. }

The four entries in the web.config file explicitly point to the destination SharePoint site collection (CSOM_URL) and authenticate as a well-known Windows user. This is effectively equivalent to SPSecurity.RunWithElevatedPrivileges, except better, as we can control exactly who we're impersonating.

Anyway, the point is that I can't blame the App Model (because I'm not using it) and I can't blame security (because I can impersonate anyone I want, which in this case is God). It took a while, but I eventually summoned up the audacity to assume that it wasn't my code that was failing; rather, it was CSOM itself that was letting me down.

So I turned my attention to the REST API, hoping that being a "lower-level" service (and thus allowing me to completely customize the request to SharePoint) I would have the power to do what I needed. I operate under the assumption that everything is possible via one of the SharePoint APIs; if one fails, another must pick up the slack. So far, these failings include:

  • Deleting a web
  • Setting "EnableFolderCreation" on a list
  • Setting "ContentTypesEnabled" on a list

It turns out that there's not a lot out there on the web discussing how to interact with the SharePoint 2013 REST API from "server" C# code. The technology is tailor-made to work with AJAX and the SharePoint JavaScript Object Model, but what about using it from the server to talk to remote farms? Or from a service, since we're not supposed to have anything from the Server Object Model in our apps?

Using HttpClient seemed like a good approach, especially if asynchronous support was a requirement. However, you have to deal with a lot of HTTP junk to get it to work. Therefore, I decided to go a level deeper down to the HttpRequest itself so that I can control the call entirely. If CSOM is failing me, I want to get as close to the wire as possible.

The RESTer

I created a class called "The RESTer" (or just RESTer.cs) that wraps all the HTTP requests needed to interact with the SharePoint 2013 REST API. This baby performs the following: manages headers, handles authentication, and provides support for both conventional CSOM and App Model CSOM. This is an important distinction, as the two paradigms for SharePoint development have vastly different security approaches.

The Payload

At the heart of this beast is the payload: the actual raw JSON that SharePoint reads off the wire. Each REST call is a single operation against a SharePoint object: create a list item, set a property on a web, delete a document from a library, etc. Therefore, this payload is essentially a JSON representation of the object we're operating against, and metadata about what we're doing to it.

The first bit of RESTer code we'll look at is a method that abstracts the creation of the payload by taking in two parameters: the "type" of object we're operating against, and a dictionary of properties. This dictionary's name/value pairs correspond to properties on the target object, and their vales. And as we're used to with RESTful programming, the verb of the request specifies what kind of CRUD operation we're doing.

Code Listing 2

  1. private string BuildPayload(ObjectType objectType, Dictionary<string, string> properties)
  2. {
  3. //initialization
  4. string type = string.Empty;
  5. //determine object type
  6. switch (objectType)
  7. {
  8. //list
  9. case ObjectType.List:
  10. type = "SP.List";
  11. break;
  12. }
  13. //build post body
  14. StringBuilder sb = new StringBuilder();
  15. properties.Keys.ToList().ForEach(k => sb.AppendFormat("'{0}': {1}, ", k, properties[k]));
  16. //return
  17. return string.Format("{{ '__metadata': {{ 'type': '{0}' }}, {1} }}", type, sb.Remove(sb.Length - 2, 2).ToString());
  18. }

In Line #1, ObjectType is a simple enum that represents the type of object we'll be operating against. Right now, I only use list, but this can easily be expanded to support any SharePoint JavaScript objects. The complete set is here. The switch statement on Line #6 converts the enum value into the text expected by the REST API. The properties are "serialized" into JSON on Line #15; no need for anything fancy here.

The magic happens on Line #18. Here is where we build out the body of REST calls that POST data to the server. In conventional MVC Web API, clients POST or PUT a JSON object over the wire, where it is automatically desterilized into a POCO on the other side. But as we all know, SharePoint is never quite conventional, so we have to build this metadata object instead. An example of JSON that updates the title of a list looks like this:

string json = "{ '__metadata': { 'type': 'SP.List' }, 'Title': 'New List Title' }";

The Authentication

Authentication in general makes a new twist in SharePoint 2013 with the App Model stuff. But at the end of the day, CSOM, weather it's running on a server or as part of an app, is a pretty bow on an ugly WCF package; we're still very much in the business of auth-ing service calls. Believe it or not, the App Model security is less complicated than the conventional CSOM, so let's discuss that part of the RESTer next.

<Note>

Configuring a SharePoint 2013 environment to work with apps is outside the scope of this post. I swear I'll get to it soon!

</Note>

The reason things are harder in this paradigm verses the App Model is because the aforementioned TokenHelper class that's generated for you every time you create a new SharePoint 2013 App project in Visual Studio isn't available. Now don't get me wrong: configuring everything on your environment to make TokenHelper work is a nightmare. However, assuming we're starting from an App-ready farm, conventional CSOM calling into the REST API needs some extra love.

Without getting into all the gory OAuth details, SharePoint 2013 REST API calls are authenticated via an access token that is generated for each user's "session" with the server. What TokenHelper is kind enough to do is provide this for us, using a certificate and a plethora of configuration behind it. This is the core of App Model security. So first, let's see the code to initialize the RESTer from an app, and then we'll look at the conventional paradigm.

Code Listing 3

  1. public RESTer(Uri siteUrl, string accessToken)
  2. {
  3. //set members
  4. this.AccessToken = accessToken;
  5. this.SiteUrl = this.ParseSiteUrl(siteUrl);
  6. }

This is the "App Model" constructor for the RESTer. It takes in the url of the target web site, as well as the access token provided by a properly-configured App Model CSOM ClientContext. We saw earlier how we get our conventional context; to extract an access token from an app's client context, refer to the second code listing in Part 2 of my App Model series. Then pass this string to the aforementioned constructor.

Since our conventional CSOM won't have an access token, we need to go fetch one. To start that process, we need to use the second RESTer constructor. It takes in a web url all the same as the first one, but instead of an access token, it starts its security journey with an instance of ICredentials. Here's what that looks like:

Code Listing 4

  1. public RESTer(Uri siteUrl, ICredentials credentials)
  2. {
  3. //set members
  4. this.Credentials = credentials;
  5. this.SiteUrl = this.ParseSiteUrl(siteUrl);
  6. //get digest
  7. this.Digest = this.GetDigest();
  8. }

We'll get to Line #7's GetDigest method in a second. It first needs to be stated that when newing up a RESTer with this constructor, simply pass in the Credentials property from your already-established ClientContext. Notice that both constructors scrub the url parameter (which is a Uri) though "PraseSiteUrl." All this does is convert it to a string and trim off any trailing slashes. This is important because we still be in the unfortunate business of cobbling fragments of urls together to form our RESTful calls.

The much more interesting GetDigest method makes an introductory call into the REST API to get our access token. This process is not too far off in nature to an NTLM handshake: make a call to the server with our credentials, get a request form digest (essentially an access token) back, and use that to auth any other calls we make during this session. The following code does this work:

Code Listing 5

  1. private string GetDigest()
  2. {
  3. //initialization
  4. XmlDocument doc = new XmlDocument();
  5. //build request
  6. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format("{0}/_api/contextinfo", this.SiteUrl));
  7. request.Method = Method.POST.ToString();
  8. request.ContentLength = 0;
  9. //set credentials
  10. if (this.Credentials != null)
  11. request.Credentials = this.Credentials;
  12. else
  13. request.UseDefaultCredentials = true;
  14. //make the call
  15. using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
  16. {
  17. //get response
  18. using (StreamReader sr = new StreamReader(response.GetResponseStream()))
  19. {
  20. //load xml
  21. doc.LoadXml(sr.ReadToEnd());
  22. XmlNamespaceManager manager = new XmlNamespaceManager(doc.NameTable);
  23. manager.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
  24. //return
  25. return doc.SelectSingleNode("/d:GetContextWebInformation/d:FormDigestValue", manager).InnerText;
  26. }
  27. }
  28. }

Starting on Line #6, we assemble a fairly straightforward credentialed HttpWebRequest that will post an empty body to the "contextinfo" endpoint. This article does an excellent job of describing the details. This line also feeds you a taste of the url building we'll be doing throughout this class. It might seem hacky, but welding urls together is required of any functionality that wraps a RESTful API. Later we'll break down the convention of what SharePoint 2013 expects its REST API urls to look like.

Next, Line #'s 7 and 8 configure our empty post. "Method" is another enum that will be included in the code attached to this article that simply contains all the HTTP verbs we'll be using. Next, we authorize the request on Line #'s 10 - 13, passing along the credentials from our CSOM ClientContext. The next few lines do the minutia of a WebRequest, so let's skip down to Line #21 where we start to extract our request form digest.

From here, the rest of the method is devoted to loading the XML that comes back from the request, parsing it, and getting our access token from it. Line #'s 22 and 23 deal with the namespace junk required for us to be able to query against it. Finally, Line #25 executes the query that grabs the "FormDigestValue" node's inner text. This return value is then stored in a global variable that the RESTer will use to do its thing.

The Request

Now that we have our payload, and can convince the server to talk to us, let's put all the pieces together into our REST call. For this example, we'll be setting two properties on a list. The following code listing is the bread and butter of the RESTer: it assembles the request object, along with its headers, url, authentication, and payload. Let's look at the code first, and then break it down step by step:

Code Listing 6

  1. private string DoRequest(Method method, Scope scope, string operation, string operationParameters, string payload)
  2. {
  3. //initialization
  4. string middlePart = string.Empty;
  5. switch (scope)
  6. {
  7. //web
  8. case Scope.Web:
  9. middlePart = "web";
  10. break;
  11. //site
  12. case Scope.Site:
  13. middlePart = "site";
  14. break;
  15. //list
  16. case Scope.Lists:
  17. middlePart = "web/lists";
  18. break;
  19. }
  20. //build request
  21. HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format("{0}/_api/{1}/{2}{3}", this.SiteUrl, middlePart, operation, operationParameters));
  22. request.Method = method == Method.GET ? "GET" : "POST";
  23. //set headers
  24. request.Headers.Add("IF-MATCH", "*");
  25. request.Accept = "application/json; odata=verbose";
  26. request.ContentType = "application/json; odata=verbose";
  27. request.Headers.Add("X-HTTP-Method", method.ToString().ToUpper());
  28. //set auth
  29. if (!string.IsNullOrWhiteSpace(this.Digest))
  30. {
  31. //windows auth (server code)
  32. request.Headers.Add("X-RequestDigest", this.Digest);
  33. if (this.Credentials != null)
  34. request.Credentials = this.Credentials;
  35. else
  36. request.UseDefaultCredentials = true;
  37. }
  38. else
  39. {
  40. //oauth (client code)
  41. request.Headers.Add("Authorization", string.Concat("Bearer ", this.AccessToken));
  42. }
  43. //handle post
  44. if (request.Method.Equals(Method.POST.ToString(), StringComparison.InvariantCultureIgnoreCase))
  45. {
  46. //set post body
  47. request.ContentLength = payload.Length;
  48. using (StreamWriter sw = new StreamWriter(request.GetRequestStream()))
  49. {
  50. //write post body
  51. sw.Write(payload);
  52. }
  53. }
  54. //make the call
  55. using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
  56. {
  57. //get response
  58. using (StreamReader sr = new StreamReader(response.GetResponseStream()))
  59. {
  60. //return
  61. return sr.ReadToEnd();
  62. }
  63. }
  64. }

Let's start at Line #'s 4 - 19. This builds out what I call the "middle part" of the RESTful url. This is a good place to discuss the anatomy of a SharePoint 2013 REST API url. The way I think of it, there are five components: Site url, API virtual directory, Middle part, Operation, and Operation parameters. Following is a blown-out example url that gets a list by unique id, annotated by component:

  • http://sharepointsite.com [Site url: The absolute url to a web.]
  • /_api [API virtual directory: Used for MVC Web API internal routing.]
  • /web/lists [Middle part: Think of this as the "type" of SharePoint object we're operating against.]
  • /GetById [Operation: Optionally, we can provide the "method" that we're using to fetch the aforementioned "middle part" object. I hate to use the word "method" in the context of REST, since that term is used to refer to the HTTP verb.]
  • (guid'7F431E04-B9F3-45B3-989D-DDA609511C5A') [Operation parameters: These are also optional, and provide the real querying power of RESTful calls. This gets really complicated, so for this post, I'll be using the simple "guid" example here.]

Stitched together, the url looks like this:

http://sharepointsite.com/_api/web/lists/GetById(guid'7F431E04-B9F3-45B3-989D-DDA609511C5A')

Now what do we do with this? Pass it to an HttpWebRequest, of course! Line #21 uses WebRequest's Create method to initialize a request with our url. Next, Line #22 sets the Method of the request. If urls are the most important part of a RESTful call, the method (verb) is a close second. The url tells the server what we're doing while the method tells it how to do it. What's counter intuitive about SharePoint's REST calls is that we don't set the specific verb here; it's "GET" for a GET and "POST" for everything else.

Indicating other methods, such as DELETEs, happens in the headers, which are set in Line #'s 24 - 27. I find The HTTP headers of a REST call to be a bit cryptic, so I err on the side of over-configuring. The first three Lines of this stanza basically indicate that we're going to speaking JSON with the server. The final Line, #27, is how we indicate the actual HTTP method we're intending SharePoint to execute. So if we're doing a PUT for example, we'd configure our request like so:

request.Method = "POST";

request.Headers.Add("X-HTTP-Method", "PUT");

Whereas a GET would be:

request.Method = "GET";

request.Headers.Add("X-HTTP-Method", "GET");

Next, in Line #'s 29 - 42, we do the authentication. This is the bit I mentioned that makes RESTer support both conventional and App Model CSOM. Basically, if we have a digest, we're Windows auth; add the token to the headers (Line #32) and pass the credentials through (Line #'s 33 - 36). As you can see on Line #36, we'll try the default credentials if none were provided. Line #41, which will be hit if we're using the App Model, set the passed in ClientContext's access token as the authorization header. Note that this token is prepended with "Bearer " (Don't forget the space after the final r!) to make all the OAuth 2.0 goodness work.

The rest of the method is fairly straightforward: if we're posting, add the payload to the request's body (Line #'s 44 - 53). Then make the call, get the response, and return the raw string that comes back from the server. Since my two examples are not GETs (MERGE for updates and DELETE for deletes) we don't really care about the result. But if we did, we'd see that a JSON representation of the object we're querying is what comes back (as expected). It looks like this:

<Raw JSON>

"{\"d\":{\"__metadata\":{\"id\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')\",\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')\",\"etag\":\"\\\"43\\\"\",\"type\":\"SP.List\"},\"FirstUniqueAncestorSecurableObject\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/FirstUniqueAncestorSecurableObject\"}},\"RoleAssignments\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/RoleAssignments\"}},\"ContentTypes\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/ContentTypes\"}},\"DefaultView\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/DefaultView\"}},\"EventReceivers\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/EventReceivers\"}},\"Fields\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/Fields\"}},\"Forms\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/Forms\"}},\"InformationRightsManagementSettings\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/InformationRightsManagementSettings\"}},\"Items\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/Items\"}},\"ParentWeb\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/ParentWeb\"}},\"RootFolder\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/RootFolder\"}},\"UserCustomActions\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/UserCustomActions\"}},\"Views\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/Views\"}},\"WorkflowAssociations\":{\"__deferred\":{\"uri\":\"http://sharepointsite.com/_api/Web/Lists(guid'e9f4ce24-91b4-40be-8c49-ea139cfdde0b')/WorkflowAssociations\"}},\"AllowContentTypes\":true,\"BaseTemplate\":850,\"BaseType\":1,\"ContentTypesEnabled\":true,\"Created\":\"2013-11-26T17:57:45Z\",\"DefaultContentApprovalWorkflowId\":\"00000000-0000-0000-0000-000000000000\",\"Description\":\"This system library was created by the Publishing feature to store pages that are created in this site.\",\"Direction\":\"none\",\"DocumentTemplateUrl\":\"\",\"DraftVersionVisibility\":1,\"EnableAttachments\":false,\"EnableFolderCreation\":true,\"EnableMinorVersions\":true,\"EnableModeration\":false,\"EnableVersioning\":true,\"EntityTypeName\":\"Pages\",\"ForceCheckout\":true,\"HasExternalDataSource\":false,\"Hidden\":false,\"Id\":\"e9f4ce24-91b4-40be-8c49-ea139cfdde0b\",\"ImageUrl\":\"/_layouts/15/images/itdl.png?rev=23\",\"IrmEnabled\":false,\"IrmExpire\":false,\"IrmReject\":false,\"IsApplicationList\":false,\"IsCatalog\":false,\"IsPrivate\":false,\"ItemCount\":2,\"LastItemDeletedDate\":\"2013-11-26T17:57:46Z\",\"LastItemModifiedDate\":\"2013-11-26T17:57:59Z\",\"ListItemEntityTypeFullName\":\"SP.Data.PagesItem\",\"MultipleDataList\":false,\"NoCrawl\":false,\"ParentWebUrl\":\"/\",\"ServerTemplateCanCreateFolders\":true,\"TemplateFeatureId\":\"22a9ef51-737b-4ff2-9346-694633fe4416\",\"Title\":\"Pages\"}}"

</Raw JSON>

As you can see, this is a CSOM-y JSON object. It's proper JSON, but the "CSOM-y" adjective I just invented means that it's in a SharePoint 2013-sepcifc dialect. For example, you'll see that a lot of the properties have a value of "__deferred" since we didn't initialize them. We're quite used to having to explicitly opt into all but the most basic property values of a CSOM object, and it's the same story with the REST API; we just do it through the original query (this is what the RESTer's DoRequest method's "Operation parameters" argument is for) instead of in a separate ExectuteQuery call.

In addition, you'll see the same "__metadata" "super property" we had to deal with in our payload that wraps all the others. The parent object is the old ".d" construct that was born of ASP.NET AJAX back in the 3.5 days. Encapsulating raw JSON results within this ".d" wrapper was a security enhancement introduced to protect us from an XSS vulnerability exposed in .NET 2.0. It's Interesting to see this concept still around in SharePoint, one-and-half versions of the framework later. Make sure your handling of RESTer's string results deals with it!

Conclusion

My plan moving forward for the RESTer isn't just to be a SharePoint 2013 REST API wrapper; I want it to be a full-service utility that sits alongside my CSOM library to compliment the functionality that it can't provide. To that end, I've wrapped my two example CSOM-failing issues into their own public methods in the RESTer: ConfigureListContentTypes and DeleteWeb. These simply puppet calls to DoRequest (see Code Listing 6) by setting parameters and handling return values. Let's take a look at them:

Code Listing 7

  1. public string ConfigureListContentTypes(Guid listId, bool enableContentTypes, bool enableFolders)
  2. {
  3. //initialization
  4. Dictionary<string, string> properties = new Dictionary<string, string>();
  5. properties.Add("EnableFolderCreation", enableFolders.ToString().ToLower());
  6. properties.Add("ContentTypesEnabled", enableContentTypes.ToString().ToLower());
  7. //return
  8. return this.DoRequest(
  9. Method.MERGE,
  10. Scope.Lists,
  11. string.Format("GetById(guid'{0}')", listId),
  12. string.Empty,
  13. this.BuildPayload(ObjectType.List, properties));
  14. }
  15. public string DeleteWeb()
  16. {
  17. //return
  18. return this.DoRequest(
  19. Method.DELETE,
  20. Scope.Web,
  21. string.Empty,
  22. string.Empty,
  23. string.Empty);
  24. }

The first method in Line #1, ConfigureListContentTypes, takes in the id of the list and flags for the two options. Line #4 throws them into a Dictionary with keys that match the actual property names of the object we're operating against. (Note that we're ToLower-ing the Booleans.) This is passed to the BuildPayLoad method (Code Listing 2) along with an ObjectType of "List" (which is a distinct enumeration from Scope because we need different string representations of a list for different aspects of the RESTer) in Line #13.

Finally, Line #8 sets the parameters for DoRequest. We pass a Method ("MERGE" since we're doing an update) and a Scope ("Lists" since we're working with a list). Then, Line #11 builds our "Operation" string with the query needed to get the object we're updating. It other words, this is how we get the list that we're setting new property values on. Since we have no operation parameters, we just assign them an empty string.

DeleteWeb, which starts on Line #15, is much simpler. All we have to do is set the Method to "DELETE" and the Scope to "Web" and RESTer will take care of the, well, rest. Since the url we pass into the constructor is already pointing to a web, we don't have to give it any operation or operation parameters. This shows us the true power of a REST API: as is, this will delete the web; if we change the Method to a GET, it will return the web. That's a win for modern web development!

That does it for the RESTer. As I said, the entire class is attached to this post. In conclusion, it seems to me that the SharePoint 2013 REST API was really designed to be consumed via AJAX from the client. However, remember that CSOM is all managed C#, and the "C" stands for "client." The RESTer is designed to make RESTful SharePoint calls from managed code easy. To wrap this up, I'll leave you with an extension method against ClientContext that shows the proper usage of the RESTer.

Have fun RESTing!

Code Listing 8

  1. public static void DeleteWeb(this ClientContext context, string url)
  2. {
  3. //get web
  4. Web web = context.Site.OpenWeb(url);
  5. context.Load(web);
  6. context.ExecuteQuery();
  7. //check web
  8. if (web != null)
  9. {
  10. //delete web - doesn't work!
  11. //web.DeleteObject();
  12. //context.ExecuteQuery();
  13. //delete web - totally works!
  14. new RESTer(new Uri(web.Url), context.Credentials).DeleteWeb();
  15. }
  16. }

No Tags

No Files

No Thoughts

Your Thoughts?

You need to login with Twitter to share a Thought on this post.


Loading...