Graph API Send Email Documentation

Graph API Send Email Documentation

Graph API Send Email

This blog explains how to work Microsoft Graph API with email, calendar, and contacts. The examples in this blog will be based on an ASP.NET MVC application, which can be, for example, a Microsoft Office 365 application.

 

Setting up the environment

To develop a .NET solution that leverages the Microsoft Graph API, you need to create a new project, which can be almost any kind of project. It can be a console application, an ASP.NET MVC or Web Forms application, a WPF desktop application, and so on.

 

Regardless of the kind of application you plan to develop, you will have to reference some .NET client libraries, and you will be able to play with REST and OData manually by using the HttpClient type of .NET.

 

The UI elements will mainly leverage the Office UI Fabric components, “Overview of Office 365 development,” “Creating Office 365 applications,” to provide a consistent UI and user experience (UX) to the end users of the application.

 

In the code samples related to this book, which are on GitHub (SharePoint/PnP), you can see all the implementation details.

 

“Azure Active Directory and security,” to consume the Microsoft Graph API you need to register your application within the Microsoft Azure Active Directory (Azure AD) tenant, and you need to configure the application permissions properly. For further details about how to register an application in Azure AD.

 

The easiest way to create a project like a demo that you will see in the following paragraphs is to create a new ASP.NET web application, choose the ASP.NET MVC 4.x template, and configure the web application authentication to use Work And School Accounts. This way, your application will already be registered in the Azure AD tenant of your choice.

 

You will also need to install the Active Directory Authentication Library (ADAL) for .NET, which is available as a NuGet package with the name “Microsoft.IdentityModel.Clients.ActiveDirectory.” At the time of this writing, the latest released version of ADAL is 3.x.

 

Once you have set up the project references and the NuGet packages, to consume any of the services available through the Microsoft Graph API you need to acquire an OAuth access token via ADAL, and to set up an HttpClient object that will consume the API by providing that specific OAuth access token within the HTTP headers of the request.

 

However, before you are able to acquire an access token through ADAL, you will need to customize the initialization code of the ASP.NET MVC project slightly. You will need to open the Startup.Auth.cs file, which is located under the App_Start folder of the ASP.NET 

 

MVC project, and add some logic to handle the OAuth 2.0 authorization flow. By default, configuring the application for Work And School Accounts authentication will set up an initial light version of that file, which looks like the code excerpt illustrated in Listing.

 

LISTING The out-of-box Startup.Auth.cs file in an ASP.NET project configured for Work And School Accounts authentication

public partial class Startup {
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static string authority = aadInstance + tenantId;
public void ConfigureAuth(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions {
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri });
}
}

 

These lines of code define the Open Web Interface for .NET (OWIN) pipeline and declare that the ASP.NET MVC web application will use cookie-based authentication, followed by OpenID Connect authentication. The latter is also configured with a specific Client ID, Authority, and post-logout redirect URL.

 

By default, all these custom configuration parameters are loaded from the web.config of the web application. In the code samples related to the current book part, these values are retrieved through a static class that shares all the general settings across the entire application.

 

In Listing, you can see the revised version of the Startup.Auth.cs file, which includes the OAuth access token handling logic.

 

LISTING  The customized Startup.Auth.cs file in the sample ASP.NET project

public partial class Startup {
public void ConfigureAuth(IAppBuilder app) {
app.SetDefaultSignInAsAuthenticationType(
CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions { ClientId = MSGraphAPIDemoSettings.ClientId, Authority = MSGraphAPIDemoSettings.Authority,
PostLogoutRedirectUri = MSGraphAPIDemoSettings.PostLogoutRedirectUri, Notifications = new OpenIdConnectAuthenticationNotifications() {
SecurityTokenValidated = (context) => {
return Task.FromResult(0);
},
AuthorizationCodeReceived = (context) => { var code = context.Code;
ClientCredential credential = new ClientCredential(
MSGraphAPIDemoSettings.ClientId,
MSGraphAPIDemoSettings.ClientSecret);
string signedInUserID =
context.AuthenticationTicket.Identity.FindFirst(
ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext( MSGraphAPIDemoSettings.Authority,
new SessionADALCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code,
new
Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)),
credential,
MSGraphAPIDemoSettings.MicrosoftGraphResourceId);
return Task.FromResult(0);
},
AuthenticationFailed = (context) => { context.OwinContext.Response.Redirect("/Home/Error"); context.HandleResponse(); // Suppress the exception return Task.FromResult(0);
}
}
});
}
}

Within the OpenIdConnectAuthenticationOptions constructor, there is the configuration of the Notifications property, which is of type OpenIdConnectAuthenticationNotifications and allows you to intercept and handle some interesting events related to the OpenID authentication flow.

 

In particular, the event called AuthorizationCodeReceived allows you to handle the OpenID Connect authorization code, and to request an OAuth access token and a refresh token based on that.

 

Inside the authorization code received notification implementation, you can see the request for an access token through the invocation of method AcquireTokenByAuthorizationCode of an object of type AuthenticationContext. The result will be an object of type AuthenticationResult, which will include both an OAuth access token and a refresh token.

 

Notice also that the AuthenticationContext instance is created by providing an object of type SessionADALCache. This is a custom cache object that ADAL will use to cache both the access token and the refresh token for a single user, based on the user ID passed to the constructor of the cache object.

 

The session-based ADAL cache sample is simple, and in a real scenario you should use another kind of cache based, for example, on a SQL Server database and some Entity Framework code or even based on the Redis Cache of Microsoft Azure if your application will be hosted on Microsoft Azure.

 

Aside from the initialization code, which is executed whenever the user’s authentication flow starts, to access the Microsoft Graph API you will have to provide a valid OAuth access token.

 

In Listing, you can see a helper function, which is part of the sample project, to retrieve an access token either from the ADAL cache or by refreshing a new one through the refresh token stored in the ADAL cache.

 

If neither the access token nor the refresh token is valid, the helper method will handle a full refresh of the authentication context by invoking the Challenge method of the current OWIN Authentication context.

 

LISTING A helper method to get an OAuth access token for accessing the Microsoft Graph API

<summary>
This helper method returns an OAuth Access Token for the current user
</summary>
<param name="resourceId">The resourceId for which we are requesting the token</param>
<returns>The OAuth Access Token value</returns>
public static String GetAccessTokenForCurrentUser(String resourceId = null) {
String accessToken = null;
if (String.IsNullOrEmpty(resourceId)) {
resourceId = MSGraphAPIDemoSettings.MicrosoftGraphResourceId;
}
try {
ClientCredential credential = new ClientCredential( MSGraphAPIDemoSettings.ClientId,
MSGraphAPIDemoSettings.ClientSecret);
string signedInUserID = System.Security.Claims.ClaimsPrincipal.Current
.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext( MSGraphAPIDemoSettings.Authority,
new SessionADALCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenSilent( MSGraphAPIDemoSettings.MicrosoftGraphResourceId, credential,
UserIdentifier.AnyUser);
accessToken = result.AccessToken;
}
catch (AdalException ex) {
if (ex.ErrorCode == "failed_to_acquire_token_silently") {
Refresh the access token from scratch
HttpContext.Current.GetOwinContext().Authentication.Challenge( new AuthenticationProperties {
RedirectUri = HttpContext.Current.Request.Url.ToString(),
},
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
else {
Rethrow the exception throw ex;
}
}
return (accessToken);
}

 

Notice the AcquireTokenSilent method invocation, which will get the access token from the ADAL cache or refresh it based on the cached refresh token. The ADAL cache, as described previously, is built based on the current user ID.

 

In case of an exception with an error code with a value of failed_to_acquire_token_silently, the helper will invoke the Challenge method to start a new authentication flow, as discussed previously.

 

In Listing, you can see a code excerpt that illustrates how to use the method GetAccessTokenForCurrentUser described in Listing.

 

LISTING  An excerpt of code that initializes an instance of the HttpClient leveraging the OAuth access token retrieved by invoking the GetAccessTokenForCurrentUser helper method

<summary>
This helper method makes an HTTP request and eventually returns a result
</summary>
<param name="httpMethod">The HTTP method for the request</param>
<param name="requestUrl">The URL of the request</param>
<param name="accept">The content type of the accepted response</param>
<param name="content">The content of the request</param>
<param name="contentType">The content type of the request</param>
<param name="resultPredicate">The predicate to retrieve the result, if any</param>
<typeparam name="TResult">The type of the result, if any</typeparam>
<returns>The value of the result, if any</returns>
private static TResult MakeHttpRequest<TResult>( String httpMethod,
String requestUrl,
String accept = null,
Object content = null,
String contentType = null,
Func<HttpResponseMessage, TResult> resultPredicate = null) {
Prepare the variable to hold the result, if any TResult result = default(TResult);
Get the OAuth Access Token
Uri requestUri = new Uri(requestUrl);
Uri graphUri = new Uri(MSGraphAPIDemoSettings.MicrosoftGraphResourceId); var accessToken =
GetAccessTokenForCurrentUser(requestUri.DnsSafeHost != graphUri.DnsSafeHost ?
$"{requestUri.Scheme}://{requestUri.Host}") : MSGraphAPIDemoSettings.MicrosoftGraphResourceId);
if (!String.IsNullOrEmpty(accessToken)) {
If we have the token, then handle the HTTP request HttpClient httpClient = new HttpClient();
Set the Authorization Bearer token httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
If there is an accept argument, set the corresponding HTTP header if (!String.IsNullOrEmpty(accept)) {
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue(accept));
}
Prepare the content of the request, if any
HttpContent requestContent =
(content != null) ?
new StringContent(JsonConvert.SerializeObject(content, Formatting.None,
new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new
CamelCasePropertyNamesContractResolver(),
}),
Encoding.UTF8, contentType) : null;
Prepare the HTTP request message with the proper HTTP method HttpRequestMessage request = new HttpRequestMessage(
new HttpMethod(httpMethod), requestUrl);
Set the request content, if any
if (requestContent != null) {
request.Content = requestContent;
}
// Fire the HTTP request
HttpResponseMessage response = httpClient.SendAsync(request).Result;
if (response.IsSuccessStatusCode) {
If the response is Success and there is a
predicate to retrieve the result, invoke it if (resultPredicate != null) {
result = resultPredicate(response);
}
}
else {
throw new ApplicationException(
String.Format("Exception while invoking endpoint {0}.",
graphRequestUri),
new HttpException(
(Int32)response.StatusCode,
response.Content.ReadAsStringAsync().Result));
}
}
return (result);
}

As you can see, the generic method called MakeHttpRequest internally handles any kind of HTTP request, based on the HTTP method and the graphRequestUri input arguments.

 

Moreover, it sets up the HTTP Authorization header of type Bearer by using the value of the OAuth access token retrieved through the GetAccessTokenForCurrentUser method illustrated in Listing.

 

The generic type TResult defines the type of the result if any. In that case, the optional resultPredicate argument is used to retrieve a typed result for the HTTP request.

 

Notice the excerpt highlighted in bold, where the content of the HTTP request is defined. The syntax leverages the JsonConvert object, providing some custom serialization settings to suppress any null property that otherwise would be noisy for the target Microsoft Graph API.

 

The MakeHttpRequest method will be used internally by the MicrosoftGraphHelper class to fire an HTTP request in the following sections. In the MicrosoftGraphHelper class, there are a bunch of HTTP-related methods to make it easy to handle any kind of request. In Listing, you can see the definition of these methods.

 

LISTING  Code excerpt of the HTTP-related methods defined in the MicrosoftGraphHelper class

<summary>
This helper method makes an HTTP GET request and returns the result as a String
</summary>
<param name="graphRequestUri">The URL of the request</param>
<returns>The String value of the result</returns>
public static String MakeGetRequestForString(String graphRequestUri) { return (MakeHttpRequest<String>("GET",
graphRequestUri,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}
<summary>
This helper method makes an HTTP GET request and returns the result as a Stream
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="accept">The accept header for the response</param>
<returns>The Stream of the result</returns>
public static http://System.IO.Stream MakeGetRequestForStream(String graphRequestUri, String accept) {
return (MakeHttpRequest<http://System.IO.Stream>("GET", graphRequestUri,
resultPredicate: r => r.Content.ReadAsStreamAsync().Result));
}
<summary>
This helper method makes an HTTP POST request without a response
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param> public static void MakePostRequest(String graphRequestUri,
Object content = null, String contentType = null) {
MakeHttpRequest<String>("POST", graphRequestUri,
content: content,
contentType: contentType);
}
<summary>
This helper method makes an HTTP POST request and returns the result as a String
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param>
<returns>The String value of the result</returns>
public static String MakePostRequestForString(String graphRequestUri, Object content = null,
String contentType = null) {
return (MakeHttpRequest<String>("POST",
graphRequestUri,
content: content,
contentType: contentType,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}
<summary>
This helper method makes an HTTP PATCH request and returns the result as a String
</summary>
<param name="graphRequestUri">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param>
<returns>The String value of the result</returns>
public static String MakePatchRequestForString(String graphRequestUri, Object content = null,
String contentType = null) {
return (MakeHttpRequest<String>("PATCH",
graphRequestUri,
content: content,
contentType: contentType,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}
<summary>
This helper method makes an HTTP DELETE request
</summary>
<param name="graphRequestUri">The URL of the request</param>
<returns>The String value of the result</returns>
public static void MakeDeleteRequest(String graphRequestUri) { MakeHttpRequest<String>("DELETE", graphRequestUri);
}
<summary>
This helper method makes an HTTP PUT request without a response
</summary>
<param name="requestUrl">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param> public static void MakePutRequest(String requestUrl,
Object content = null, String contentType = null) { MakeHttpRequest<String>("PUT",
requestUrl, content: content, contentType: contentType);
}
<summary>
This helper method makes an HTTP PUT request and returns the result as a String
</summary>
<param name="requestUrl">The URL of the request</param>
<param name="content">The content of the request</param>
<param name="contentType">The content/type of the request</param>
<returns>The String value of the result</returns>
public static String MakePutRequestForString(String requestUrl, Object content = null, String contentType = null) { return(MakeHttpRequest<String>("PUT",
requestUrl,
content: content,
contentType: contentType,
resultPredicate: r => r.Content.ReadAsStringAsync().Result));
}

You are now ready to consume the Microsoft Graph API within your code by leveraging these helper methods and the setup environment.

 

Mail services

“Microsoft Graph API reference,” to consume the mail services you will need to access the proper REST endpoint and process the related JSON responses.

 

However, from a .NET perspective, you will have to deserialize every JSON response into something that can be handled by your code. To achieve this, you can, for example, use the Newtonsoft.Json package, which is available through NuGet.

 

Reading folders, messages, and attachments

When playing with the mail services, the first and most common thing to do is to enumerate the email messages that you have in a mailbox. In Listing, you can see a code excerpt about how to retrieve the list of mail folders in the current user’s mailbox.

 

LISTING  Code excerpt to enumerate the email folders in a mailbox

<summary>
This method retrieves the email folders of the current user
</summary>
<param name="startIndex">The startIndex (0 based) of the folders to retrieve</param>
<returns>A page of up to 10 email folders</returns>
public static List<MailFolder> ListFolders(Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/mailFolders?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex));
var folders = JsonConvert.DeserializeObject<MailFolderList>(jsonResponse); return (folders.Folders);
}

 

As you can see, the method leverages the helper method MakeGetRequestForString. It uses the JsonConvert type of Newtonsoft.Json to convert the JSON response string into a custom type (MailFolderList) that holds a collection of objects of type MailFolder. 

 

Moreover, as you can see in the code highlighted in bold, the URL of the request handles paging of responses by leveraging the OData $skip query string parameter. By default, the Microsoft Graph API will return results in chunks of no more than 10 elements, skipping a number of elements based on the $skip parameter value.

 

Thanks to this out-of-box behavior of the Microsoft Graph API, you easily can do paging of every result set with pages of up to 10 elements each.

 

LISTING Code excerpt that defines the mail folder list and the MailFolder model types

<summary>
Defines a list of email message folders
</summary>
public class MailFolderList {
<summary>
The list of email message folders
</summary>
[JsonProperty("value")]
public List<MailFolder> Folders { get; set; }
}
<summary>
Defines an email Folder
</summary>
public class MailFolder : BaseModel {
<summary>
The display name of the email folder
</summary>
[JsonProperty("displayName")] public String Name { get; set; }
<summary>
Total number of items
</summary>
public Int32 TotalItemCount { get; set; }
<summary>
Number of unread items
</summary>
public Int32 UnreadItemCount { get; set; }
}

 

Notice that the MailFolder type provides just a subset of the properties defined in a mail folder, but the JsonConvert engine will handle that, including any property remapping, by leveraging the JsonProperty attribute.

 

Shaping the MailFolder type and any other domain model type is a task you must perform based on your real business requirements if you want to consume the Microsoft Graph API manually and at a low level, with pure HTTP, REST, and JSON.

 

Once you have the list of folders for the current user, you can access the email messages of a specific folder by making a REST request for a URL like the following: https://graph.microsoft.com/v1.0/me/mailFolders/<FolderID>/messages In Listing, you can see a code excerpt of a method that retrieves such a list of email messages.

 

LISTING Code excerpt of a method that retrieves the email messages of a mail folder

<summary>
This method retrieves the email messages from a folder in the current user's mailbox
</summary>
<param name="folderId">The ID of the target folder, optional</param>
<param name="startIndex">The startIndex (0 based) of the messages to retrieve</param>
<param name="includeAttachments">Defines whether to include attachments</param>
<returns>A page of up to 10 email messages in the folder</returns>
public static List<MailMessage> ListMessages(String folderId = null, Int32 startIndex = 0,
Boolean includeAttachments = false) {
String targetUrl = null;
if (!String.IsNullOrEmpty(folderId)) {
targetUrl = String.Format("{0}me/mailFolders/{1}/messages?$skip={2}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
folderId, startIndex);
}
else {
targetUrl = String.Format("{0}me/messages?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex);
}
String jsonResponse =
MicrosoftGraphHelper.MakeGetRequestForString(targetUrl);
var messages = JsonConvert.DeserializeObject<MailMessageList>(jsonResponse);
if (includeAttachments)
foreach (var message in messages.Messages.Where(m => m.HasAttachments))
{
message.LoadAttachments();
}
}
return (messages.Messages);
}

The logic of the ListMessages method illustrated in Listing is similar to that of the ListFolders method illustrated in Listing, including how the paging of results is handled.

 

However, in the ListMessages method, there is also some business logic to retrieve the attachments of the messages, if requested with the included attachments Boolean argument, by leveraging an extension method called LoadAttachments that extends the MailMessage custom type.

 

LISTING Code excerpt that defines the mail message list and the MailMessage types

<summary>
Defines a list of email messages
</summary>
public class MailMessageList {
<summary>
The list of messages
</summary>
[JsonProperty("value")]
public List<MailMessage> Messages { get; set; }
}
<summary>
Defines an email message
</summary>
public class MailMessage : BaseModel {
public MailMessage() {
this.Attachments = new List<MailAttachment>();
}
<summary>
The importance of the email message
</summary>
[JsonConverter(typeof(StringEnumConverter))] public ItemImportance Importance { get; set; }
<summary>
The sender email address
</summary>
[JsonProperty("from")
public MailMessageRecipient From { get; set; }
<summary>
The list of email address TO recipients
</summary>
[JsonProperty("toRecipients")]
public List<MailMessageRecipient> To { get; set; }
<summary>
The list of email address CC recipients
</summary>
[JsonProperty("ccRecipients")]
public List<MailMessageRecipient> CC { get; set; }
<summary>
The list of email address BCC recipients
</summary>
[JsonProperty("bccRecipients")]
public List<MailMessageRecipient> BCC { get; set; }
<summary>
The subject of the email message
</summary>
public String Subject { get; set; }
<summary>
The body of the email message
</summary>
public ItemBody Body { get; set; }
<summary>
The UTC sent date and time of the email message
</summary>
public Nullable<DateTime> SentDateTime { get; set; }
<summary>
The UTC received date and time of the email message
</summary>
public Nullable<DateTime> ReceivedDateTime { get; set; }
<summary>
Defines whether the email message is read on unread
</summary>
public Boolean IsRead { get; set; }
<summary>
Defines whether the email message is a draft
</summary>
public Boolean IsDraft { get; set; }
<summary>
Defines whether the email has attachments
</summary>
public Boolean HasAttachments { get; set; }
<summary>
The list of email message attachments, if any
</summary>
public List<MailAttachment> Attachments { get; private set; }
}
<summary>
Defines the importance of an email message
</summary>
public enum ItemImportance {
<summary>
Low importance
</summary>
Low,
<summary>
Normal importance, default value
</summary>
<summary>
High importance
</summary>
High,
}
<summary>
Defines a recipient of an email message/meeting
</summary>
public class UserInfoContainer {
<summary>
The email address of the recipient
</summary>
[JsonProperty("emailAddress")]
public EmailAddress Recipient { get; set; }
}
<summary>
Defines a user info
</summary>
public class UserInfo {
<summary>
The email address
</summary>
public String Address { get; set; }
<summary>
The description of the email address
</summary>
public String Name { get; set; }
}

 

The layouts of the custom MailMessage and MailMessageList types are defined to make it easier to deserialize the JSON response retrieved from the Microsoft Graph API into .NET complex types.

 

You can also think about using some custom JsonConvert types to customize the out-of-box behavior of the Newtonsoft.Json library, transforming the results into something different from the JSON response structure.

 

For example, within the definition of the MailMessage type, there is the custom converter of type StringEnumConverter applied to the Importance property of type ItemImportance, to serialize the value of the enum type as a JSON string instead of using a number.

 

Moreover, the properties called SentDateTime and ReceivedDateTime are of type Nullable<DateTime> to customize the behavior of the Newtonsoft.Json library while serializing an email message instance.

 

These settings will be helpful later in this blog in the section “Sending an email message,” when we will send email messages through the Microsoft Graph API.

 

In Listing, you can see how the LoadAttachments extension method is defined.

LISTING Code excerpt of the LoadAttachments extension method to retrieve the attachments of an email message

<summary>
Extension method to load the attachments of an email message
</summary>
<param name="message">The target email message</param>
public static void LoadAttachments(this MailMessage message) {
if (message.HasAttachments) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/messages/{1}/attachments",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
http://message.Id));
var attachments = JsonConvert.DeserializeObject<MailAttachmentList> (jsonResponse);
message.Attachments.AddRange(attachments.Attachments);
foreach (var attachment in message.Attachments) { attachment.ParentMessageId = http://message.Id;
}
}
}

 

The business logic makes an HTTP request for the URL of the Microsoft Graph API that retrieves the attachments of the current message. Then, it deserializes the JSON response into a list of .NET complex types and loads each instance of the MailAttachment type into the collection of attachments of the target MailMessage instance.

 

The binary content of every attachment is handled automatically by the Newtonsoft.Json library, and you will find it in the Byte array property with name Content of the custom type MailAttachment.

 

Unfortunately, if the email message for which you are downloading the attachments has one or more big files attached, executing the request illustrated in Listing could become expensive and slow, depending on the available bandwidth.

 

It is better to leverage the OData querying capabilities and query for the list of attachments, including their size and excluding their binary content. Later, you can download just the necessary content of those attachments by accessing their URL endpoint directly. 

 

LISTING Code excerpt of the LoadAttachments extension method to retrieve the attachments of an email message, with improved code quality

<summary>
Extension method to load the attachments of an email message in a smart way
</summary>
<param name="message">The target email message</param>
public static void LoadAttachments(this MailMessage message) { if (message.HasAttachments) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/messages/{1}/attachments?
select=contentType,id,name,size",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
http://message.Id));
var attachments = JsonConvert.DeserializeObject<MailAttachmentList> (jsonResponse);
message.Attachments.AddRange(attachments.Attachments);
foreach (var attachment in message.Attachments) { attachment.ParentMessageId = http://message.Id;
}
}
}
<summary>
Extension method to load the content of a specific attachment
</summary>
<param name="message">The target email message</param>
public static void EnsureContent(this MailAttachment attachment) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/messages/{1}/attachments/{2}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, attachment.ParentMessageId, http://attachment.Id));
var result = JsonConvert.DeserializeObject<MailAttachment>(jsonResponse); attachment.Content = result.Content;
}

The EnsureContent method takes care of downloading the binary content of a single attachment if it is needed.

 

Sending an email message

Another common use case is sending an email message through the Microsoft Graph API. Achieving this result with ASP.NET  MVC is straightforward. In Listing, there is a code excerpt to show how to leverage the microsoft.graph.sendMail action of my entity.

 

LISTING Code excerpt showing how to send an email message

MailHelper.SendMessage(new Models.MailMessageToSend { Message = new Models.MailMessage {
Subject = "Test message",
Body = new Models.ItemBody {
Content = "<html><body><h1>Hello from ASP.NET MVC calling " + "the Microsoft Graph API!</h1></body></html>",
Type = Models.BodyType.Html,
},
To = new List<Models.UserInfoContainer>(new Models.UserInfoContainer[] { new Models.UserInfoContainer {
Recipient = new Models.UserInfo {
Name = "Thesis Scientist",
Address = "Thesis@Scientist.com",
}
}
}),
},
SaveToSentItems = true,
});
<summary>
This method sends an email message
</summary>
<param name="message"></param>
public static void SendMessage(MailMessageToSend message) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/microsoft.graph.sendMail", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri), message, "application/json");
}

 

In Listing, the content of the request is an object of type MailMessageToSend, which represents the structure of the JSON message expected by the microsoft.graph.sendMail method. 

 

The SendMessage method internally uses the helper method called MakePostRequest. It makes an HTTP POST request, providing the body content of the request as a JSON-serialized object.

 

As you can see from these examples, the processes for every kind of mail API call and every kind of complex type are similar. You have to make a REST request, with or without a request content, depending on the API you want to consume.

 

You have to define a .NET complex type to hold the JSON serialization/deserialization of the request and the response.

 

Last, you have to do some custom mapping or additional REST requests to enrich the resulting object model. From an ASP.NET MVC application perspective, you also have to implement the controllers to handle the requests and the views to present the output to the end users.

 

Note

Implementing an  ASP.NET MVC application is out of the scope of this book. If you need to improve your knowledge about how to create an ASP.NET  MVC application, you can read the web section “Learn About ASP.NET MVC,” which is available at the following URL: www.asp.net/mvc.

 

Reply, reply all, and forward email messages

Sending an email message is just one option you have. You can also reply to a received message, reply all, or forward a received message. To accomplish these tasks you can rely on the actions reply, and forward that are available through the Microsoft Graph API and apply to any object that represents an email message.

 

In Listing, you can see a code excerpt of three helper methods that reply, reply all, and forward an email message.

 

LISTING Code excerpt to show the implementation of helper methods to reply, reply all and forward an email message

<summary>
This method sends a reply to an email message
</summary>
<param name="messageId">The ID of the message to reply to</param>
<param name="comment">Any comment to include in the reply, optional</param> public static void Reply(String messageId, String comment = null) {
MicrosoftGraphHelper.MakePostRequest( String.Format("{0}me/messages/{1}/reply", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, messageId),
content: !String.IsNullOrEmpty(comment) ? new { Comment = comment } :
null,
contentType: "application/json");
}
<summary>
This method sends a reply all to an email message
</summary>
<param name="messageId">The ID of the message to reply all to</param>
<param name="comment">Any comment to include in the reply all, optional</param>
public static void ReplyAll(String messageId, String comment = null) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/messages/{1}/replyAll", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, messageId),
content: !String.IsNullOrEmpty(comment) ? new { Comment = comment } :
null,
contentType: "application/json");
}
<summary>
This method forwards an email message to someone else
</summary>
<param name="messageId">The ID of the message to forward</param>
<param name="recipients">The recipients of the forward</param>
<param name="comment">Any comment to include in the reply all, optional</param>
public static void Forward(String messageId,
List<UserInfoContainer> recipients, String comment = null) { MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/messages/{1}/forward", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, messageId), content: new {
Comment = !String.IsNullOrEmpty(comment) ? comment : null,
ToRecipients = recipients,
}, contentType: "application/json");
}

 

In the sample, the URL of the actions and the content of the reply/reply all messages that will be sent is highlighted in bold. Notice also the use of an anonymous type to hold the value of the response content.

 

The same applies to the method for forwarding an email message, in which the HTTP request content is built as an anonymous type made of the Comment, which represents the body on top of the forward, and the ToRecipients, which are those to whom the message is forwarded. Using these methods to handle email responses is straightforward.

 

Calendar services

When developing custom Office 365 applications, it is often useful to interact with users’ calendars and events to provide functionalities around the basic calendar features.

 

As with users’ mailboxes, here you will learn how to enumerate calendars and events and how to send, accept, and reject meeting requests by using C# within the ASP.NET Site MVC sample application.

 

Reading calendars and events

Browsing the list of a current user’s calendars—or anybody else’s calendars, as long as you have proper permissions—requires you to make an HTTP GET request for the calendars navigation property of the target user.

 

In Listing, you can see the implementation of a helper method called ListCalendars that you can use to target the current user’s calendars.

 

LISTING Code excerpt to show the implementation of the ListCalendars method

<summary>
This method retrieves the calendars of the current user
</summary>
<param name="startIndex">The startIndex (0 based) of the folders to retrieve</param>
<returns>A page of up to 10 calendars</returns>
public static List<Calendar> ListCalendars(Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/calendars?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex));
var calendarList = JsonConvert.DeserializeObject<CalendarList> (jsonResponse);
return (calendarList.Calendars);
}

The calendars navigation property supports OData querying and paging through the $skip query string argument. The result has to be deserialized into a typed object like the CalendarList type illustrated in Listing, which internally holds a collection of objects of type Calendar.

 

LISTING Code excerpt to show the definition of the CalendarList type

<summary>
Defines a list of calendars
</summary>
public class CalendarList {
<summary>
The list of calendars
</summary>
[JsonProperty("value")]
public List<Calendar> Calendars { get; set; }
}
<summary>
Defines a user's calendar
</summary>
public class Calendar : BaseModel {
<summary>
The color of the calendar
</summary>
public String Color { get; set; }
<summary>
The name of the calendar
</summary>
public String Name { get; set; }
}

Once you have the list of calendars, you can retrieve a single calendar object by ID by using a syntax like the one shown in Listing, where the GetCalendar helper method makes an HTTP GET request for a specific calendar item.

 

LISTING Code excerpt to show the GetCalendar method

<summary>
This method retrieves a calendar of the current user
</summary>
<param name="id">The ID of the calendar</param>
<returns>The calendar</returns>
public static Calendar GetCalendar(String id) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/calendars/{1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, id));
var calendar = JsonConvert.DeserializeObject<Calendar>(jsonResponse); return (calendar);
}

The result of the HTTP request illustrated in Listing is a JSON string that can be deserialized into an object of custom type Calendar. To browse the events in the calendar, you will need to have a collection of objects of type Event, which can be retrieved by invoking the events navigation property of a calendar.

 

The JSON response will be deserialized into the collection. In Listing, you can see a helper method to retrieve a calendar’s events with paging.

 

LISTING Code excerpt to show the definition of the ListEvents helper method

<summary>
This method retrieves the events of the current user's calendar
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="startIndex">The startIndex (0 based) of the items to retrieve</param>
<returns>A page of up to 10 events</returns>
public static List<Event> ListEvents(String calendarId, Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/calendars/{1}/events?$skip={2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
calendarId,
startIndex));
var eventList = JsonConvert.DeserializeObject<EventList>(jsonResponse); return (eventList.Events);
}

 

An event is similar to an email message; the custom Event type shares many properties, which are highlighted in bold, with the MailMessage type. If you like, you could do some refactoring and share an abstract base class between the MailMessage and Event types.

 

Moreover, every Event type includes information specific to the event like the location, the start, and end date, the event type and status, and so on.

 

Most of these properties are defined through enumerations, and maybe you are wondering how to find the possible values for these enum types. The metadata document of the Microsoft Graph API provides you with all the needed information.

 

If you browse to the metadata URL (https://graph.microsoft.com/v1.0/$metadata), at the beginning of the XML metadata document you will find the definition of all the enumerations and their possible values.

 

Browsing calendar views

As you probably noticed while playing with the code samples illustrated in the previous section, when you query the Microsoft Graph API for the events of a calendar, you get back up to 10 current and past events. You probably will also need to play with events in the future. 

 

LISTING Code excerpt that shows how to retrieve the events within a specific date range

<summary>
Retrieves the events of the current user's calendar within a specific date range
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="startDate">The start date of the range</param>
<param name="endDate">The end date of the range</param>
<param name="startIndex">The startIndex (0 based) of the items to retrieve</param>
<returns>A page of up to 10 events</returns>
public static List<Event> ListEvents(String calendarId, DateTime startDate, DateTime endDate, Int32 startIndex = 0) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/calendars/{1}/calendarView?" + "startDateTime={2:o}&endDateTime={3:o}&$skip={4}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, calendarId,
startDate.ToUniversalTime(),
endDate.ToUniversalTime(),
startIndex));
var eventList = JsonConvert.DeserializeObject<EventList>(jsonResponse); return (eventList.Events);
}

The URL of the request targets the calendarView navigation property of the target calendar object and is built providing the arguments startDateTime and endDateTime within the query string. Notice that the values of the two date and time arguments are converted into UTC time zone and formatted according to the OData protocol format requirements.

 

You will still get back objects of type Event, divided in pages of no more than 10 items. Thus, if you want to download all the events or multiple pages of events, you will still have to make multiple requests, leveraging the $skip query string argument.

 

Keep in mind that you are not obliged to download the entire set of properties for every event. You can use the $select query string argument introduced earlier to project a subset of the properties to download only what you need.

 

Managing a series of events

Another interesting scenario is recurring events and series of events. From a business requirements perspective, you may need to create a custom Office 365 application that enriches the capabilities around an event series. As you have seen in the previous section, every event has a Type property.

 

When that property assumes a value of SeriesMaster, Occurrence, or Exception, it means that the event is part of a series. Aside from the event of type SeriesMaster, which is the main event of a series as the type implies, the two other kinds of events have a property called SeriesMasterId, which is a reference to the ID of the master event for the series of events.

 

The events of type Occurrence are regular events based on a series, while the events of type Exception correspond to modified items of a series. Moreover, in every recurring event, there is a complex property called Recurrence that defines the recurrence pattern and range for the series of events. 

 

LISTING

Code excerpt showing the definition of the EventRecurrence type
<summary>
Defines the Recurrence for a series of events
</summary>
public class EventRecurrence {
<summary>
The Recurrence Pattern
</summary>
public EventRecurrencePattern Pattern { get; set; }
<summary>
The Recurrence Range
</summary>
public EventRecurrenceRange Range { get; set; }
}
<summary>
Defines the Recurrence Pattern for a series of events
</summary>
public class EventRecurrencePattern {
<summary>
The day of the month for the recurrence
</summary>
public Int32 DayOfMonth { get; set; }
<summary>
The days of the week for the recurrence
</summary>
[JsonProperty(ItemConverterType = typeof(StringEnumConverter))] public DayOfWeek[] DaysOfWeek { get; set; }
<summary>
The first day of the week
</summary>
[JsonConverter(typeof(StringEnumConverter))]
public DayOfWeek FirstDayOfWeek { get; set; }
<summary>
The week of the month
</summary>
[JsonConverter(typeof(StringEnumConverter))] public WeekIndex Index { get; set; }
<summary>
The interval for repeating occurrences
</summary>
public Int32 Interval { get; set; }
<summary>
The month for the recurrence
</summary>
public Int32 Month { get; set; }
<summary>
The type of recurrence
</summary>
[JsonConverter(typeof(StringEnumConverter))] public RecurrenceType Type { get; set; }
}
<summary>
Defines the Recurrence Range for a series of events
</summary>
public class EventRecurrenceRange
{
<summary>
The Start Date of the recurrence
</summary>
[JsonConverter(typeof(Converters.DateOnlyConverter))] public Nullable<DateTime> StartDate { get; set; }
<summary>

The End Date of the recurrence

</summary>
[JsonConverter(typeof(Converters.DateOnlyConverter))] public Nullable<DateTime> EndDate { get; set; }
<summary>
The number of occurrences
</summary>
public Int32 NumberOfOccurrences { get; set; }
<summary>
The reference TimeZone for the recurrence
</summary>
public String RecurrenceTimeZone { get; set; }
<summary>
The type of recurrence
</summary>
[JsonConverter(typeof(StringEnumConverter))] public RecurrenceRangeType Type { get; set; }
}

 

There are some infrastructural enumerations under the cover of some properties. However, for the sake of simplicity. You can also see that some of the properties are decorated with custom attributes to adapt the Newtonsoft.Json serialization engine to the context.

 

For example, the StartDate and EndDate properties of the EventRecurrenceRange type have a custom JSON converter to serialize date-only values, skipping the time part.

 

In addition, the DaysOfWeek property of the EventRecurrencePattern type has a JsonProperty attribute to instruct the serialization engine to handle every item of the array with the custom StringEnumConverter converter provided by the Newtonsoft.Json library.

 

If you want to retrieve all the occurrences of a specific series of events, you can leverage the instances navigation property of the event that is master of the series.

 

To invoke the instances navigation property, you have to provide a startDateTime and an endDateTime query string parameter to declare the boundaries of the range of dates from which you want to retrieve the instances.

 

You cannot retrieve all the instances at once; the events will be divided into chunks of up to 10 items, and you can leverage the $skip query string argument to move across pages of instances. In Listing, you can see a code excerpt to retrieve the event occurrences of an event series based on the ID of the master event.

 

LISTING Code excerpt to retrieve the events of a series of events from a target calendar

<summary>
This method retrieves the events of a series within a specific date range
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="masterSeriesId">The ID of the master event of the series</param>
<param name="startDate">The start date of the range</param>
<param name="endDate">The end date of the range</param>
<param name="startIndex">The startIndex (0 based) of the items to retrieve</param>
<returns>A page of up to 10 events</returns>
public static List<Event> ListSeriesInstances(String calendarId, String masterSeriesId,
DateTime startDate,
DateTime endDate,
Int32 startIndex = 0) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/calendars/{1}/events/{2}/instances?" + "startDateTime={3:o}&endDateTime={4:o}&$skip={5}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
calendarId,
masterSeriesId,
startDate.ToUniversalTime(),
endDate.ToUniversalTime(),
startIndex));
var eventList = JsonConvert.DeserializeObject<EventList>(jsonResponse); return (eventList.Events);
}

 

The helper method builds the URL of the request, setting the ID of the calendar and of the master event of the series. Moreover, it configures the startDateTime, endDateTime, and $skip query string arguments.

 

Managing invitations for meeting requests

What if you get an invitation to a meeting request? Let’s say you want to write a custom Office 365 application to automate the process of handling meeting request invitations. You can use the HTTP helper methods that we have to send feedbacks for meeting requests.

 

In blog 3, you saw that every meeting request has the ResponseStatus property and that you can leverage the operations Accept, Decline, and TentativelyAccept to provide your feedback to the meeting organizer. In Listing, you can see a code excerpt to give feedback for a received meeting request.

 

LISTING Code excerpt of a helper method to provide feedback for a received meeting request

<summary>
This method provides a feedback for a received meeting request
</summary>
<param name="calendarId">The ID of the calendar</param>
<param name="eventId">The ID of the meeting request</param>
<param name="feedback">The feedback for the meeting request</param <param name="comment">Any comment to include in the feedback, optional</param>
public static void SendFeedbackForMeetingRequest(String calendarId,
String eventId,
MeetingRequestFeedback feedback,
String comment = null) {
MicrosoftGraphHelper.MakePostRequest(
String.Format("{0}me/calendars/{1}/events/{2}/{3}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, calendarId, eventId, feedback),
content: !String.IsNullOrEmpty(comment) ? new { Comment = comment
} : null,
contentType: "application/json");
}

Note that the feedback is represented using a custom enumeration of possible feedbacks, which is called MeetingRequestFeedback. Moreover, every meeting request feedback can include a comment that will be sent to the meeting organizer with the feedback.

 

Thus, the custom helper method accepts a comment argument of type String, which is provided within the HTTP request content using an anonymous type that will be serialized in JSON according to the content structure expected by the backing Microsoft Graph API.

 

Contact services

The last group of services provided by the Microsoft Graph API against Microsoft Exchange Online is those related to handling contacts. In this section, you will learn how to retrieve, add, update, or delete users’ personal contacts.

 

Reading contacts

Like mailboxes and calendars, you can access a user’s default folder of contacts by making a request for the contacts navigation property of a user, whether it is the current user (that is, me) or any other user for whom you have the permission to read contacts. For example, in Listing there is a helper method to retrieve the contacts of the current user.

 

LISTING Code excerpt of a helper method to get the contacts of the current user

<summary>
This method retrieves the contacts of the current user
</summary>
<param name="startIndex">The startIndex (0 based) of the contacts to retrieve</param>
/// <returns>A page of up to 10 contacts</returns>
public static List<Contact> ListContacts(Int32 startIndex = 0) { String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString(
String.Format("{0}me/contacts?$skip={1}", MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, startIndex));
var contactList = JsonConvert.DeserializeObject<ContactList>(jsonResponse); return (contactList.Contacts);
}

As expected, the method retrieves contacts in pages of up to 10 items each. There is nothing special in the code sample of Listing when compared to those related to email messages, calendars, or events.

 

The only interesting difference is the outline of the Contact type, which presents some of the main properties returned by the Microsoft Graph API for every contact object. 

 

LISTING Code excerpt to define the Contact and ContactList types

<summary>
Defines a list of contacts
</summary>
public class ContactList {
<summary>
The list of contacts
</summary>
[JsonProperty("value")]
public List<Contact> Contacts { get; set; }
}
<summary>
Defines a user's contact
</summary>
public class Contact : BaseModel {
<summary>
The business address of the contact
</summary>
public PhysicalAddress BusinessAddress { get; set; }
<summary>
The business phones of the contact
</summary>
public List<String> BusinessPhones { get; set; }
<summary>
The business home page of the contact
</summary>
public String BusinessHomePage { get; set; }
<summary>
The company name of the contact
</summary>
public String CompanyName { get; set; }
<summary>
The department name of the contact
</summary>
public String Department { get; set; }
<summary The display name of the contact </summary>
public String DisplayName { get; set; }
<summary>
The list of email addresses of the contact
</summary>
public List<UserInfo> EmailAddresses { get; set; }
<summary>
The "File As" of the contact
</summary>
public String FileAs { get; set; }
<summary>
The home address of the contact
</summary>
public PhysicalAddress HomeAddress { get; set; }
<summary>
The home phones of the contact
</summary>
public List<String> HomePhones { get; set; }
<summary>
The office location of the contact
</summary>
public String OfficeLocation { get; set; }
<summary>
The other address of the contact
</summary>
public PhysicalAddress OtherAddress { get; set; }
<summary>
Personal notes about the contact
</summary>
public String PersonalNotes { get; set; }
<summary>
The first name of the contac
</summary>
public String GivenName { get; set; }
<summary>
The family name of the contac
</summary>
public String Surname { get; set; }
<summary>
The title of the contact
</summary>
public String Title { get; set; }
}
<summary>
Defines a physical address
</summary>
public class PhysicalAddress {
<summary>
The Street part of the address
</summary>
public String Street { get; set; }
<summary>
The City part of the address
</summary>
public String City { get; set; }
<summary>
The State part of the address
</summary>
public String State { get; set; }
<summary>
The Country or Region part of the address
</summary>
public String CountryOrRegion { get; set; }
<summary>
The Postal Code part of the address
</summary>
public String PostalCode { get; set; }
}

You can define these types as you like, as long as they can be used to deserialize the JSON response returned by the service. When looking at how the code samples related to this book implement these types, it is interesting to notice how the properties HomeAddress, BusinessAddress, and OtherAddress are defined by leveraging the PhysicalAddress type.

 

Moreover, note that the property EmailAddresses of every contact is a collection of instances of type UserInfo, which is also used to define the recipients of an email message. Thus, you can reuse this property to send an email message directly to a specific contact.

 

Furthermore, like with email messages and calendars, with contacts, you can browse multiple contact folders. Every user object has a navigation property called contact folders that represents a container folder for contacts. 

 

By default, every user’s account has a default contact folder that is the root folder, which can contain a hierarchy of child folders. Using the navigation properties contact folders and child folders, you can browse all these folders. 

 

LISTING Code excerpt to retrieve the contacts of a contacts folder

<summary>
This method retrieves the contacts of a contacts folder for the current user
</summary>
<param name="contactFolderId">The ID of the contacts folder</param>
<param name="startIndex">The startIndex (0 based) of the contacts to retrieve</param>
<returns>A page of up to 10 contacts</returns>
public static List<Contact> ListContacts(String contactFolderId, Int32 startIndex = 0) {
String jsonResponse = MicrosoftGraphHelper.MakeGetRequestForString( String.Format("{0}me/contactFolders/{1}/contacts?$skip={2}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
contactFolderId,
startIndex));
var contactList = JsonConvert.DeserializeObject<ContactList>(jsonResponse); return (contactList.Contacts);
}

 

Every contact can have a picture to better define the contact in the address list. To access the contact’s picture, as you will do in blog 6, you can make an HTTP GET request for the navigation property photo and extract its $value. 

 

LISTING Code excerpt of a helper method to get a contact’s picture

<summary>
Retrieves the picture of a contact, if any
</summary>
<param name="contactId">The ID of the contact</param>
<returns>The picture as a binary Stream</returns> public static Stream GetContactPhoto(String contactId) {
Stream result = null;
String contentType = "image/png";
try {
result = MicrosoftGraphHelper.MakeGetRequestForStream( String.Format("{0}me/contacts/{1}/photo/$value",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri, contactId), contentType);
}
catch (HttpException ex) {
if (ex.ErrorCode == 404) {
If 404 -> The contact does not have a picture
Keep NULL value for result
result = null;
}
}
return (result);
}

When you request the photo of a contact that does not have an associated image, the Microsoft Graph API will return an HTTP Status Code with a value of 404 (Not Found) and an error message with a value of “The specified object was not found in the store.” 

 

Managing contacts

Another set of common tasks when you work with contacts is updating existing contacts and adding new contacts. As you can imagine, updating an existing contact is just a matter of making an HTTP PATCH request, providing the JSON serialized representation of the contact to update. The Contact type defined in Listing is also suitable for this task. 

 

LISTING Code excerpt of a helper method to update a contact

<summary>
This method updates a contact
</summary>
<param name="contact">The contact to update</param>
<returns>The updated contact</returns>
public static Contact UpdateContact(Contact contact) {
String jsonResponse = MicrosoftGraphHelper.MakePatchRequestForString( String.Format("{0}me/contacts/{1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri,
http://contact.Id),
contact,
"application/json");
var updatedContact = JsonConvert.DeserializeObject<Contact>(jsonResponse); return (updatedContact);
}

In Listing, you can see how to add a new contact.

 

LISTING Code excerpt of a helper method to add a new contact

<summary>
This method adds a contact
</summary>
<param name="contact">The contact to add</param>
<returns>The added contact</returns>
public static Contact AddContact(Contact contact) {
String jsonResponse = MicrosoftGraphHelper.MakePostRequestForString( String.Format("{0}me/contacts",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri),
contact,
"application/json");
var addedContact = JsonConvert.DeserializeObject<Contact>(jsonResponse); return (addedContact);
}

Both the methods return the updated or added contact object to make it easier for you to handle the refresh of the UI if needed. Deleting a contact is also straightforward. In Listing, you can see the corresponding helper method, which accepts the ID of the contact to delete.

 

LISTING Code excerpt of a helper method to delete a contact

<summary>
This method deletes a contact
</summary>
<param name="contactId">The ID of the contact to delete</param> public static void DeleteContact(String contactId) {
MicrosoftGraphHelper.MakeDeleteRequest( String.Format("{0}me/contacts/{1}",
MicrosoftGraphHelper.MicrosoftGraphV1BaseUri contactId));
}

Summary

In this blog, you saw how to set up an ASP.NET MVC application to authenticate with Microsoft Azure Active Directory and how to register the application in the Azure AD tenant that sits under the cover of a Microsoft Office 365 tenant.

 

Moreover, you saw how to leverage the Microsoft Active Directory Authentication Library (ADAL) to acquire an OAuth access token and how to store that token and a refresh token in a temporary cache.

 

Next, you saw how to leverage a helper class to manage the OAuth access token and make all the required HTTP requests to consume the Microsoft Graph API.

 

Last, you saw how to build a client application on top of these foundations that can consume the mail, calendar, and contact services provided by the Microsoft Graph API.

 

You can use the code you saw in this blog and the related code samples you can find on the Internet ((SharePoint/PnP)) as a starting point for writing your own software solutions that leverage the powerful capabilities of the Microsoft Graph.

 

Recommend