在此部分中,你将 Microsoft Graph 合并到应用程序中。In this section you will incorporate Microsoft Graph into the application. 对于此应用程序,你将使用 适用于 .NET 的 Microsoft Graph 客户端库调用 Microsoft Graph。For this application, you will use the Microsoft Graph Client Library for .NET to make calls to Microsoft Graph.
获取日历视图Get a calendar view
日历视图是用户日历中两个时间点之间发生的一组事件。A calendar view is a set of events from the user's calendar that occur between two points of time. 你将使用它获取当前一周的用户事件。You'll use this to get the user's events for the current week.
打开 ./Controllers/CalendarController.cs, 将以下函数添加到 CalendarController 类。Open ./Controllers/CalendarController.cs and add the following function to the CalendarController class.
private DateTime GetUtcStartOfWeekInTimeZone(DateTime today, string timeZoneId) { // Time zone returned by Graph could be Windows or IANA style // TimeZoneConverter can take either TimeZoneInfo userTimeZone = TZConvert.GetTimeZoneInfo(timeZoneId); // Assumes Sunday as first day of week int diff = System.DayOfWeek.Sunday - today.DayOfWeek; // create date as unspecified kind var unspecifiedStart = DateTime.SpecifyKind(today.AddDays(diff), DateTimeKind.Unspecified); // convert to UTC return TimeZoneInfo.ConvertTimeToUtc(unspecifiedStart, userTimeZone); }
添加以下函数以处理从 Microsoft Graph 调用返回的异常。Add the following function to handle exceptions returned from Microsoft Graph calls.
private async Task HandleGraphException(Exception exception) { if (exception is MicrosoftIdentityWebChallengeUserException) { _logger.LogError(exception, "Consent required"); // This exception indicates consent is required. // Return a 403 with "consent_required" in the body // to signal to the tab it needs to prompt for consent HttpContext.Response.ContentType = "text/plain"; HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; await HttpContext.Response.WriteAsync("consent_required"); } else if (exception is ServiceException) { var serviceException = exception as ServiceException; _logger.LogError(serviceException, "Graph service error occurred"); HttpContext.Response.ContentType = "text/plain"; HttpContext.Response.StatusCode = (int)serviceException.StatusCode; await HttpContext.Response.WriteAsync(serviceException.Error.ToString()); } else { _logger.LogError(exception, "Error occurred"); HttpContext.Response.ContentType = "text/plain"; HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; await HttpContext.Response.WriteAsync(exception.ToString()); } }
将现有的
Get
函数替换为以下内容。Replace the existingGet
function with the following.[HttpGet] public async Task<IEnumerable<Event>> Get() { // This verifies that the access_as_user scope is // present in the bearer token, throws if not HttpContext.VerifyUserHasAnyAcceptedScope(apiScopes); // To verify that the identity libraries have authenticated // based on the token, log the user's name _logger.LogInformation($"Authenticated user: {User.GetDisplayName()}"); try { // Get the user's mailbox settings var me = await _graphClient.Me .Request() .Select(u => new { u.MailboxSettings }) .GetAsync(); // Get the start and end of week in user's time // zone var startOfWeek = GetUtcStartOfWeekInTimeZone( DateTime.Today, me.MailboxSettings.TimeZone); var endOfWeek = startOfWeek.AddDays(7); // Set the start and end of the view var viewOptions = new List<QueryOption> { new QueryOption("startDateTime", startOfWeek.ToString("o")), new QueryOption("endDateTime", endOfWeek.ToString("o")) }; // Get the user's calendar view var results = await _graphClient.Me .CalendarView .Request(viewOptions) // Send user time zone in request so date/time in // response will be in preferred time zone .Header("Prefer", $"outlook.timezone=\"{me.MailboxSettings.TimeZone}\"") // Get max 50 per request .Top(50) // Only return fields app will use .Select(e => new { e.Subject, e.Organizer, e.Start, e.End, e.Location }) // Order results chronologically .OrderBy("start/dateTime") .GetAsync(); return results.CurrentPage; } catch (Exception ex) { await HandleGraphException(ex); return null; } }
查看更改。Review the changes. 此函数的新版本:This new version of the function:
- 返回
IEnumerable<Event>
而不是string
。ReturnsIEnumerable<Event>
instead ofstring
. - 使用 Microsoft Graph 获取用户的邮箱设置。Gets the user's mailbox settings using Microsoft Graph.
- 使用用户的时区计算本周的开始和结束。Uses the user's time zone to calculate the start and end of the current week.
- 获取日历视图Gets a calendar view
- 使用函数包含标头,这将导致返回的事件的开始时间和结束时间转换为
.Header()
Prefer: outlook.timezone
用户的时区。Uses the.Header()
function to include aPrefer: outlook.timezone
header, which causes the returned events to have their start and end times converted to the user's timezone. - 使用
.Top()
函数最多请求 50 个事件。Uses the.Top()
function to request at most 50 events. - 使用
.Select()
函数仅请求应用使用的字段。Uses the.Select()
function to request just the fields used by the app. - 使用
OrderBy()
函数按开始时间对结果进行排序。Uses theOrderBy()
function to sort the results by the start time.
- 使用函数包含标头,这将导致返回的事件的开始时间和结束时间转换为
- 返回
保存更改并重新启动该应用。Save your changes and restart the app. 刷新 Microsoft Teams 中的选项卡。Refresh the tab in Microsoft Teams. 应用显示事件的 JSON 列表。The app displays a JSON listing of the events.
显示结果Display the results
现在,你可以以更用户友好的方式显示事件列表。Now you can display the list of events in a more user friendly way.
打开 ./Pages/Index.cshtml, 在标记中添加以下
<script>
函数。Open ./Pages/Index.cshtml and add the following functions inside the<script>
tag.function renderSubject(subject) { if (!subject || subject.length <= 0) { subject = '<No subject>'; } return $('<div/>', { class: 'ms-fontSize-18 ms-fontWeight-bold', text: subject }); } function renderOrganizer(organizer) { return $('<div/>', { class: 'ms-fontSize-14 ms-fontWeight-semilight', text: organizer.emailAddress.name }).append($('<i/>', { class: 'ms-Icon ms-Icon--PartyLeader', style: 'margin-right: 10px;' })); } function renderTimeSpan(start, end) { return $('<div/>', { class: 'ms-fontSize-14 ms-fontWeight-semilight', text: `${formatDateTime(start.dateTime)} - ${formatDateTime(end.dateTime)}` }).append($('<i/>', { class: 'ms-Icon ms-Icon--DateTime2', style: 'margin-right: 10px;' })); } function formatDateTime(dateTime) { const date = new Date(dateTime); // Format like 10/14/2020 4:00 PM let hours = date.getHours(); const minutes = date.getMinutes(); const ampm = hours >= 12 ? 'PM' : 'AM'; hours = hours % 12; hours = hours ? hours : 12; const minStr = minutes < 10 ? `0${minutes}` : minutes; return `${date.getMonth()+1}/${date.getDate()}/${date.getFullYear()} ${hours}:${minStr} ${ampm}`; } function renderLocation(location) { if (!location || location.displayName.length <= 0) { return null; } return $('<div/>', { class: 'ms-fontSize-14 ms-fontWeight-semilight', text: location.displayName }).append($('<i/>', { class: 'ms-Icon ms-Icon--MapPin', style: 'margin-right: 10px;' })); }
将现有的
renderCalendar
函数替换为以下内容。Replace the existingrenderCalendar
function with the following.function renderCalendar(events) { $('#tab-container').empty(); // Add title $('<div/>', { class: 'tab-title ms-fontSize-42', text: 'Week at a glance' }).appendTo('#tab-container'); // Render each event events.map(event => { const eventCard = $('<div/>', { class: 'event-card ms-depth-4', }); eventCard.append(renderSubject(event.subject)); eventCard.append(renderOrganizer(event.organizer)); eventCard.append(renderTimeSpan(event.start, event.end)); const location = renderLocation(event.location); if (location) { eventCard.append(location); } eventCard.appendTo('#tab-container'); }); }
保存更改并重新启动该应用。Save your changes and restart the app. 刷新 Microsoft Teams 中的选项卡。Refresh the tab in Microsoft Teams. 应用在用户日历上显示事件。The app displays events on the user's calendar.