SDK in .NET

SDK in .NET

SDK in .NET

Microsoft released a NuGet package called Microsoft Graph SDK for .NET, which enables you to accomplish some of the most common and useful tasks by using a helper library, without having to dig into the details of the HTTP and REST protocols.

 

In this blog, you will learn about the architecture, the functionalities, and the capabilities of the Microsoft Graph SDK for .NET and about the Microsoft Graph SDKs for all the other platforms. All the code samples and snippets illustrated in this blog are available as a sample application on GitHub in the OfficeDev PnP repository at the following URL: 

https://github.com/OfficeDev/PnP/tree/master/Samples/MicrosoftGraph.Office365.DotNetSDK

 

Introduction to the Microsoft Graph SDK

First of all, it is important to say that the Microsoft Graph SDK for .NET is just one flavor of many. At the time of this writing, the Microsoft Graph SDK is available for the following platforms:

  • Microsoft .NET
  • iOS
  • Android

 

The SDKs for Node.js, PHP, Python, Ruby, and AngularJS are under development. The basic idea of this SDK is to leverage the Graph REST metadata to generate the domain model objects representing the entities provided by the Microsoft Graph, targeting all the main platforms. For example, the .NET SDK is made of a .NET Portable Library, which targets Microsoft .NET 4.5, .NET Windows Store apps, Universal Windows Platform (UWP) apps, and Windows Phone 8.1 or higher.

 

All the types that define objects like UserContactGroupDrive, and so on are autogenerated. There are some more infrastructural and helper types that are written manually and made available as open source on GitHub (https://github.com/microsoftgraph/msgraph-sdk-dotnet/), while the tool used to generate the domain model objects are based on VIPR (https://github.com/microsoft/vipr) and is available at the following URL: https://github.com/microsoftgraph/MSGraph-SDK-Code-Generator.

 

Registering the app and using the SDK

From a developer perspective, to use the Microsoft Graph SDK for .NET you just need to add the NuGet package with name Microsoft graph to your projects. Like many other packages, the package has a dependency on the Newtonsoft.Json package.

 

Once you have a reference to the Graph SDK, you will need to create a client session to consume the Microsoft Graph API. The first step to create a client session is to register your application to use the Graph API. At the time of this writing, it is possible to register an application through two different platforms:

 

Microsoft application registration portal: This is a new kind of registration that is still under review at the time of this writing. It provides a unified authentication model that embraces both Azure AD organizational accounts (work and school accounts) and Microsoft accounts (personal accounts). Often, it is defined as the v2 authentication endpoint.

 

More Information

For further details about the v2 authentication endpoint, you can read the article “Authenticate Office 365 and Microsoft free personal email APIs using the v2.0 authentication endpoint preview,” which is available at the following URL: https://msdn.microsoft.com/en-us/office/office365/howto/authenticate-Office-365-APIs-using-v2.

 

Moreover, if you want to learn about the current limitations of the v2 authentication endpoint, you can read the article “Should I use the v2.0 endpoint?” at the following URL: https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-limitations/.

 

In the following paragraphs, you will learn how to leverage each of these two registration models, which imply a corresponding authentication and authorization model. Regardless of the registration model, you will have to provide the authentication logic to the Microsoft Graph SDK to be able to consume the Graph API.

 

Out of the box, the Microsoft Graph SDK does not have any authentication logic. Instead, there is an abstract interface called IAuthenticationProvider that has to be implemented by a class that implements the real authentication. In Listing, you can see the definition of the IAuthenticationProvider interface.

 

LISTING The IAuthenticationProvider interface definition in the Microsoft Graph SDK

namespace Microsoft.Graph {
using system.net - Domain Name For Sale | Undeveloped.Http;
using System.Threading.Tasks;
<summary>
Interface for authenticating requests.
</summary>
public interface IAuthenticationProvider {
<summary>
Authenticates the specified request message.
</summary>
<param name="request">The <see cref="HttpRequestMessage"/>
to authenticate.</param>
<returns>The task to await.</returns>
Task AuthenticateRequestAsync(HttpRequestMessage request);
}
}

 

The interface defines only an asynchronous method called AuthenticateRequestAsync, which will be responsible for handling any authentication technique by managing the input object of type HttpRequestMessage before sending the request on the wire.

 

Out of the box, the SDK provides a class called DelegateAuthenticationProvider, which implements the IAuthenticationProvider interface and internally uses a delegate to process any authentication logic. If you registered your application in Azure AD, you will use Microsoft Active Directory Authentication Library (ADAL) to get an access token from the OAuth 2.0 endpoint and to put it into the headers of the HttpRequestMessage.

 

If you registered the application in the Microsoft application registration portal, you will be able to use the new Microsoft Authentication Library (MSAL) to get the access token from the new v2 authentication endpoint, and you will put it into the headers of the HttpRequestMessage object.

 

More Information

The Microsoft Authentication Library (MSAL) is a new library, still under preview at the time of this writing, which has been announced at // Build 2016 and which targets the new v2 authentication endpoint. For further information about MSAL, you can read the document “Microsoft Identity at //build/ 2016,” which is available at the following URL: 

https://blogs.technet.microsoft.com/ad/2016/03/31/microsoft-identity-at-build-2016/. You can also watch the video “Microsoft Identity: State of the Union and Future Direction” that is available at the following URL: https://channel9.msdn.com/Events/Build/2016/B868.

 

The Graph SDK is completely independent and agnostic from the authentication logic. Thus, if you like you can implement your own logic to retrieve an access token by using the OAuth 2.0 protocol at a low level. Once you have defined the authentication technique, you are ready to create a Graph SDK client object, which is based on type GraphServiceClient. The constructor of the GraphServiceClient type accepts up to three arguments:

 

baseUrl The base URL of the target service endpoint, which is the Microsoft Graph API endpoint. For example, it can be https://graph.microsoft.com/v1.0. By default, it assumes the value https://graph.microsoft.com/currentServiceVersion, and it is an optional argument.

 

authenticationProvider Is of type IAuthenticationProvider and will handle the authentication logic. It is mandatory, and usually, you can provide an anonymous delegate that implements the authentication logic.

httpProvider Is responsible for providing the HTTP protocol support together with the serialization of the messages. It is optional.

 

In Listing, you can see a code excerpt taken from an ASP.NET MVC web application, which is included in the code samples of this book, that initializes a GraphServiceClient instance by using ADAL. In such an example, you have to install the Microsoft.IdentityModel.Clients.ActiveDirectory (that is, ADAL) package from NuGet.

 

LISTING Creation of the GraphServiceClient class using ADAL and Azure AD for authentication

var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
(requestMessage) => {
// Get back the access token.
var accessToken = ADALHelper.GetAccessTokenForCurrentUser();
if (!String.IsNullOrEmpty(accessToken)) {
Configure the HTTP bearer Authorization Header requestMessage.Headers.Authorization = new
AuthenticationHeaderValue("bearer", accessToken);
}
else {
throw new Exception("Invalid authorization context");
}
return (Task.FromResult(0));
}));

 

In the code excerpt, there is a helper method called GetAccessTokenForCurrentUser that internally handles the retrieval of the OAuth 2.0 access token by using the AuthenticationContext of ADAL. In Listing, you can see a code excerpt that uses the DelegateAuthenticationProvider class to authenticate against the v2 authentication endpoint by using MSAL. In this case, you have to install the Microsoft.Identity.Client (that is, MSAL) pre-release package from NuGet.

 

LISTING Creation of the GraphServiceClient class by using MSAL and the v2 authentication endpoint

 GraphServiceClient graphClient = new GraphServiceClient( new DelegateAuthenticationProvider( async (requestMessage) => {
Configure the permissions String[] scopes = {
"User.Read",
"Mail.Send",
};
Create the application context.
var clientApplication = new PublicClientApplication(MSAL_ClientID);
// Acquire an access token for the given scope.
clientApplication.RedirectUri = "urn:ietf:wg:oauth:2.0:oob"; var authenticationResult = await
clientApplication.AcquireTokenAsync(scopes);
// Get back the access token.
var accessToken = authenticationResult.Token;
Configure the HTTP bearer Authorization Header requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", accessToken);
}));

 

The MSAL library requires you to create one object of type PublicClientApplication if you are in a native application, like we are in the console application from which the code excerpt of Listing has been taken. Otherwise, if you are in a web application, you have to create an instance of the ConfidentialClientApplication type. The latter is defined “confidential” because it involves the use of a shared secret, which can be generated in the application registration portal, if needed.

 

As you can see, highlighted in bold is a set of scopes defined and provided to the PublicClientApplication instance to declare the permissions requested for the current authentication phase. Thus, instead of having to declare all the permissions while configuring the application, as with Azure AD and ADAL, with MSAL you can just ask for a set of permissions and try to get a consent for them. Depending on the requested permission scopes, you will need a user’s consent or an administrator’s consent.

 

The list of available permissions corresponds to the list of permissions defined in the Microsoft Graph and is available in the document “Microsoft Graph permission scopes” at the following URL: http://graph.microsoft.io/en-us/docs/authorization/permission_scopes. In case you provide an incorrect or nonexistent permission in the list of permissions scope, you will get back an exception like the following:

 

AADSTS70011: The provided value for the input parameter 'scope' is not valid. The scope <Wrong list of permissions> openid email profile offline_access is not valid. Notice that aside from the custom permissions that you provide to MSAL through the scope, internally it will always add permission requests for signing in the user (openid), reading the user’s email and profile, and accessing the user’s data any time (offline_access).

 

Moreover, the tenant scope is also implied by the provided user’s credentials, and you don’t need to register any specific tenant in the application registration portal or provide a target tenant to the PublicClientApplication instance. The v2 authentication endpoint and MSAL are multitenant by default.

 

For the sake of brevity, and because at the time of this writing MSAL is still under preview and in alpha release, we will not go deeper into this topic. However, we will use it in the code samples related to this blog to keep the samples up to date.

 

Regardless of how you created the GraphServiceClient instance, once you have it you can just browse the properties of the object, which provide a fluent API of objects and a common and useful query model. The objects provided by the Graph SDK domain model map to the resources published by the Microsoft Graph REST API and are provided on the .NET side as property bag classes. For example, in Listing you can see a code excerpt to query the DisplayName property of the current user (me).

 

LISTING Code excerpt to query the current user’s DisplayName property

var me = await http://graphClient.Me.Request().Select("DisplayName").GetAsync(); var displayName = me.DisplayName;

Notice that the Graph SDK object model is completely asynchronous and the syntax for querying objects looks similar to that of LINQ (Language Integrated Query), but it is not identical and you cannot use LINQ and IQueryable<T> to query the Graph SDK domain model.

 

Under the cover of every request, there will be an HTTP request handled by the Graph SDK engine through the implementation of the HTTP provider and a process of serialization/deserialization of JSON objects into domain model objects.

 

Request model

To better understand how the Microsoft Graph SDK works, it’s worth digging a little deeper into the request model that sits under the cover of the library.

 

Whenever you want to access one or more resources, you have to ask the Graph SDK to make a REST request for you. Internally, the SDK has an engine made of request builders that are invoked whenever you call the Request() method of a target object. Because the Microsoft Graph API is continuously growing and evolving, the request builders are generated together with the domain model types that map the resources by using the $metadata endpoint of the target Graph API.

 

The VIPR project, referenced earlier in this blog, is responsible for the autogeneration of all these types. Moreover, to allow easy unit testing of custom developed solutions, all the request handlers implement a specific interface that is autogenerated by VIPR. The listing shows how the GraphClientService type is defined from an interface-level perspective at the time of this writing.

 

LISTING The interface-level definition of the GraphServiceClient type

public class GraphServiceClient : BaseClient, IGraphServiceClient, IBaseClient {
public GraphServiceClient(IAuthenticationProvider authenticationProvider, IHttpProvider httpProvider = null);
public GraphServiceClient(string baseUrl, IAuthenticationProvider authenticationProvider,
IHttpProvider httpProvider = null);
public IGraphServiceDevicesCollectionRequestBuilder Devices { get; }
public IGraphServiceDirectoryObjectsCollectionRequestBuilder DirectoryObjects { get; }
public IGraphServiceDirectoryRolesCollectionRequestBuilder DirectoryRoles { get; }
public IGraphServiceDirectoryRoleTemplatesCollectionRequestBuilder DirectoryRoleTemplates { get; }
public IDriveRequestBuilder Drive { get; }
public IGraphServiceDrivesCollectionRequestBuilder Drives { get; }
public IGraphServiceGroupsCollectionRequestBuilder Groups { get; }
public IUserRequestBuilder Me { get; }
public IGraphServiceOrganizationCollectionRequestBuilder Organization { get;
}
public IGraphServiceSubscribedSkusCollectionRequestBuilder SubscribedSkus { get; }
public IGraphServiceSubscriptionsCollectionRequestBuilder Subscriptions { get; }
public IGraphServiceUsersCollectionRequestBuilder Users { get; }
}

 

As you can see, every complex property like the collection of Users, the collection of Subscriptions, the Drive, the current user (Me), and all the other properties are of type I*RequestBuilder, where the asterisk corresponds to the underling type name. For example, the Me property will return the current user resource object. Thus, it will be of type IUserRequestBuilder.

 

In the set of generated request builder types, there will be the UserRequestBuilder class, which implements the IUserRequestBuilder interface and internally prepares all the REST requests for a single user resource by leveraging the UserRequest type.

 

For the sake of completeness, if you open the source code of the user request type in the Microsoft Graph SDK, either by browsing it on GitHub (https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/master/class='lazy' data-src/Microsoft.Graph/Requests/Generated/UserRequest.cs) or by forking the GitHub repository locally on your development environment, you will see that internally it handles the HTTP REST requests, including the proper HTTP methods, all the HTTP headers, and the query string parameters.

 

You saw how to select one or more properties of a user by querying the endpoint for the collection of users and providing the $select query string parameter. For example, a REST query to get the DisplayName, UserPrincipalName, and Mail address of all the users can be done by making a GET request for the following URL: https://graph.microsoft.com/v1.0/users? $select=displayName,userPrincipalName,mail

 

Here, you can see the corresponding syntax if you want to make the same request by using the Microsoft Graph SDK:

var users = await

graphClient.Users.Request().Select("DisplayName,UserPrincipalName,Mail").GetAsy

As you can see, the request model is straightforward and clearly maps to the corresponding low-level REST approach.

 

For the request model, it is important to underline that to execute the HTTP request effectively against the target, you have to invoke the GetAsync method, which is an asynchronous method, as the name implies. In fact, the Graph SDK is completely asynchronous and ready for the asynchronous development model of .NET.

 

Querying resources

The situation becomes more challenging if you also want to filter, sort, or partition data. As you learned in the previous blogs, the Graph REST API is compliant with the OData v4 protocol specification. Thus, it supports query string parameters like $filter$orderBy$skip$top, and $expand. The Microsoft Graph SDK supports the same statements. In this section, you will see each of them in action.

 

Basic query operations

Let’s start with the capability to filter resources. The SDK provides the Filter method for every *RequestBuilder type. In Listing, you can see a sample code excerpt to query the list of users, filtered by the work department.

 

LISTING  Code excerpt to query the users filtered by Department property value

var filteredUsers = await graphClient.Users.Request() .Select("DisplayName,UserPrincipalName,Mail") .Filter("department eq 'IT'") .GetAsync();

 

Notice that you can create a chain of methods so that, for example, in Listing you apply the Filter method on top of the results of the Select method. The corresponding REST request will be something like the following:

https://graph.microsoft.com/v1.0/users?

$select=displayName,userPrincipalName,mail,department&$fi

lter=department%20eq%20'IT'

 

As you can see, the string argument of the Filter method is what you can provide to the REST endpoint directly, without the URL encoding. Thus, using the new Microsoft Graph SDK you are free to make whatever query you like, but you will have to know the OData querying syntax because there are no high-level tools for building queries.

 

Another useful method you can use is OrderBy, which sorts the results of a query based on one or more property values. However, at the time of this writing you cannot combine the OrderBy method with the Filter method. This is highlighted in the REST API online documentation (see the “$orderby” section here: http://graph.microsoft.io/en-us/docs/overview/query_parameters).

 

Moreover, depending on the target resource type, you could have to provide the properties’ names with the same case that is used in the $metada document. For example, if you want to sort the users by DisplayName, you cannot use the DisplayName clause—you will have to use the displayName clause, with a lowercase trailing letter, according to its definition in the $metadata document.

 

This is a requirement of the REST API, depending on the target type of resources, and not of the SDK because internally the SDK will build the REST request for the API, and the API refuses the query that has the wrong casing of properties, providing an exception message like the following:

 

Given an expression for $orderby Microsoft.Data.OData.Query.semantics. SingleValueOpenPropertyAccessNode is not supported In Listing, you can see a query with sorting based on DisplayName ascending.

 

LISTING Code excerpt to query the users sorted by DisplayName ascending

var filteredUsers = await graphClient.Users.Request()

.Select("DisplayName,UserPrincipalName,Mail")

.OrderBy("displayName")

.GetAsync();

 

If the target collection supports the capability, you can sort the resources in ascending (default) or descending order by providing the desc or asc keywords after the name of the sorting fields. You can even combine multiple fields to achieve multilevel hierarchical sorting.

 

Furthermore, you can partition the results of queries by leveraging the Top and Skip methods. These are the counterparts of the $top and $skip query string parameters of the OData protocol. In Listing, you can see a query that retrieves the top 5 email messages in the inbox folder of the current user’s mailbox, skipping the first 10 messages.

 

LISTING Code excerpt to query the top 5 messages in inbox, skipping the first 10 messages

var partitionedMails = await http://graphClient.Me.MailFolders.Inbox.Messages.Request()
.Select("subject,from,receivedDateTime")
.OrderBy("receivedDateTime desc")
.Skip(10)
.Top(5)
.GetAsync();

 

Again, depending on the target type of resource, some of these methods could be unsupported. For example, at the time of this writing, if you invoke the Skip method against the collection of users, you will get back an exception like the following:

'$skip' is not supported by the service.

 

The last method related to the querying capabilities of the Microsoft Graph SDK is the Expand method, which allows you to include in the results resources related to the main resource that is subject to query. For example, in Listing you can see a code excerpt to retrieve a folder in a drive, together with (Expand) the children files and folders of that folder, within a unique request.

 

LISTING Code excerpt to retrieve a folder and its children items

var expandedFiles = await http://graphClient.Me.Drive.Root.Request()
.Expand("children($select=id,name,createdBy,lastModifiedBy)")
.Select("id,name,webUrl")
.GetAsync();

 

It is important to highlight that the Select method chained to the Expand method targets the root folder of the current user’s OneDrive for Business drive and not the children items. To select a subset of properties for the children resources of the folder, you have to provide the list of properties through the input argument of the Expand method. Notice also that by design of the Graph REST API—independent from the Graph SDK—the Expand method can expand a maximum of 20 objects per request. If you are targeting a user resource, you can expand only one child resource or collection of resources for each request.

 

Handling paging of collections

In the previous paragraphs, you learned how to query collections of resources and how to filter, partition, and expand resulting data. However, in real business scenarios you could have large numbers of items. Thus, it is fundamental to also do paging of collections.

 

The Microsoft Graph API makes it possible to do paging by splitting the results in pages and providing the @odata.nextLink property in the result set. Thus, whenever you query the Graph API collections of resources, you can consume the query result and then make another query against the URL provided in the @odata.nextLink property of the result set. Considering a query for a set of users, the @odata.nextLink property value will be something like the following:

@odata.nextLink=https://graph.microsoft.com/v1.0/users?

$skiptoken=X%2744537074020000203A666E313834B900000000000000000000%27

 

The $skiptoken query string argument instructs the REST service to provide the next page of the current page. Internally, the $skiptoken query string is an opaque token that identifies a starting point in the collection of resources identified by the current REST query. Basically, it defines the first record of the next page to query.

 

If the query orders the resulting resources by using the $orderby query string argument, the results will be ordered according to the sorting rule. Otherwise—and by default—the results will be ordered by entity key (the ID).

 

When you query collections of resources by using the Microsoft Graph SDK, all the internals of paging and of the $skiptoken argument are hidden from you under the cover of the ICollectionPage<T> interface and a set of collection interfaces autogenerated by VIPR. For example, in Listing you can see the definition of the ICollectionPage<T> interface.

 

LISTING  Definition of the ICollectionPage<T> interface of the Microsoft Graph SDK

<summary>
Interface for collection pages.
</summary>
<typeparam name="T">The type of the collection.</typeparam> public interface ICollectionPage<T> : IList<T> {
<summary>
The current page of the collection.
</summary>
IList<T> CurrentPage { get; }
IDictionary<string, object> AdditionalData { get; set; }
}

 

The interface definition is straightforward, and whenever you query a pageable collection you will get back a CurrentPage property and any additional data in the AdditionalData dictionary property, which will hold the values for the properties @odata.context and @odata.nextLink. For example, consider a collection of users. The result of the following query is an object of type GraphServiceUsersCollectionPage that implements the IGraphServiceUsersCollectionPage interface:

 

var pagedUsers = await

graphClient.Users.Request().Select("id,DisplayName,Mail").GetAsync();

In Listing, you can see the definition of the IGraphServiceUsersCollectionPage interface, which implements the ICollectionPage<T> interface.

 

LISTING Definition of the IGraphServiceUsersCollectionPage interface of the Microsoft Graph SDK

public interface IGraphServiceUsersCollectionPage : ICollectionPage<User> {
<summary>
Gets the next page <see cref="IGraphServiceUsersCollectionRequest"/> instance.
</summary>
IGraphServiceUsersCollectionRequest NextPageRequest { get; }
<summary>
Initializes the NextPageRequest property.
</summary>
void InitializeNextPageRequest(IBaseClient client, string
nextPageLinkString);
}

Aside from the implemented interface, there is a property called NextPageRequest of type IGraphServiceUsersCollectionRequest that is an autogenerated interface, and that provides the shortcut to access the next page of the current page. When you invoke the GetAsync method to retrieve a result set of resources, internally that method will initialize the NextPageRequest property so that it will represent a request for the next page of resources.

 

If you invoke the GetAsync method against the NextPageRequest property, you will get back the next page. By iterating through the collections of resources and invoking the GetAsync method of every NextPageRequest, as long as that property is not NULL you will get back all the resources page by page.

 

Thus, in Listing you can see a full example of how you can browse all the pages of users by leveraging these interfaces and properties.

 

LISTING Code sample of how to do paging of users by using the Microsoft Graph SDK

var pagedUsers = await graphClient.Users
.Request()
.Select("id,DisplayName,Mail")
.GetAsync();
Int32 pageCount = 0;
while (true) {
pageCount++;
Console.WriteLine("Page: {0}", pageCount);
foreach (var user in pagedUsers) {
Console.WriteLine("{0} - {1} - {2}", http://user.Id, user.DisplayName, user.Mail);
}
if (pagedUsers.NextPageRequest != null) {
pagedUsers = await pagedUsers.NextPageRequest.GetAsync();
}
else {
break;
}
}

You can implement the paging logic however you like; the key point is to request pages of resources as long as the NextPageRequest property is not NULL.

 

Managing resources

In the previous blogs, you saw that you can add, update, and delete the resources provided by the Microsoft Graph API. In this section, you will learn how to accomplish these tasks by using the Microsoft Graph SDK.

 

Adding a resource to a collection

You can add resources to collections by invoking the AddAsync method that is offered by any collection, thanks to the definition of the autogenerated IGraphService*CollectionRequest interfaces. Again, the asterisk has to be replaced with the name of every resource type.

 

Imagine that you want to create a new Office 365 Group, which is a useful business case for a real solution. First of all, you have to target the collection of groups, which is available through the Groups collection property of the IGraphServiceClient interface. Then, you have to invoke the Request method to get a reference to the REST request for the target resource collection. After that, you will be able to invoke the AddAsync method of the resulting object. In Listing, you can see a code excerpt that creates a new Office 365 Group.

 

LISTING Code excerpt that creates a new Office 365 Group by using the Microsoft Graph SDK

String randomSuffix = Guid.NewGuid().ToString("N");
Prepare the group resource object Group newGroup = new Group {
DisplayName = "SDK Group " + randomSuffix, Description = "This has been created via Graph SDK", MailNickname = "sdk-" + randomSuffix,
MailEnabled = true, SecurityEnabled = false,
GroupTypes = new List<string> { "Unified" },
};
Add the group to the collection of groups
var addedGroup = await graphClient.Groups.Request().AddAsync(newGroup);

 

Notice that you have to configure the group completely in code. For example, to create an Office 365 Group, you have to specify the value for the GroupTypes property, selecting the Unified value.

 

Furthermore, when you create an Office 365 Group, usually you also would like to set up a photo for the group and to assign owners and members to the group. The group’s photo can be configured by providing a http://System.IO.Stream object to the PutAsync method of the Photo.Content.Request of the target group. In Listing you can see the corresponding code.

 

LISTING Code excerpt that uploads a photo for an Office 365 Group by using the Microsoft Graph SDK

// Upload a new photo for the Office 365 Group

using (FileStream fs = new FileStream(@"..\..\AppIcon.png", FileMode.Open, FileAccess.Read, FileShare.Read)) {

await

graphClient.Groups[http://addedGroup.Id].Photo.Content.Request().PutAsync(fs);

}

 

If you are going to add a photo to a new group, you probably will need to provide a retry logic because usually it will take a few seconds after the creation for the group to be ready and available for uploading the photo.

 

The owners and members are more interesting. If you think about the Graph API from a REST perspective, the collections of members and owners of a group are accessible through the $ref keyword, which represents a reference to the underlying collection objects. Because the Graph SDK domain model is autogenerated from the $metadata definition of resources, the $ref endpoints are also made available through an autogenerated References property. That property gives us a reference to the underlying collection of items.

 

For example, if you reference a group item by using the Groups property of an IGraphServiceClient implementation, you will see that the Owners property will be of type IGroupOwnersCollectionReferencesRequest, where the interface name implies that there are also references included in the interface definition and that you can invoke the Request method against them. In general, in the autogenerated domain model there are a bunch of interfaces with name I*CollectionReferencesRequest, where the asterisk refers to the collection name.

 

If you get a reference to the collections of owners or members of a group, you can then invoke the AddAsync method of those collections and add new user objects of type DirectoryObject to them. In Listing, you can see a code excerpt that adds one owner and a couple of members to a just-created Office 365 Group.

 

LISTING  Code excerpt adding an owner and members to an Office 365 Group by using the Microsoft Graph SDK

// Add owners to the group
var ownerQuery = await graphClient.Users
.Request()
.Filter("userPrincipalName eq 'paolo.pialorsi@sharepoint-camp.com'")
.GetAsync();
var owner = ownerQuery.FirstOrDefault();
if (owner != null) {
try {
await graphClient.Groups[http://addedGroup.Id].Owners
.References.Request().AddAsync(owner);
}
catch (ServiceException ex) {
Console.WriteLine(ex.Message);
}
}
// Add members to the group
var memberOneQuery = await graphClient.Users
.Request()
.Filter("userPrincipalName eq 'paolo.pialorsi@sharepoint-camp.com'")
.GetAsync();
var memberTwoQuery = await graphClient.Users
.Request()
.Filter("userPrincipalName eq 'john.white@sharepoint-camp.com'")
.GetAsync();
var memberOne = memberOneQuery.FirstOrDefault(); var memberTwo = memberTwoQuery.FirstOrDefault();
if (memberOne != null) {
try {
await graphClient.Groups[http://addedGroup.Id].Members .References.Request().AddAsync(memberOne);
}
catch (ServiceException ex) {
Console.WriteLine(ex.Message);
}
}
if (memberTwo != null) {
try {
await graphClient.Groups[http://addedGroup.Id].Members .References.Request().AddAsync(memberTwo);
}
catch (ServiceException ex) {
Console.WriteLine(ex.Message);
}
}

 

Consider that if you try to add the same user object to the same target role (member or owner) more than once, you will get back an exception with an error message like the following, which is provided in the case of a duplicate owner:

One or more added object references already exist for the following modified

properties:

'owners'.

 

Notice that if you access the Owners property of a Group instance retrieved using the GetAsync method, you will not have access to the References property. This is because the single instance of type Group does not expose the Owners as an implementation of IGroupOwnersCollectionReferencesRequest, but just as an implementation of the IGroupOwnersCollectionWithReferencesPage interface. This same is true for the Members property of a Group instance.

 

Last, consider that to add a new Office 365 Group, you will need to have the permission scope of type Group.ReadWrite.All, but if you also want to manage owners and members, you most likely will also need permissions to access the Azure AD tenant. For example, the permission scope Directory.AccessAsUser.All could be enough as long as you don’t want to act with elevated privileges—for example, using an app-only access token. Nevertheless, keep in mind that at the time of this writing, you cannot create an Office 365 Group with an app-only access token.

 

Updating a resource

Now that you have seen how to add resources to collections, it is time to learn how to update an existing resource. The Microsoft Graph SDK provides the UpdateAsync method for this purpose.

 

The UpdateAsync method is available through the I*Request interface for objects that are updatable. For example, in Listing you can see a code excerpt to update the DisplayName and Description properties of an Office 365 group.

 

LISTING Code excerpt to update an Office 365 Group by using the Microsoft Graph SDK

var groupToUpdate = await graphClient.Groups[groupId]
.Request()
.GetAsync();
groupToUpdate.DisplayName = "SDK Group - Updated!"; groupToUpdate.Description += " - Updated!";
var updatedGroup = await graphClient.Groups[groupId]
.Request()
.UpdateAsync(groupToUpdate);

 

When you update a resource, particularly an Office 365 Group, you have to consider that there could be some delay due to data caching on the service side and because of asynchronous update operations that are working in the background. Thus, there is no guarantee that you will see your updates in real time.

 

Deleting a resource

Deleting a resource is another straightforward use case. You just need to get a reference to the resource object you want to delete and then invoke the DeleteAsync method on it. In Listing, you can see a code excerpt to delete an existing Office 365 Group.

 

LISTING Code excerpt to delete an Office 365 Group by using the Microsoft Graph SDK

await graphClient.Groups[groupId]

.Request()

 

.DeleteAsync();

As you can see, the DeleteAsync method is applied directly to the target resource. In case of success, it does not provide any kind of result. In case of failure, you will have to catch the proper exception type.

 

Handling exceptions and concurrency

Whenever you interact with a service layer, various kinds of exceptions can happen. The Microsoft Graph SDK will wrap all of the possible exceptions into the ServiceException type of the Microsoft.Graph namespace.

 

LISTING The definition of the ServiceException type of the Microsoft Graph SDK

public class ServiceException : Exception {
public ServiceException(Error error, Exception innerException = null)
base(null, innerException) { this.Error = error;
}
public Error Error { get; private set; }
public bool IsMatch(string errorCode) {
if (string.IsNullOrEmpty(errorCode)) {
throw new ArgumentException("errorCode cannot be null or empty", "errorCode");
}
var currentError = this.Error;
while (currentError != null) {
if (string.Equals(currentError.Code, errorCode, StringComparison.OrdinalIgnoreCase)) {
return true;
}
currentError = currentError.InnerError;
}
return false;
}
public override string ToString() {
if (this.Error != null) {
return this.Error.ToString();
}
return null;
}
}

 

The main information provided by the ServiceException is the Error property, which provides detailed information about the service-side exception. The main information that you can find within the Error property, which is of type Microsoft.Graph.Error.

 

Moreover, the IsMatch method of the ServiceException type enables you to double-check the type of the error, based on the error code. You can invoke the IsMatch method by providing a reference error code, and the method will give you back a Boolean value that enables you to verify if the exception corresponds to the provided error code. In Listing, you can see a sample of how to handle the ServiceException type.

 

LISTING Sample of how to handle the ServiceException type with the Microsoft Graph SDK

try {
// Do something with the Microsoft Graph SDK ...
}
catch (ServiceException ex) {
Console.WriteLine(ex.Error);
if (ex.IsMatch(GraphErrorCode.AccessDenied.ToString())) { Console.WriteLine("Access Denied! Fix permission scopes ...");
}
else if (ex.IsMatch(GraphErrorCode.ThrottledRequest.ToString())) { Console.WriteLine("Please retry ...");
}
}

 

As you can see, the exception handling is clean and clear. It is also interesting to see that there is a specific error code for throttled requests, which is useful when you are consuming a cloud service like the Microsoft Graph API and you need to implement a retry logic.

Real-life examples

It is now time to play a little bit with the Microsoft Graph SDK, looking at how to accomplish some of the most common and useful tasks in real business scenarios.

 

Sending an email

Sending an email on behalf of the current user is one of the most common use cases. There is an operation called microsoft.graph.sendMail available under the current user resource, which is called Me.

 

Using the Microsoft Graph SDK, you can invoke the SendMail method, which is available for the Me property of the GraphServiceClient object. In Listing, you can see a code excerpt that uses the SendMail method.

 

LISTING Sample of how to send an email by using the Microsoft Graph SDK

try {
await http://graphClient.Me.SendMail(new Message { Subject = "Sent from Graph SDK",
Body = new ItemBody {
Content = "<h1>Hello from Graph SDK!</h1>", ContentType = BodyType.Html,
},
ToRecipients = new Recipient[] {
new Recipient {
EmailAddress = new EmailAddress {
Address = recipientMail,
Name = recipientName,
}
}
},
Importance = Importance.High,
},
true).Request().PostAsync();
}
catch (ServiceException ex) {
Console.WriteLine(ex.Error);
}

Notice that the SendMail method does not send the email message directly, but it returns a request builder of type IUserSendMailRequestBuilder. Thus, you have to invoke the Request method to retrieve the request object, and after that you have to POST over HTTP the request invoking the PostAsync method.

 

The main input of the SendMail method is an object of type Microsoft.Graph.Message, which is a domain model object autogenerated by VIPR and corresponding to the resource defined in the $metadata document.

 

Searching for Office 365 Groups

Another interesting and common use case is retrieving all the Office 365 Groups. We already saw that the GraphServiceClient object offers a Groups collection, but that collection returns all the available flavors of groups including security groups, Unified Groups (Office 365 Groups), and dynamic groups.

 

To select only the Office 365 Groups, you can use the Filter method applied to the Request for the Groups collection, searching for those groups that have the GroupType property containing a value of Unified. In Listing, you can see a code excerpt to accomplish this task.

 

LISTING Code excerpt to retrieve all the Office 365 Groups page by page by using the Microsoft Graph SDK

var pagedGroups = await graphClient.Groups
.Request()
.Filter("groupTypes/any(grp: grp eq 'Unified')")
.GetAsync();
Int32 pageCount = 0;
while (true) {
pageCount++;
Console.WriteLine("Page: {0}", pageCount);
foreach (var group in pagedGroups) {
Console.WriteLine("{0} - {1} - {2}", Okusi Infotech,
group.DisplayName, group.Description);
}
if (pagedGroups.NextPageRequest != null) {
pagedGroups = await pagedGroups.NextPageRequest.GetAsync();
}
else {
break;
}
}

The key point of the sample in Listing is the argument provided to the Filter method.

 

Handling content of Office 365 Groups

Once you have a reference to an Office 365 Group resource, most likely you will have to retrieve information and content about the group. For example, if you want to retrieve the files of a group, you can access the Drive property of the target group. From the group’s drive, you will have access to the root folder and to its children items. Listing illustrates how to retrieve the files and folders from the root folder of a group’s drive.

 

LISTING Code excerpt to retrieve all the files in the root folder of an Office 365 Group

var groupDriveItems = await graphClient

.Groups[unifiedGroupId].Drive.Root.Children

.Request()

.GetAsync();

 

As usual, you have to build the request and then execute it asynchronously. If you are looking for a specific file by name or content, you can use the search capabilities of OneDrive for Business and SharePoint. In Listing you can see how to search for a file.

 

LISTING Code excerpt to search for files in the root folder of an Office 365 Group

var groupDriveItems = await graphClient

.Groups[unifiedGroupId].Drive.Root.Search("query text")

.Request()

.GetAsync();

 

Another common need is the retrieval of conversations from the current group. To do this, you need to make a request for the Conversations collection resource. Listing illustrates the corresponding sample.

 

LISTING Code excerpt to retrieve all the conversations of an Office 365 Group

var groupDriveItems = await graphClient

.Groups[unifiedGroupId].Conversations

.Request()

.GetAsync();

One last common use case is the retrieval of one or more events from the group’s calendar. There is an Events collection property for the group, and you can make a request for it, like Listing does.

 

LISTING Code excerpt to retrieve all the events of an Office 365 Group

var groupEvents = await graphClient

.Groups[unifiedGroupId].Events

.Request()

.GetAsync();

 

All of these collections are also available for management of their resources. For example, if you want to add a new topic to a group conversation, you can use a syntax like the one illustrated in Listing.

 

LISTING Code excerpt to add a new conversation thread in an Office 365 Group

var posts = new ConversationThreadPostsCollectionPage(); posts.Add(new Post { Body = new ItemBody {
Content = "Welcome to this group!",
ContentType = BodyType.Text,
} });
var ct = new ConversationThread {
Topic = "The Microsoft Graph SDK!",
Posts = posts
};
var unifiedGroups = await graphClient.Groups
.Request()
.Filter("groupTypes/any(grp: grp eq 'Unified')")
.GetAsync();
var groupEvents = await graphClient
.Groups[unifiedGroups.FirstOrDefault().Id].Threads
.Request()
.AddAsync(ct);

 

As you can see from reading the code samples, you need to create an instance of type ConversationThread, providing the mandatory Topic property and one or more instances of Post resources. In Listing, you can see how to add a new event in the group’s calendar.

 

LISTING Code excerpt to add a new event in the calendar of an Office 365 Group

Event evt = new Event {
Subject = "Created with Graph SDK",
Body = new ItemBody {
Content = "<h1>Office 365 Party!</h1>",
ContentType = BodyType.Html,
},
Start = new DateTimeTimeZone {
DateTime = DateTime.Now.AddDays(1).ToUniversalTime()
.ToString("yyyy-MM-ddThh:mm:ss"),
TimeZone = "UTC",
},
End = new DateTimeTimeZone {
DateTime = DateTime.Now.AddDays(2).ToUniversalTime()
.ToString("yyyy-MM-ddThh:mm:ss"),
TimeZone = "UTC",
},
Location = new Location {
Address = new PhysicalAddress {
City = "Redmond",
CountryOrRegion = "USA",
State = "WA",
Street = "Microsft Way",
PostalCode = "98052",
},
DisplayName = "Microsoft Corp. HQ",
},
Type = EventType.SingleInstance,
ShowAs = FreeBusyStatus.Busy,
};
var groupEvents = await graphClient
.Groups[unifiedGroups.FirstOrDefault().Id].Events
.Request()

 

.AddAsync(evt);

The syntax is pretty self-explanatory. First, you have to create an instance of the Microsoft.Graph.Event type. Then, you have to pass it to the AddAsync method of the collection of Events for the current group. Note that the Start and End properties of the Event type have to be provided in a specific date and time format. Thus, the code sample makes a custom ToString call to reproduce the required behavior.

 

Managing current user’s photo

Managing the current user’s photo is another frequently used capability, and it is similar to handling the photo of an Office 365 Group. In Listing, you can see an excerpt of a code sample that retrieves the current user’s photo.

 

LISTING Code excerpt to retrieve the current user’s photo by using the Microsoft Graph SDK

// Get the photo of the current user
var userPhotoStream = await http://graphClient.Me.Photo.Content.Request().GetAsync();
using (FileStream fs = new FileStream(@"..\..\user-photo-original.png", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)) {
userPhotoStream.CopyTo(fs);
}

 

As you can see, any user resource—including the current user (Me)—has a Photo property that provides a Content property. You just need to invoke the GetAsync method against the Request method of the Content. The result of the GetAsync method is an object of type System.IO.Stream that you can consume freely. The code sample in Listing saves the image on the local file system. Updating the current user’s photo is even more interesting.

 

LISTING Code excerpt to update the current user’s photo by using the Microsoft Graph SDK

// Upload a new photo for the current user
using (FileStream fs = new FileStream(@"..\..\user-photo-two.png", FileMode.Open,
FileAccess.Read, FileShare.Read)) {
try {
await http://graphClient.Me.Photo.Content.Request().PutAsync(fs);
}
catch (ServiceException ex) {
Console.WriteLine(ex.Error);
if (ex.IsMatch(GraphErrorCode.AccessDenied.ToString()))
Console.WriteLine("Access Denied! Fix permission scopes ...")
}
else if (ex.IsMatch(GraphErrorCode.ThrottledRequest.ToString())) { Console.WriteLine("Please retry ...");
}
}
}

 

The sample is straightforward. You have to get a reference to the Content property of the user’s Photo, and after that you have to invoke the PutAsync method, providing an argument of type http://System.IO.Stream that represents the content of the image to upload. Of course, you will need to have proper permissions for the client application to be able to update the user’s picture.

 

Managing current user’s manager and direct reports

In real business solutions, you often have to provide organization charts or escalate tasks from direct reports to managers. Thus, being able to read who are the direct reports of a user or who is the manager of a user is really useful—as is managing users’ hierarchical relationships in general.

 

The Microsoft Graph provides a couple of operations for getting the manager or the direct reports of a user. In Listing, you can see how to retrieve the current user’s manager, which is a use case that can be applied to any user resource, not only to the current user.

 

LISTING Code excerpt to retrieve the current user’s manager by using the Microsoft Graph SDK

var managerPointer = await http://graphClient.Me.Manager.Request().GetAsync();
var manager = await graphClient.Users[http://managerPointer.Id].Request()
.Select("DisplayName").GetAsync();
if (manager != null) {
Console.WriteLine("Your manager is: {0}", manager.DisplayName);
}

Notice that the Manager property of the user resource gives you back only the ID of the target manager, not the whole directory object. Thus, if you want to retrieve any property of the manager, you will have to make one more request, targeting the collection of Users of the GraphServiceClient object and providing the ID as the key to access the proper user resource.

 

Moreover, the Manager property of the user object provides a Reference property, which can be retrieved through the Request method. The Manager.Reference resource provides a DeleteAsync method that can be used to unlink the current user from his manager.

 

Furthermore, the direct reports can be retrieved by querying the DirectReports collection property of the target user resource. Because there can be many direct reports, the collection supports paging. In Listing, you can see a sample of how to retrieve a list of direct reports for the current user, without paging.

 

LISTING Code excerpt to retrieve the current user’s direct reports by using the Microsoft Graph SDK

var reports = await http://graphClient.Me.DirectReports.Request().GetAsync();
if (reports.Count > 0) {
Console.WriteLine("Here are your direct reports:"); foreach (var r in reports) {
var report = await graphClient.Users[http://r.Id].Request()
.Select("DisplayName").GetAsync();
Console.WriteLine(report.DisplayName);
}
}
else {
Console.WriteLine("You don't have direct reports!");
}

 

The sample is straightforward and just browses the direct report resources. Like the manager resource, the direct reports provide only the ID of the target directory object. Thus, you will have to query explicitly for every direct report resource that you want to use, if any.

 

To add a direct report to a user, you can invoke the AddAsync method against the Request provided by the References property of the DirectReports user’s property. Based on what you just saw, there are methods to add direct reports to a user and to delete the manager reference from a user. However, there are no direct methods to add or change the manager of a user or to delete the direct reports from a user. Nevertheless, you can achieve all of the possible results by leveraging the available methods in the right sequence.

 

If you want to add or change a manager of a user, you can just add that user as a direct report for her manager. If you want to remove a direct report of a user, you can delete his manager’s reference. Last, if you want to change the direct reports of a user, you can assign them as direct reports of a new manager or delete their manager’s reference.

 

Uploading a file to OneDrive for Business

A common use case in business solutions is to upload files onto OneDrive for Business. In Listing, you can see how to create a new file in the root folder of the current user’s OneDrive for Business.

 

LISTING Code excerpt to create a new file in current user’s OneDrive for Business root folder by using the Microsoft Graph SDK

var newFile = new Microsoft.Graph.DriveItem { File = new Microsoft.Graph.File(),
Name = "filename.ext",
};
newFile = await http://graphClient.Me.Drive.Root.Children.Request().AddAsync(newFile);

 

Notice that the file has to be provided as a DriveItem instance and that at a minimum you have to configure the File property and the Name property. Creating a folder or a specific file type like a picture or a video will use the same approach, setting the proper properties on the DriveItem instance.

 

Once you have created the DriveItem instance, you can add the item to the collection of children of the target folder, which is the root folder in Listing. So far, you have only created a new item in the target folder. Now you need to upload the real content of the file, which is also useful if you need to update the content of an existing file. Every drive item resource provides the Content property, which can be used to update the file content by requesting the content resource and invoking the PutAsync method.

 

The PutAsync method accepts an argument of type http://System.IO.Stream, which represents the binary content of the file. In Listing, you can see how to upload or update the content of a target file.

 

LISTING Code excerpt to upload or update the content of a file in OneDrive for Business by using the Microsoft Graph SDK

using (FileStream fs = new FileStream(@"..\..\user-photo-two.png", FileMode.Open,
FileAccess.Read, FileShare.Read)) {
var newFileContent = await http://graphClient.Me.Drive.Items[http://newFile.Id].Content
.Request().PutAsync<DriveItem>(fs);
}

Notice that the result of the PutAsync method will be the just-uploaded or updated DriveItem.

 

Searching for files in OneDrive for Business

Today, every user has a huge number of files and folders in her OneDrive for Business. Thus, being able to search for something is fundamental.

The Microsoft Graph SDK enables you to leverage the search capabilities of OneDrive for Business by invoking a Search method, which is available for every DriveItem resource, where the resource should be a folder. You saw this method when searching for files in an Office 365 Group. In Listing, you can evaluate a code sample to make a free text search against the root folder of the current user’s OneDrive for Business.

 

LISTING Sample to search files in OneDrive for Business by using the Microsoft Graph SDK

var searchResults = await http://graphClient.Me.Drive.Root
.Search(queryText).Request().GetAsync();
Int32 pageCount = 0;
while (true) {
pageCount++;
Console.WriteLine("Page: {0}", pageCount);
foreach (var result in searchResults) {
Console.WriteLine("{0} - {1}\n{2}\n", result.id -&nbspresult Resources and Information., http://result.Name, result.WebUrl);
}
if (searchResults.NextPageRequest != null) {
searchResults = await searchResults.NextPageRequest.GetAsync()
}
else {
break;
}
}

Because the number of results is unpredictable and could be very large, the code sample leverages the paging capabilities of the Microsoft Graph SDK to browse all the results.

 

Moreover, consider that the search query results are provided through an instance type that implements the IDriveItemSearchCollectionPage interface. Thus, every result will be an object of type Microsoft.Graph.DriveItem, and you can apply querying methods like SelectFilter, OrderBy, and so on to the result so that you can partition, order, and project the results based on your needs.

 

Downloading a file from OneDrive for Business

The last use case that we will consider in this blog is the download of a file. If you search for one or more files, you most likely will want to access the content of those files. The Microsoft Graph SDK enables you to consume (that is, download) the content of a file with an easy technique. You just need to make a request for the Content property of an object of type DriveItem. In Listing, you can see how to achieve the result.

 

LISTING  Sample to download the content of a file stored in OneDrive for Business by using the Microsoft Graph SDK

var file = await http://graphClient.Me.Drive.Items[driveItemId]
.Request().Select("id,Name").GetAsync();
var fileContent = await http://graphClient.Me.Drive.Items[driveItemId]
.Content.Request().GetAsync();
using (FileStream fs = new FileStream(@"..\..\" + http://file.Name, FileMode.CreateNew, FileAccess.Write, FileShare.None)) {
fileContent.CopyTo(fs);
}

 

Because by requesting the Content property you will get back an object of type http://System.IO.Stream, if you also want to retrieve metadata information like the file name or its content type you will have to make a separate request selecting what you need against the target resource. In Listing, we just download the file and save it onto the file system, retrieving the name of the file from the Graph API.

 

Summary

In this section, you learned about the new Microsoft Graph SDK for .NET, which is an object model Microsoft provides to make it easy to consume the Microsoft Graph API without having to dig into the HTTP and REST protocol details.

 

Throughout this section, you examined the architecture of the SDK and saw how to authenticate by using either ADAL for Azure AD or MSAL for the new v2 authentication endpoint. Moreover, you learned about the request model and the querying model of the Graph SDK. You saw how to do paging of collections, how to manage items of collections, and how to do exceptions handling properly.

 

Last, you had a preview of some of the most common use cases, which can be useful whenever you need to create real business-level solutions using the Microsoft Graph API and the Microsoft Graph SDK for .NET.

 

SharePoint REST API

Since Microsoft SharePoint 2013, SharePoint has included a rich set of REST (Representational State Transfer) API, which is useful for creating SharePoint Add-ins, SharePoint workflows, and other software solutions. The SharePoint REST API gives any platform access to many key objects, properties, and methods that are also available via the client-side object model (CSOM).

 

The new API provides a rich set of REST URIs that you can access via HTTP and XML/JSON (JavaScript Object Notation) for consuming nearly every capability of the CSOM. All you need is a third-party technology capable of consuming REST services. In this blog, you will learn about the architecture of this REST API and how to manage the most common tasks for everyday programming needs.

 

Introducing the REST API

The overall architecture of the REST API is based on the client.svc Windows Communication Foundation (WCF) service, which serves the classic CSOM and also implements an OData-compliant endpoint.

 

More Information

stands for Open Data Protocol, and you can read more about it at http://www.odata.org/. You can access the REST API at the relative URL _api/ of any SharePoint site. For example, to access the API targeting the root site collection of a target web application, you can open your browser and navigate to a URL such as the following: https://<your-tenant>.http://sharepoint.com/_api/site

 

where <your-tenant>.Http://sharepoint.com/_api/site is the host name of a sample web application hosted in Microsoft Office 365. However, the SharePoint REST API is also available on-premises. The previous URL is just an alias of the real URL of the WCF service under the cover of the REST API, which is: https://<your-tenant>.Http://sharepoint.com/_api/site

 

This is just an additional RESTful endpoint that publishes the capabilities of the classic CSOM through the OData protocol. By browsing to such a URL, you will see that the result is an XML representation—based on the ATOM protocol—of information about the current site collection. (When using Internet Explorer, be sure to disable the feed-reading view in the browser’s content properties.)

 

At the beginning of the ATOM response, there is a list of links targeting many additional URLs for accessing information and API related to the current site collection. At the end of the response, there are some properties specific to the current site collection.

 

Here are some other commonly used URLs of API, which are useful while developing on SharePoint:

https://<your-tenant>.Http://sharepoint.com/_api/site Use to access the information about the target website.
https://<your-tenant>.Http://sharepoint.com/_api/site Use to access the collection of lists in the target website.
https://<your-tenant>.Http://sharepoint.com/_api/site of the List’) Use to access the information of a specific list instance, selected by title.
https://<your-tenant>.Http://sharepoint.com/_api/site Use to access the search query engine.

 

As you can see, the root of any relative endpoint is the _api/ trailer, which can be followed by many API targets (as the following section will illustrate) and corresponds to the most common artifacts of SharePoint. As with many REST services, you can communicate with this REST API not only by using the browser, invoking URLs with the HTTP GET method, but also by using a client capable of communicating over HTTP and parsing ATOM or JSON responses.

 

Depending on the HTTP Accept header provided within the request, the REST service will provide ATOM (Accept: application/atom+xml) or JSON (Accept: application/json;odata=verbose) answers. By default, REST service responses are presented by using the ATOM protocol according to the OData specification.

 

Depending on the HTTP method and headers (X-Http-Method) you use, you can take advantage of various capabilities of the API, such as a complete CRUDQ (create, read, update, delete, and query) set of methods. The available HTTP methods and headers are as follows:

 

GET These requests typically represent read operations that apply to objects, properties, or methods and return information.

 

POST Without any additional X-Http-Method header, this method is used for creation operations. For example, you can use POST to post a file to a library, post an item to a list, or post a new list definition for creation in a target website. While invoking POST operations against a target object, any property that is not required and is not specified in the HTTP invocation will be set to its default value. If you provide a value for a read-only property, you will get an exception.

 

PUT, PATCH, and MERGE These requests are used for update operations. You can use PUT to update an object. While invoking PUT operations, you should specify all writable properties. If any property is missing, the operation could fail or could set the missing properties back to their default values.

 

The PATCH and MERGE operations are based on the POST method, with the addition of an X-Http-Method header with a value of PATCH or MERGE. They are equivalent, and you should always use the former because the latter is provided for backward compatibility only. Like PUT, PATCH and MERGE handle update operations. The big difference is that with PATCH and MERGE, any writeable property that is not specified will retain its current value.

 

DELETE These requests are for deleting an item and can be implemented with POST plus the additional X-Http-Method header with a value of DELETE. If you invoke this operation against recyclable objects, SharePoint will move them to the Recycle Bin.

 

Listing demonstrates how to use the new REST API from within PowerShell. The sample is intentionally written using a PowerShell script to demonstrate that the REST API is available to any platform and any technology landscape. The code reads the title of a list instance in a target website. Moreover, this sample leverages a useful and powerful set of PowerShell extensions for Microsoft SharePoint Online and Office 365, which are available through the Office 365 Developer Patterns & Practices (PnP) community project.

 

For further details about the Office 365 Developer Patterns & Practices community project, you can browse to the following URL: http://aka.ms/OfficeDevPnP. The PowerShell extensions, which have been created by Erwin van Hunen (https://twitter.com/erwinvanhunen), can be installed from the following URL: https://github.com/OfficeDev/PnP-PowerShell/tree/master/Binaries.

 

They are available in three flavors: SharePointPnPPowerShell2013.msi targets SharePoint 2013 on-premises, SharePointPnPPowerShell2016.msi targets SharePoint 2016 on-premises, and SharePointPnPPowerShellOnline.msi targets SharePoint Online.

 

LISTING A sample PowerShell script for reading the title of a list instance in a target website using the REST API

# Connect to SharePoint Online
$targetSite = "https://<your-tenant>.Http://sharepoint.com/_api/site<Site-Name>/" $targetSiteUri = [System.Uri]$targetSite Connect-SPOnline $targetSite
Retrieve the client credentials and the related Authentication Cookies $context = (Get-SPOWeb).Context
$credentials = $context.Credentials
$authenticationCookies = $credentials.GetAuthenticationCookie($targetSiteUri, $true)
Set the Authentication Cookies and the Accept HTTP Header
$webSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession $webSession.Cookies.SetCookies($targetSiteUri, $authenticationCookies) $webSession.Headers.Add("Accept", "application/json;odata=verbose")
# Set request variables
$targetLibrary = "Documents"
$apiUrl = "$targetSite" + "_api/web/lists/getByTitle('$targetLibrary')"
# Make the REST request
$webRequest = Invoke-WebRequest -Uri $apiUrl -Method Get -WebSession $webSession
# Consume the JSON result
$jsonLibrary = $webRequest.Content | ConvertFrom-Json Write-Host $jsonLibrary.d.Title

 

The sample code invokes a GET method through the Invoke-WebRequest cmdlet. However, before invoking the HTTP request, it logs into SharePoint Online using the custom Connect-SPOnline cmdlet and invokes the GetAuthenticationCookie method provided by the SharePointOnlineCredentials type, retrieving the authentication cookies required by SharePoint Online and Microsoft Azure Active Directory (Azure AD).

 

You can achieve the same goal with any other programming or scripting language capable of communicating over HTTP and managing ATOM or JSON contents. For testing purposes, you can also play with tools like Fiddler Composer (http://www.fiddler2.com) to test the behavior and the responses provided by the REST API.

 

API reference

Every method offered by the REST API can be invoked by using a reference URL. The protocol moniker can be http or https, depending on the web application configuration. If you target Microsoft SharePoint Online, it will be https. The {hostname} argument is the host name—which will include the fully qualified domain name—of the target web application. For Microsoft SharePoint Online, it will be <your-tenant>.Http://sharepoint.com/_api/site.

 

The subsequent {site} is the target site collection and is optional because you could target the root site collection. Following the _api trailer is a {namespace} argument that corresponds to one of the target families of API. Table lists some of the main available namespaces.

 

The URL ends with a reference to an {object}, a specific {property}, an {indexer}, or a {method} call. Indexers will be followed by a numeric {index} argument, while method calls could be followed by {parameter} arguments. For some operations, the arguments can be provided as a JSON object in the HTTP POST request body.

 

The REST API offers about 2,000 classes and more than 6,000 members, which are available throughout the hierarchy of objects of the CSOM using the preceding namespaces as root objects. The first three namespaces are easy to manage and understand because you just need to reference the corresponding CSOM types and compose the request URLs.

 

For example, the Site class of the Microsoft.SharePoint.Client namespace offers a property with name Owner and type User. By using the REST API, you can invoke the GET verb to retrieve the following URL: https://<your-tenant>.Http://sharepoint.com/_api/site

 

To invoke the GetWebTemplates method, which accepts the culture parameter, you can invoke the following URL:

https://<your-tenant>.Http://sharepoint.com/_api/site

 

The value 1033 provided is the en-US culture. Consult the CSOM online reference

(http://msdn.microsoft.com/en-us/library/ee544361.aspx) to see all the available properties, methods, and members.

 

Notice that for security reasons, all the operations that modify data will require a security form digest with a name of X-RequestDigest in the HTTP request headers. To retrieve the value needed for this header, you have a couple of options:

 

Working in JavaScript, inside a webpage directly hosted in SharePoint or a SharePoint-hosted add-in, you can retrieve the value of the digest from a hidden INPUT field with an ID value of __REQUESTDIGEST. For example, using jQuery, you can reference the field with the following syntax: $(“# __REQUESTDIGEST”).val().

 

Working in any other context, you can invoke (using the POST method) the ContextInfo namespace and retrieve the form digest value from the ATOM or JSON response. By default, the form digest retrieved through this method will expire in 1,800 seconds. Listing shows the JSON output of the ContextInfo method invocation. The form digest value is highlighted in bold.

 

LISTING The JSON output of the ContextInfo method invocation

{
"d": {
"GetContextWebInformation": {
"__metadata": {
"type":"SP.ContextWebInformation"
},
"FormDigestTimeoutSeconds":1800,
"FormDigestValue":"0x3C8E83432D855AC62850B198CDE3D4A3CF
A2D081864200B78ED4 D86A18687773,06 Jul 2015 21:19:58 -0000",
"LibraryVersion":"16.0.4208.1220",
"SiteFullUrl":"https://piasysdev.sharepoint.com/sites/ProgrammingOffice365",
"SupportedSchemaVersions": {
"__metadata": {
"type":"Collection(Edm.String)"
},
"results": [
"14.0.0.0",
"15.0.0.0"
]
},
"WebFullUrl":"https://piasysdev.sharepoint.com/sites/ProgrammingOffice365"
}
}
}

 

Listing provides a code excerpt of a PowerShell script that invokes the EnsureUser method of a target website, providing a value for the form digest HTTP header, which is called X-RequestDigest, after extracting that value from the ContextInfo method.

 

LISTING A PowerShell code excerpt for invoking the EnsureUser method of a target website via the REST API

$global:webSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
function Initialize-SPOSecuritySession {
param ($targetSite)
# Connect to SharePoint Online
$targetSiteUri = [System.Uri]$targetSite
Connect-SPOnline $targetSite
Retrieve the client credentials and the related Authentication Cookies $context = (Get-SPOWeb).Context
$credentials = $context.Credentials $authenticationCookies =
$credentials.GetAuthenticationCookie($targetSiteUri, $true)
Set the Authentication Cookies and the Accept HTTP Header $global:webSession.Cookies.SetCookies($targetSiteUri,
$authenticationCookies)
$global:webSession.Headers.Add("Accept", "application/json;odata=verbose")
}
function Initialize-SPODigestValue {
param ($targetSite)
$contextInfoUrl = $targetSite + "_api/ContextInfo"
$webRequest = Invoke-WebRequest -Uri $contextInfoUrl -Method Post -WebSession $global:webSession
$jsonContextInfo = $webRequest.Content | ConvertFrom-Json
$digestValue = $jsonContextInfo.d.GetContextWebInformation.FormDigestValue $global:webSession.Headers.Add("X-RequestDigest", $digestValue)
}
$targetSite = "https://<your-tenant>.Http://sharepoint.com/_api/site<Site-Name>/" Initialize-SPOSecuritySession -targetSite $targetSite Initialize-SPODigestValue -targetSite $targetSite
# Define the EnsureUser REST API call
$ensureUserUrl = $targetSite + "_api/web/EnsureUser('username@domain.tld')"
# Make the REST request
$webRequest = Invoke-WebRequest -Uri $ensureUserUrl -Method Post -WebSession $global:webSession
# Check the result
if ($webRequest.StatusCode -ne 200) {
Write-Host "Error:" $webRequest.StatusDescription
}
else {
Write-Host $webRequest.StatusDescription
}

 

So far, you have seen that all the REST requests were decorated with the Accept HTTP header and configured with a value of application/json;odata=verbose. This header instructs the OData engine to give back a JSON (application/json) response with a rich set of information (metadata) about the type of the result.

 

This capability could be useful if you query unknown data structures and want to retrieve not only the data but also the data type because you want to update the data back to SharePoint. However, often you consume data sets by themselves, in read-only mode and with a fixed set of fields. Having all the metadata information of what is coming back from the server in every response could be noisy and expensive.

 

That is why the international community defined the so-called JSON Light protocol and why Office 365 supports the JSON Light format since August 2014. JSON Light is an open standard that allows developers to provide in the HTTP headers of the REST requests how much metadata has to be returned. The supported values for the Accept HTTP header are:

  • application/json;odata=verbose
  • application/json;odata=minimalmetadata
  • application/json;odata=nometadata

 

The first option is the most common and well known, and it retrieves all the metadata available from the HTTP server. In the following excerpt, you can see a JSON object retrieved with the verbose option by using a customized OData query that retrieves the ID and the Title of a single document from a library with the EMail field of the document’s Author. The metadata information is highlighted in bold.

{"d":
{"__metadata":
{"id":"00af8a37-6fbd-454b-bd02-8fe1b74f8c41",
"uri":"https://<your-tenant>.Http://sharepoint.com/_api/site<Site-Name>/_api/Web/...", "etag":"\"2\"",
"type":"SP.Data.Sample_x0020_LibraryItem"}, "Author":
{"__metadata":
{"id":"8d8bbb4d-aa28-4a9b-be38-30cb8667fb2e",
"type":"SP.Data.UserInfoItem"},
"EMail":"<author>@<your-tenant>.http://onmicrosoft.com"},
"Id":1,
"Title":"Sample-File-03",
"ID":1}
}

 

The second option—which is now the default in Office 365 if you don’t specify the odata attribute in the Accept request header—instructs the engine to release minimal metadata information. In the following excerpt, you can see the JSON result of the same query as before, but with the minimalmetadata option.

{"odata.metadata":"https://<your-tenant>.Http://sharepoint.com/_api/site<Site- Name>/_api/$metadata#...",
"odata.type":"SP.Data.Sample_x0020_LibraryItem",
"http://odata.id":"50be68eb-be36-46b0-a3ef-6a8452a134cd",
"odata.etag":"\"2\"",
"odata.editLink":"Web/Lists(guid'70473747-fda3-4a81-a16a-
3231f2876aa7')/Items(1)",
"Author@odata.navigationLinkUrl":"Web/Lists(guid'70473747-fda3-4a81-a16a-
3231f2876aa7')/
Items(1)/Author",
"Author":
{"odata.type":"SP.Data.UserInfoItem", "http://odata.id":"4cdc633c-8f3a-41e5-87ec-8117256b409b", "EMail":"<author>@<your-tenant>.http://onmicrosoft.com"},
"Id":1,
"Title":"Sample-File-03",
"ID":1
}

 

The last option, nometadata, declares to skip any metadata information. It is the lightest solution if you just need the data and don’t want to focus on metadata. Here, you can see an excerpt of a JSON object, the same as before, retrieved with the nometadata option.

{"Author":

{"EMail":"<author>@<your-tenant>.http://onmicrosoft.com"},

"Id":1,

"Title":"Sample-File-03",

"ID":1}

 

In SharePoint on-premises, the JSON Light support is provided since SharePoint 2013 Service Pack 1.

 

Querying data

Another useful capability of the REST API is support for OData querying. Every time you invoke an operation that returns a collection of entities, you can also provide an OData-compliant set of query string parameters for sorting, filtering, paging, and projecting that collection. For example, imagine querying the list of items available in a document library. The URL would be:

https://<your-tenant>.Http://sharepoint.com/_api/site

 

If you are interested in the list of files in the root folder of the library, the corresponding

URL is:https://<your-tenant>.Http://sharepoint.com/_api/site

 

According to the OData specification, you can append the following querying parameters to the URL:

 

$filter Defines partitioning criteria on the current entity set. For example, you can provide the query string argument $filter=substringof(‘Budget’,Name)%20eq%20true to retrieve documents with Budget in their file name.

 

$select Projects only a subset of properties (fields) of the entities in the current entity set. For example, you can provide a value of $select=Name,Author to retrieve only the file name and the author of every file in the entity set.

 

$orderby Sorts data returned by the query. You can provide query string arguments with a syntax like $sort=TimeLastModified%20desc,Name%20asc to sort files descending by TimeLastModified and ascending by Name.

 

$top Selects the first N items of the current entity set. Use the syntax $top=5 to retrieve only the first five entities from the entity set.

$skip Skips the first N items of the current entity set. Use the syntax $skip=10 to skip the first 10 entities of the entity set.

 

$expand Automatically and implicitly resolves and expands a relationship between an entity in the current entity set and another related entity. For example, you can use the syntax $expand=Author to retrieve the author of a file.

 

The arguments provided to an OData query must be URL encoded because they are passed to the query engine via REST, through the URL of the service. Space characters must be converted into %20, for example, and any other non-alphanumeric characters must be converted into their corresponding encoded values.

 

In the previous examples, you saw a quick preview of the available functions and operators for filtering entities with OData. Table provides the full list of the available logical operations defined in the OData core specification. You can read the official core documentation of OData at http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part1-protocol.html. The operators in bold are supported by the SharePoint REST API.