使用Microsoft Graph C#创建O365日历事件无报错但失败求助
问题排查:Microsoft Graph创建日历事件无报错但未生成
问题描述
使用管理员账号通过GraphServiceClient为O365用户日历创建会议事件,代码执行无报错,但操作账号及参会者账号中均未生成该事件,已确认Azure门户中用户拥有Calendars.ReadWrite权限。
代码示例
try { var scopes = new[] { "Calendars.ReadWrite.All" }; // Multi-tenant apps can use "common", // single-tenant apps must use the tenant ID from the Azure portal var tenantId = "common"; Configuracoes Dados = new Configuracoes(); // Values from app registration string clientId = Dados.EMailCV.ToString(); string clientSecret = Dados.PasswordCV.ToString(); // using Azure.Identity; var options = new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud }; // This is the incoming token to exchange using on-behalf-of flow var oboToken = "JWT_TOKEN_TO_EXCHANGE"; var cca = ConfidentialClientApplicationBuilder .Create(clientId) .WithTenantId(tenantId) .WithClientSecret(clientSecret) .Build(); // DelegateAuthenticationProvider is a simple auth provider implementation // that allows you to define an async function to retrieve a token // Alternatively, you can create a class that implements IAuthenticationProvider // for more complex scenarios var authProvider = new DelegateAuthenticationProvider(async (request) => { // Use Microsoft.Identity.Client to retrieve token var assertion = new UserAssertion(oboToken); var result = await cca.AcquireTokenOnBehalfOf(scopes, assertion).ExecuteAsync(); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken); }); GraphServiceClient graphClient = new GraphServiceClient(authProvider); var @event = new Event { Subject = "Let's go for lunch", Body = new ItemBody { ContentType = BodyType.Html, Content = "Does noon work for you?" }, Start = new DateTimeTimeZone { DateTime = "2022-11-04T12:00:00", TimeZone = "GMT Standard Time" }, End = new DateTimeTimeZone { DateTime = "2017-04-15T14:00:00", TimeZone = "GMT Standard Time" }, // Location = new Location // { // displayName = "Harry's Bar" // }, Attendees = new List<Attendee>() { new Attendee { EmailAddress = new EmailAddress { Address = "p560@esenviseu.net", Name = "Nuno Barros" }, Type = AttendeeType.Required } }, AllowNewTimeProposals = true, IsOnlineMeeting = true, OnlineMeetingProvider = OnlineMeetingProviderType.TeamsForBusiness }; graphClient.Me.Events .Request() .Header("Prefer", "outlook.timezone=\"GMT Standard Time\"") .AddAsync(@event); } catch (Exception ex) { lblTexto.Text = "Erros!<br>" + ex.Message; } finally { lblTexto.Text = "Sem erros!"; }
核心排查点
1. 异步方法未等待
AddAsync是异步方法,代码中未添加await关键字,导致方法还未执行完成就进入finally块,操作可能被中断。修改方式:
await graphClient.Me.Events .Request() .Header("Prefer", "outlook.timezone=\"GMT Standard Time\"") .AddAsync(@event);
同时确保包含这段代码的方法标记为async。
2. 事件时间逻辑错误
代码中事件的结束时间(2017-04-15)早于开始时间(2022-11-04),这会导致Microsoft Graph API拒绝创建事件。需要修正结束时间为晚于开始时间的合理值。
3. 异常信息被覆盖
finally块直接覆盖了lblTexto.Text的内容,即使catch捕获到异常,用户也看不到错误信息。调整逻辑:
catch (Exception ex) { lblTexto.Text = "Erros!<br>" + ex.Message; } finally { // 仅在未捕获异常时显示成功信息 if (string.IsNullOrEmpty(lblTexto.Text)) { lblTexto.Text = "Sem erros!"; } }
4. OBO令牌有效性
oboToken是硬编码的占位符,实际运行时需传入当前用户的有效JWT令牌,且该令牌需包含足够的委托权限。可以使用JWT解析工具验证令牌的scp声明是否包含Calendars.ReadWrite或Calendars.ReadWrite.All。
5. 权限配置确认
- 确保Azure AD应用注册中添加的是委托权限的
Calendars.ReadWrite.All,而非应用权限; - 确认已完成管理员同意(针对租户级权限);
- 如果仅操作当前用户的日历,
Calendars.ReadWrite权限已足够,无需使用Calendars.ReadWrite.All。
内容的提问来源于stack exchange,提问作者user3330412
相关产品推荐
相关产品推荐

