Appeler Microsoft Graph

Appelez Microsoft Graph à partir de vos applications ASP.NET Core et OWIN à l’aide de Microsoft. Identity.Web et le Kit de développement logiciel (SDK) Microsoft Graph pour accéder à Microsoft 365 données et services.

Comprendre l’intégration de Microsoft Graph

Microsoft Graph fournit un point de terminaison d’API unifié pour accéder aux données entre Microsoft 365, Windows et Enterprise Mobility + Security. Microsoft.Identity.Web simplifie l’authentification et l’acquisition de jetons pour Microsoft Graph, tandis que le SDK Microsoft Graph fournit une API fluide et typée pour appeler des points de terminaison du Graph.

Choisissez Microsoft. Identity.Web.GraphServiceClient

Les avantages suivants rendent Microsoft. Identity.Web.GraphServiceClient l’approche recommandée pour appeler Microsoft Graph.

  • Acquisition automatique de jetons : gère en toute transparence les jetons d’utilisateur et d’application
  • Mise en cache des jetons : mise en cache intégrée pour les performances
  • API Fluent : Appels Graph permettant la sécurité de type et le soutien d'IntelliSense
  • Consentement incrémentiel : demander des étendues supplémentaires à la demande
  • Schémas d’authentification multiples : prise en charge des applications web et des API web
  • V1.0 et Bêta : Utiliser ensemble des points de connexion stables et en préversion

Installer les packages requis

Installez le package d’intégration Microsoft Graph SDK :

dotnet add package Microsoft.Identity.Web.GraphServiceClient

Pour les API bêta Microsoft Graph :

dotnet add package Microsoft.Identity.Web.GraphServiceClientBeta

Configurer ASP.NET Core

1. Configurer les services

Ajoutez la prise en charge de Microsoft Graph à votre application.

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Add authentication (web app or web API)
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

// Add Microsoft Graph support
builder.Services.AddMicrosoftGraph();

builder.Services.AddControllersWithViews();

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

2. Configurer appsettings.json

Configurez les options Graph dans votre fichier de configuration :

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",
    "ClientSecret": "your-client-secret",
    "CallbackPath": "/signin-oidc"
  },
  "DownstreamApis": {
    "MicrosoftGraph": {
      "BaseUrl": "https://graph.microsoft.com/v1.0",
      "Scopes": ["User.Read", "User.ReadBasic.All"]
    }
  }
}

Configuration avec du code :

builder.Services.AddMicrosoftGraph(options =>
{
    builder.Configuration.GetSection("DownstreamApis:MicrosoftGraph").Bind(options);
});

Ou configurez directement dans le code :

builder.Services.AddMicrosoftGraph();
builder.Services.Configure<MicrosoftGraphOptions>(options =>
{
    options.BaseUrl = "https://graph.microsoft.com/v1.0";
    options.Scopes = new[] { "User.Read", "Mail.Read" };
});

3. Configurer la prise en charge du cloud national

Pour utiliser Microsoft Graph dans les clouds nationaux, spécifiez la baseUrl dans votre configuration :

{
  "DownstreamApis": {
    "MicrosoftGraph": {
      "BaseUrl": "https://graph.microsoft.us/v1.0",
      "Scopes": ["User.Read"]
    }
  }
}

Consultez Microsoft Graph déploiements pour les URL de point de terminaison.

Utiliser GraphServiceClient

Injecter GraphServiceClient

Injectez GraphServiceClient depuis le constructeur :

using Microsoft.Graph;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize]
public class ProfileController : Controller
{
    private readonly GraphServiceClient _graphClient;
    
    public ProfileController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }
    
    public async Task<IActionResult> Index()
    {
        // Call Microsoft Graph
        var user = await _graphClient.Me.GetAsync();
        return View(user);
    }
}

Utiliser des autorisations déléguées (jetons utilisateur)

Appelez Graph pour le compte de l’utilisateur connecté avec des autorisations déléguées.

Récupérer le profil utilisateur de base

Récupérez les informations de profil de l'utilisateur actuel à partir de Microsoft Graph.

[Authorize]
public class ProfileController : Controller
{
    private readonly GraphServiceClient _graphClient;
    
    public ProfileController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }
    
    public async Task<IActionResult> Me()
    {
        // Get current user's profile
        var user = await _graphClient.Me.GetAsync();
        
        return View(new UserViewModel
        {
            DisplayName = user.DisplayName,
            Mail = user.Mail,
            JobTitle = user.JobTitle
        });
    }
}

Demandez des étendues supplémentaires dynamiquement lorsque votre application en a besoin :

[Authorize]
[AuthorizeForScopes("Mail.Read")]
public class MailController : Controller
{
    private readonly GraphServiceClient _graphClient;
    
    public MailController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }
    
    public async Task<IActionResult> Inbox()
    {
        try
        {
            // Request Mail.Read scope dynamically
            var messages = await _graphClient.Me.Messages
                .GetAsync(r => r.Options.WithScopes("Mail.Read"));
            
            return View(messages);
        }
        catch (MicrosoftIdentityWebChallengeUserException)
        {
            // ASP.NET Core will redirect user to consent
            // thansk to the AuthorizeForScopes attribute.
            throw;
        }
    }
}

Appliquer des options de requête

Utilisez les options de requête du Kit de développement logiciel (SDK) Graph pour filtrer, sélectionner et classer les résultats :

public async Task<IActionResult> UnreadMessages()
{
    var messages = await _graphClient.Me.Messages
        .GetAsync(requestConfiguration =>
        {
            requestConfiguration.QueryParameters.Filter = "isRead eq false";
            requestConfiguration.QueryParameters.Select = new[] { "subject", "from", "receivedDateTime" };
            requestConfiguration.QueryParameters.Orderby = new[] { "receivedDateTime desc" };
            requestConfiguration.QueryParameters.Top = 10;
            
            // Request specific scope
            requestConfiguration.Options.WithScopes("Mail.Read");
        });
    
    return View(messages);
}

Feuilleter les résultats

Gérez les résultats paginés de Microsoft Graph en itérant à travers chaque page :

public async Task<IActionResult> AllUsers()
{
    var allUsers = new List<User>();
    
    // Get first page
    var users = await _graphClient.Users
        .GetAsync(r => r.Options.WithScopes("User.ReadBasic.All"));
    
    // Add first page
    allUsers.AddRange(users.Value);
    
    // Iterate through remaining pages
    var pageIterator = PageIterator<User, UserCollectionResponse>
        .CreatePageIterator(
            _graphClient,
            users,
            user =>
            {
                allUsers.Add(user);
                return true; // Continue iteration
            });
    
    await pageIterator.IterateAsync();
    
    return View(allUsers);
}

Utiliser des autorisations d’application (jetons d’application uniquement)

Appelez Graph avec des autorisations d’application quand aucun contexte utilisateur n’est requis.

Appeler Graph avec WithAppOnly()

Utilisez la WithAppOnly() méthode pour effectuer des appels Graph avec des autorisations d’application.

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
    private readonly GraphServiceClient _graphClient;
    
    public AdminController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }
    
    [HttpGet("users/count")]
    public async Task<ActionResult<int>> GetUserCount()
    {
        // Get count using app permissions
        var count = await _graphClient.Users.Count
            .GetAsync(r => r.Options.WithAppOnly());
        
        return Ok(count);
    }
    
    [HttpGet("applications")]
    public async Task<ActionResult> GetApplications()
    {
        // List applications using app permissions
        var apps = await _graphClient.Applications
            .GetAsync(r => r.Options.WithAppOnly());
        
        return Ok(apps.Value);
    }
}

Configuration des autorisations d'application

Spécifiez une demande de jeton d’application dans appsettings.json:

{
  "DownstreamApis": {
    "MicrosoftGraph": {
      "BaseUrl": "https://graph.microsoft.com/v1.0",
      "RequestAppToken": true
    }
  }
}

Les étendues sont automatiquement définies sur ["https://graph.microsoft.com/.default"].

Configurer des options détaillées d’application uniquement

Définissez des options d’authentification explicites d’application uniquement dans le code.

public async Task<IActionResult> GetApplicationsDetailed()
{
    var apps = await _graphClient.Applications
        .GetAsync(r =>
        {
            r.Options.WithAuthenticationOptions(options =>
            {
                // Request app token explicitly
                options.RequestAppToken = true;
                
                // Scopes automatically become [.default]
                // No need to specify: options.Scopes = new[] { "https://graph.microsoft.com/.default" };
            });
        });
    
    return Ok(apps);
}

Gérer plusieurs schémas d’authentification

Si votre application utilise plusieurs schémas d’authentification (par exemple, application web + API), spécifiez le schéma à utiliser :

using Microsoft.AspNetCore.Authentication.JwtBearer;

[Authorize]
public class ApiDataController : ControllerBase
{
    private readonly GraphServiceClient _graphClient;
    
    public ApiDataController(GraphServiceClient graphClient)
    {
        _graphClient = graphClient;
    }
    
    [HttpGet("profile")]
    public async Task<ActionResult> GetProfile()
    {
        // Specify JWT Bearer scheme
        var user = await _graphClient.Me
            .GetAsync(r => r.Options
                .WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme));
        
        return Ok(user);
    }
}

Configurer des options de schéma détaillées

Définissez explicitement le schéma d’authentification et les étendues dans le code.

public async Task<ActionResult> GetMailWithScheme()
{
    var messages = await _graphClient.Me.Messages
        .GetAsync(r =>
        {
            r.Options.WithAuthenticationOptions(options =>
            {
                // Specify authentication scheme
                options.AcquireTokenOptions.AuthenticationOptionsName = 
                    JwtBearerDefaults.AuthenticationScheme;
                
                // Specify scopes
                options.Scopes = new[] { "Mail.Read" };
            });
        });
    
    return Ok(messages);
}

Utiliser les points de terminaison v1.0 et Bêta

Inscrivez et appelez Microsoft Graph version 1.0 et bêta dans la même application.

1. Installer les deux packages

dotnet add package Microsoft.Identity.Web.GraphServiceClient
dotnet add package Microsoft.Identity.Web.GraphServiceClientBeta

2. Inscrire les deux services

using Microsoft.Identity.Web;

builder.Services.AddMicrosoftGraph();
builder.Services.AddMicrosoftGraphBeta();

3. Utiliser les deux clients

using GraphServiceClient = Microsoft.Graph.GraphServiceClient;
using GraphBetaServiceClient = Microsoft.Graph.Beta.GraphServiceClient;

public class MyController : Controller
{
    private readonly GraphServiceClient _graphClient;
    private readonly GraphBetaServiceClient _graphBetaClient;
    
    public MyController(
        GraphServiceClient graphClient,
        GraphBetaServiceClient graphBetaClient)
    {
        _graphClient = graphClient;
        _graphBetaClient = graphBetaClient;
    }
    
    public async Task<IActionResult> GetData()
    {
        // Use stable v1.0 endpoint
        var user = await _graphClient.Me.GetAsync();
        
        // Use beta endpoint for preview features
        var profile = await _graphBetaClient.Me.Profile.GetAsync();
        
        return View(new { user, profile });
    }
}

Envoyer des requêtes par lots

Combinez plusieurs appels Graph dans une seule requête HTTP pour améliorer les performances :

using Microsoft.Graph.Models;

public async Task<IActionResult> GetDashboard()
{
    var batchRequestContent = new BatchRequestContentCollection(_graphClient);
    
    // Add multiple requests to batch
    var userRequest = _graphClient.Me.ToGetRequestInformation();
    var messagesRequest = _graphClient.Me.Messages.ToGetRequestInformation();
    var eventsRequest = _graphClient.Me.Events.ToGetRequestInformation();
    
    var userRequestId = await batchRequestContent.AddBatchRequestStepAsync(userRequest);
    var messagesRequestId = await batchRequestContent.AddBatchRequestStepAsync(messagesRequest);
    var eventsRequestId = await batchRequestContent.AddBatchRequestStepAsync(eventsRequest);
    
    // Send batch request
    var batchResponse = await _graphClient.Batch.PostAsync(batchRequestContent);
    
    // Extract responses
    var user = await batchResponse.GetResponseByIdAsync<User>(userRequestId);
    var messages = await batchResponse.GetResponseByIdAsync<MessageCollectionResponse>(messagesRequestId);
    var events = await batchResponse.GetResponseByIdAsync<EventCollectionResponse>(eventsRequestId);
    
    return View(new DashboardViewModel 
    { 
        User = user,
        Messages = messages.Value,
        Events = events.Value
    });
}

Appliquer des modèles Graph courants

Utilisez ces modèles pour effectuer des opérations de Microsoft Graph fréquentes dans votre application.

Obtenir le responsable de l’utilisateur

Récupérez le gestionnaire de l’utilisateur connecté à partir du répertoire.

public async Task<IActionResult> GetManager()
{
    var manager = await _graphClient.Me.Manager.GetAsync();
    
    // Cast to User (manager is DirectoryObject)
    if (manager is User managerUser)
    {
        return View(managerUser);
    }
    
    return NotFound("Manager not found");
}

Obtenir la photo de l’utilisateur

Téléchargez la photo de profil de l'utilisateur connecté sous forme de flux de données.

public async Task<IActionResult> GetPhoto()
{
    try
    {
        var photoStream = await _graphClient.Me.Photo.Content.GetAsync();
        
        return File(photoStream, "image/jpeg");
    }
    catch (ServiceException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
    {
        return NotFound("Photo not available");
    }
}

Envoyer un e-mail

Envoyez un e-mail pour le compte de l’utilisateur connecté.

public async Task<IActionResult> SendEmail([FromBody] EmailRequest request)
{
    var message = new Message
    {
        Subject = request.Subject,
        Body = new ItemBody
        {
            ContentType = BodyType.Html,
            Content = request.Body
        },
        ToRecipients = new List<Recipient>
        {
            new Recipient
            {
                EmailAddress = new EmailAddress
                {
                    Address = request.ToEmail
                }
            }
        }
    };
    
    await _graphClient.Me.SendMail
        .PostAsync(new SendMailPostRequestBody
        {
            Message = message,
            SaveToSentItems = true
        },
        requestConfiguration =>
        {
            requestConfiguration.Options.WithScopes("Mail.Send");
        });
    
    return Ok("Email sent");
}

Créer un événement de calendrier

Créez un événement de calendrier avec les participants pour l’utilisateur connecté.

public async Task<IActionResult> CreateEvent([FromBody] EventRequest request)
{
    var newEvent = new Event
    {
        Subject = request.Subject,
        Start = new DateTimeTimeZone
        {
            DateTime = request.StartTime.ToString("yyyy-MM-ddTHH:mm:ss"),
            TimeZone = "UTC"
        },
        End = new DateTimeTimeZone
        {
            DateTime = request.EndTime.ToString("yyyy-MM-ddTHH:mm:ss"),
            TimeZone = "UTC"
        },
        Attendees = request.Attendees.Select(email => new Attendee
        {
            EmailAddress = new EmailAddress { Address = email },
            Type = AttendeeType.Required
        }).ToList()
    };
    
    var createdEvent = await _graphClient.Me.Events
        .PostAsync(newEvent, r => r.Options.WithScopes("Calendars.ReadWrite"));
    
    return Ok(createdEvent);
}

Rechercher des utilisateurs

Recherchez les utilisateurs dans le répertoire par nom complet ou adresse e-mail.

public async Task<IActionResult> SearchUsers(string searchTerm)
{
    var users = await _graphClient.Users
        .GetAsync(requestConfiguration =>
        {
            requestConfiguration.QueryParameters.Filter = 
                $"startswith(displayName,'{searchTerm}') or startswith(mail,'{searchTerm}')";
            requestConfiguration.QueryParameters.Select = 
                new[] { "displayName", "mail", "jobTitle" };
            requestConfiguration.QueryParameters.Top = 10;
            
            requestConfiguration.Options.WithScopes("User.ReadBasic.All");
        });
    
    return Ok(users.Value);
}

Implémenter le support technique OWIN

Pour les applications ASP.NET qui utilisent OWIN, configurez la fabrique d’acquisition de jetons et inscrivez les services Microsoft Graph.

using Microsoft.Identity.Web;
using Microsoft.Identity.Web.OWIN;
using Owin;

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
      OwinTokenAcquirerFactory factory = TokenAcquirerFactory.GetDefaultInstance<OwinTokenAcquirerFactory>();
      app.AddMicrosoftIdentityWebApi(factory);
      factory.Services
        .AddMicrosoftGraph();
      factory.Build();
    }
}

2. Appeler l’API à partir de contrôleurs

Récupérez une instance GraphServiceClient dans votre contrôleur et appelez Microsoft Graph.

using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;
using System.Web.Http;

[Authorize]
public class DataController : ApiController
{
    public DataController()
    {
    }

    public async Task<IHttpActionResult> GetMyProfile()
    {
        GraphServiceClient graphServiceClient = this.GetGraphServiceClient();
        var me = await graphServiceClient.Me.GetAsync();
        return Ok(me);
    }
}

Migrez à partir de Microsoft. Identity.Web.MicrosoftGraph 2.x

Si vous effectuez une migration à partir de la version plus ancienne du package Microsoft.Identity.Web.MicrosoftGraph (SDK 4.x), examinez les modifications clés suivantes :

1. Supprimer l’ancien package et ajouter nouveau

dotnet remove package Microsoft.Identity.Web.MicrosoftGraph
dotnet add package Microsoft.Identity.Web.GraphServiceClient

2. Mettre à jour les appels de méthode

La .Request() méthode a été supprimée dans le Kit de développement logiciel (SDK) 5.x :

Avant (SDK 4.x) :

var user = await _graphClient.Me.Request().GetAsync();

var messages = await _graphClient.Me.Messages
    .Request()
    .WithScopes("Mail.Read")
    .GetAsync();

Après (SDK 5.x) :

var user = await _graphClient.Me.GetAsync();

var messages = await _graphClient.Me.Messages
    .GetAsync(r => r.Options.WithScopes("Mail.Read"));

3. Mettre à jour l’emplacement WithScopes()

Before:

var users = await _graphClient.Users
    .Request()
    .WithScopes("User.Read.All")
    .GetAsync();

After:

var users = await _graphClient.Users
    .GetAsync(r => r.Options.WithScopes("User.Read.All"));

4. Mettre à jour l’emplacement WithAppOnly()

Before:

var apps = await _graphClient.Applications
    .Request()
    .WithAppOnly()
    .GetAsync();

After:

var apps = await _graphClient.Applications
    .GetAsync(r => r.Options.WithAppOnly());

5. Mettre à jour l’emplacement WithAuthenticationScheme()

Before:

var user = await _graphClient.Me
    .Request()
    .WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme)
    .GetAsync();

After:

var user = await _graphClient.Me
    .GetAsync(r => r.Options
        .WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme));

Pour plus d’informations sur la migration, consultez Microsoft Graph .NET sdk v5 changelog.

Gérer les erreurs

Gérer l'exception ServiceException

Interceptez ODataError et MicrosoftIdentityWebChallengeUserException pour gérer avec élégance les défaillances de l'API Graph.

using Microsoft.Graph.Models.ODataErrors;

public async Task<IActionResult> GetData()
{
    try
    {
        var user = await _graphClient.Me.GetAsync();
        return Ok(user);
    }
    catch (ODataError ex) when (ex.ResponseStatusCode == 404)
    {
        return NotFound("Resource not found");
    }
    catch (ODataError ex) when (ex.ResponseStatusCode == 403)
    {
        return Forbid("Insufficient permissions");
    }
    catch (MicrosoftIdentityWebChallengeUserException)
    {
        // User needs to consent
        throw;
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Graph API call failed");
        return StatusCode(500, "An error occurred");
    }
}

Suivre les bonnes pratiques

1. Demander des étendues minimales

Demandez uniquement les périmètres dont vous avez besoin :

//  Bad: Requesting too many scopes
options.Scopes = new[] { "User.Read", "Mail.ReadWrite", "Calendars.ReadWrite", "Files.ReadWrite.All" };

//  Good: Request only what you need
options.Scopes = new[] { "User.Read" };

Demandez des étendues supplémentaires uniquement si nécessaire :

// Sign-in: Only User.Read
// Later, when accessing mail:
var messages = await _graphClient.Me.Messages
    .GetAsync(r => r.Options.WithScopes("Mail.Read"));

3. Cache le GraphServiceClient

GraphServiceClient est sûr à réutiliser. Inscrivez-vous en tant que singleton ou injectez à partir de DI.

4. Utiliser la sélection pour réduire la taille de réponse

//  Bad: Getting all properties
var users = await _graphClient.Users.GetAsync();

//  Good: Select only needed properties
var users = await _graphClient.Users
    .GetAsync(r => r.QueryParameters.Select = 
        new[] { "displayName", "mail", "id" });

Résoudre les problèmes courants

Résolvez « Privilèges insuffisants pour terminer l’opération »

Cause : l’application n’a pas d’autorisations Graph requises.

Solution:

  • Ajouter des autorisations d’API requises dans l’inscription d’application
  • Consentement administrateur requis pour les autorisations d’application
  • Consentement de l’utilisateur requis pour les autorisations déléguées

Résolvez « AADSTS65001 : l’utilisateur ou l’administrateur n’a pas consenti »

Cause : l’utilisateur n’a pas consenti aux étendues demandées.

Solution : utilisez le consentement incrémentiel avec .WithScopes() pour déclencher le flux de consentement.

Résoudre les erreurs photo 404

Cause : l’utilisateur n’a pas de photo de profil.

Solution : gérez correctement 404 et fournissez un avatar par défaut.

Résoudre les défaillances de requêtes par lots

Cause : les requêtes individuelles par lots peuvent échouer indépendamment.

Solution : vérifiez chaque réponse par lot pour les erreurs :

var userResponse = await batchResponse.GetResponseByIdAsync<User>(userRequestId);
if (userResponse == null)
{
    // Handle individual request failure
}

Next Steps : En savoir plus sur calling SDK Azure ou custom API.