You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Microsoft Graph获取共享日历事件失败求助

无法获取Microsoft Graph中共享日历的事件

我正在尝试实现微软官方的共享日历事件获取示例,使用ASP.NET Core 3.1、Azure AD和Microsoft Graph API。目前已成功获取个人日历事件,以及共享日历的名称、ID、所有者等信息,但始终无法获取共享日历中的事件,也没找到相关可用示例。


现有代码

获取共享日历事件的代码

[Authorize]
[AuthorizeForScopes(ScopeKeySection = "MicrosoftGraph:Scopes")]
public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly GraphServiceClient graphServiceClient;
    private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;

    private string[] _graphScopes;

    public HomeController(ILogger<HomeController> logger, GraphServiceClient gsc,
                        MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler
        )
    {
        _logger = logger;
        graphServiceClient = gsc;

        this._consentHandler = consentHandler;

        _graphScopes = Startup.ConfigurationInstance.GetSection("MicrosoftGraph")["Scopes"].Split(" ");
    }

    [AuthorizeForScopes(ScopeKeySection = "MicrosoftGraph:Scopes")]
    public IActionResult Index()
    {
        User currentUser = null;

        try
        {
            currentUser = graphServiceClient.Me.Request().GetAsync().Result;
        }
        // Catch CAE exception from Graph SDK
        catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge"))
        {
            try
            {
                Console.WriteLine($"{svcex}");
                string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
                _consentHandler.ChallengeUser(_graphScopes, claimChallenge);
                return new EmptyResult();
            }
            catch (Exception ex2)
            {
                _consentHandler.HandleException(ex2);
            }                
        }

        var viewOptions = new List<QueryOption>
            {
                new QueryOption("startDateTime", DateTime.Today.ToString("o")),
                new QueryOption("endDateTime", DateTime.Today.AddDays(20).ToString("o"))
            };

        var sha = graphServiceClient.Me.Calendars
                                       .Request()
                                       .Select(e => e.Id)
                                       .GetAsync().Result.ToList();

        var x = new List<Event>();

        foreach(var row in sha)
        {
            var id = row.Id;
            var data = graphServiceClient.Me.Calendars[id].Request(viewOptions)
                .Select(e => e.Events).GetAsync().Result;
            ICalendarEventsCollectionPage d1 = data != null? data.Events: null;

            if (d1 != null)
            {
                x.AddRange(d1.ToList());
            }
        }
    }
}

这段代码返回的事件始终为null。

尝试直接访问其他用户日历的代码

var x = graphServiceClient.Users["<email>"]
        .CalendarView
        .Request(viewOptions)
        .Select(e => new
        {
            e.Subject,
            e.Organizer,
            e.Start,
            e.End
        }).GetAsync().Result;

执行后返回access denied(访问被拒绝)。


配置文件(appsettings.json)

"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<domain>",
    "TenantId": "<id>",
    "ClientId": "<id>",
    "CallbackPath": "/signin-oidc",
    "SignedOutCallbackPath": "/signout-callback-oidc",
    "ClientSecret": "<secret>"
  },
  "AllowedHosts": "*",
  "MicrosoftGraph": {
    "Scopes": "User.Read MailboxSettings.ReadWrite Calendars.ReadWrite.Shared",
    "BaseUrl": "https://graph.microsoft.com/v1.0"
  }

解决方案

1. 修正共享日历事件的获取方式

你当前通过graphServiceClient.Me.Calendars[id].Request(viewOptions).Select(e => e.Events)获取事件的方式不正确——Calendars[id]返回的是日历对象,直接Select(e => e.Events)不会自动加载事件列表。正确的做法是直接调用CalendarView的请求方法:

foreach(var row in sha)
{
    var id = row.Id;
    // 直接请求该日历下的CalendarView事件
    var eventsPage = graphServiceClient.Me.Calendars[id].CalendarView
        .Request(viewOptions)
        .Select(e => new {
            e.Subject,
            e.Organizer,
            e.Start,
            e.End
        })
        .GetAsync().Result;
    
    if (eventsPage != null)
    {
        x.AddRange(eventsPage.ToList());
    }
}

CalendarView必须指定startDateTime和endDateTime参数,你已经定义的viewOptions可以直接复用。

2. 解决访问其他用户日历的权限问题

直接访问Users["<email>"].CalendarView需要额外权限:

  • 委派权限(用户登录场景)下,需添加Calendars.Read或Calendars.ReadWrite权限(Calendars.ReadWrite.Shared仅针对已共享给当前用户的日历);
  • 确保管理员已同意该租户级权限,或用户登录时已授权该权限。

如果目标是已共享给当前用户的日历,更推荐通过Me.Calendars找到对应日历ID再获取事件,这种方式权限要求更低。

3. 更新权限范围

当前你的Scopes仅包含Calendars.ReadWrite.Shared,若要访问其他用户的完整日历,需补充Calendars.Read权限。建议更新配置:

"Scopes": "User.Read MailboxSettings.ReadWrite Calendars.ReadWrite.Shared Calendars.Read"

更新后需让用户重新登录,以获取包含新权限的令牌。

4. 替换同步调用为异步模式

代码中使用.Result阻塞异步调用可能导致死锁,建议改为async/await模式:

[AuthorizeForScopes(ScopeKeySection = "MicrosoftGraph:Scopes")]
public async Task<IActionResult> Index()
{
    User currentUser = null;

    try
    {
        currentUser = await graphServiceClient.Me.Request().GetAsync();
    }
    // Catch CAE exception from Graph SDK
    catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge"))
    {
        try
        {
            Console.WriteLine($"{svcex}");
            string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
            _consentHandler.ChallengeUser(_graphScopes, claimChallenge);
            return new EmptyResult();
        }
        catch (Exception ex2)
        {
            _consentHandler.HandleException(ex2);
        }                
    }

    var viewOptions = new List<QueryOption>
        {
            new QueryOption("startDateTime", DateTime.Today.ToString("o")),
            new QueryOption("endDateTime", DateTime.Today.AddDays(20).ToString("o"))
        };

    var sha = await graphServiceClient.Me.Calendars
                                   .Request()
                                   .Select(e => e.Id)
                                   .GetAsync();

    var x = new List<Event>();

    foreach(var row in sha)
    {
        var id = row.Id;
        var eventsPage = await graphServiceClient.Me.Calendars[id].CalendarView
            .Request(viewOptions)
            .Select(e => new {
                e.Subject,
                e.Organizer,
                e.Start,
                e.End
            })
            .GetAsync();
        
        if (eventsPage != null)
        {
            x.AddRange(eventsPage.ToList());
        }
    }

    return View(x);
}

内容的提问来源于stack exchange,提问作者Eugene Chepurnyy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 18:10:32