Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Chiamare Microsoft Graph dalle applicazioni ASP.NET Core e OWIN usando Microsoft. Identity.Web e Microsoft Graph SDK per accedere ai dati e ai servizi Microsoft 365.
Informazioni sull'integrazione di Microsoft Graph
Microsoft Graph fornisce un endpoint API unificato per l'accesso ai dati tra Microsoft 365, Windows e Enterprise Mobility + Security. Microsoft. Identity.Web semplifica l'autenticazione e l'acquisizione di token per Microsoft Graph, mentre Microsoft Graph SDK offre un'API fluente e tipizzata per chiamare gli endpoint Graph.
Scegliere Microsoft. Identity.Web.GraphServiceClient
I vantaggi seguenti rendono Microsoft. Identity.Web.GraphServiceClient l'approccio consigliato per chiamare Microsoft Graph.
- Acquisizione automatica dei token: gestisce facilmente i token utente e app
- Memorizzazione nella cache dei token: caching integrato per migliorare le prestazioni
- API Fluent: chiamate Graph con sicurezza tipologica e compatibili con IntelliSense
- Consenso incrementale: richiedere ambiti aggiuntivi su richiesta
- Più schemi di autenticazione: supporto per app Web e API Web
- Sia la versione 1.0 che la Beta: si usano insieme endpoint stabili e di anteprima
Installare i pacchetti necessari
Installare il pacchetto di integrazione Microsoft Graph SDK:
dotnet add package Microsoft.Identity.Web.GraphServiceClient
Per le API beta Microsoft Graph:
dotnet add package Microsoft.Identity.Web.GraphServiceClientBeta
Configurare ASP.NET Core
1. Configurare i servizi
Aggiungi il supporto di Microsoft Graph alla tua applicazione:
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. Configurare appsettings.json
Configurare le opzioni graph nel file di configurazione:
{
"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"]
}
}
}
Configurazione con codice:
builder.Services.AddMicrosoftGraph(options =>
{
builder.Configuration.GetSection("DownstreamApis:MicrosoftGraph").Bind(options);
});
Oppure configurare direttamente nel codice:
builder.Services.AddMicrosoftGraph();
builder.Services.Configure<MicrosoftGraphOptions>(options =>
{
options.BaseUrl = "https://graph.microsoft.com/v1.0";
options.Scopes = new[] { "User.Read", "Mail.Read" };
});
3. Configurare il supporto cloud nazionale
Per usare Microsoft Graph nei cloud nazionali, specificare BaseUrl nella configurazione:
{
"DownstreamApis": {
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.us/v1.0",
"Scopes": ["User.Read"]
}
}
}
Consulta Distribuzioni di Microsoft Graph per gli URL degli endpoint.
Usare GraphServiceClient
Inserire GraphServiceClient
GraphServiceClient Inserire dal costruttore:
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);
}
}
Usare le autorizzazioni delegate (token utente)
Chiamare Graph per conto dell'utente connesso con autorizzazioni delegate.
Recuperare il profilo utente di base
Recuperare le informazioni del profilo dell'utente corrente da 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
});
}
}
Richiedere il consenso incrementale
Richiedere ambiti aggiuntivi in modo dinamico quando l'applicazione ne necessita:
[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;
}
}
}
Applicare le opzioni di query
Usare le opzioni di query di Graph SDK per filtrare, selezionare e ordinare i risultati:
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);
}
Scorrere i risultati
Gestisci i risultati paginati da Microsoft Graph iterando attraverso ogni pagina.
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);
}
Usare le autorizzazioni dell'applicazione (token solo per app)
Chiamare Graph con le autorizzazioni dell'applicazione quando non è necessario alcun contesto utente.
Chiamare Graph con WithAppOnly()
Usare il WithAppOnly() metodo per effettuare chiamate Graph con autorizzazioni dell'applicazione.
[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);
}
}
Configurare le autorizzazioni dell'app
Specificare una richiesta di token dell'app in appsettings.json:
{
"DownstreamApis": {
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"RequestAppToken": true
}
}
}
Gli ambiti verranno impostati automaticamente su ["https://graph.microsoft.com/.default"].
Configurare opzioni dettagliate solo per app
Impostare opzioni esplicite di autenticazione solo app nel codice.
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);
}
Gestire più schemi di autenticazione
Se l'app usa più schemi di autenticazione (ad esempio, app Web e API), specificare lo schema da usare:
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);
}
}
Configurare le opzioni dettagliate dello schema
Impostare lo schema di autenticazione e gli ambiti in modo esplicito nel codice.
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);
}
Usare sia gli endpoint v1.0 che Beta
Registrare e chiamare sia Microsoft Graph versione 1.0 che beta nella stessa applicazione.
1. Installare entrambi i pacchetti
dotnet add package Microsoft.Identity.Web.GraphServiceClient
dotnet add package Microsoft.Identity.Web.GraphServiceClientBeta
2. Registrare entrambi i servizi
using Microsoft.Identity.Web;
builder.Services.AddMicrosoftGraph();
builder.Services.AddMicrosoftGraphBeta();
3. Usare entrambi i client
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 });
}
}
Inviare richieste in batch
Combinare più chiamate Graph in una singola richiesta HTTP per migliorare le prestazioni:
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
});
}
Applicare modelli comuni di grafo
Usare questi modelli per eseguire operazioni frequenti Microsoft Graph nell'applicazione.
Recupera il responsabile dell'utente
Recuperare il responsabile dell'utente autenticato dalla directory.
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");
}
Ottenere la foto dell'utente
Esportare la foto del profilo dell'utente connesso in formato di flusso di dati.
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");
}
}
Invia messaggio di posta elettronica
Inviare un messaggio di posta elettronica per conto dell'utente connesso.
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");
}
Creare un evento del calendario
Creare un nuovo evento del calendario con i partecipanti per l'utente connesso.
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);
}
Cerca utenti
Cercare gli utenti nella directory in base al nome visualizzato o all'indirizzo di posta elettronica.
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);
}
Implementare il supporto OWIN
Per applicazioni ASP.NET che usano OWIN, configurare la fabbrica di acquisizione dei token e registrare i servizi di 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. Chiamare l'API dai controller
Recuperare un'istanza di GraphServiceClient nel controller e chiamare 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);
}
}
Eseguire la migrazione da Microsoft. Identity.Web.MicrosoftGraph 2.x
Se stai eseguendo la migrazione dal vecchio pacchetto Microsoft.Identity.Web.MicrosoftGraph (SDK 4.x), esamina le seguenti modifiche chiave:
1. Rimuovere il pacchetto precedente e aggiungere nuovo
dotnet remove package Microsoft.Identity.Web.MicrosoftGraph
dotnet add package Microsoft.Identity.Web.GraphServiceClient
2. Aggiornare le chiamate al metodo
Il .Request() metodo è stato rimosso in SDK 5.x:
Before (SDK 4.x):
var user = await _graphClient.Me.Request().GetAsync();
var messages = await _graphClient.Me.Messages
.Request()
.WithScopes("Mail.Read")
.GetAsync();
Dopo (SDK 5.x):
var user = await _graphClient.Me.GetAsync();
var messages = await _graphClient.Me.Messages
.GetAsync(r => r.Options.WithScopes("Mail.Read"));
3. Aggiornare la posizione 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. Aggiornare il percorso WithAppOnly()
Before:
var apps = await _graphClient.Applications
.Request()
.WithAppOnly()
.GetAsync();
After:
var apps = await _graphClient.Applications
.GetAsync(r => r.Options.WithAppOnly());
5. Aggiornare conAuthenticationScheme() location
Before:
var user = await _graphClient.Me
.Request()
.WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme)
.GetAsync();
After:
var user = await _graphClient.Me
.GetAsync(r => r.Options
.WithAuthenticationScheme(JwtBearerDefaults.AuthenticationScheme));
Per informazioni dettagliate sulla migrazione, vedere Microsoft Graph .NET SDK v5 changelog.
Gestire gli errori
Gestire ServiceException
Intercettare ODataError e MicrosoftIdentityWebChallengeUserException per gestire correttamente gli errori di 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");
}
}
Seguire le migliori pratiche
Richiedere livelli minimi di accesso
Richiedi solo gli ambiti di cui hai bisogno.
// 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" };
2. Usare il consenso incrementale
Richiedere ambiti aggiuntivi solo quando necessario:
// Sign-in: Only User.Read
// Later, when accessing mail:
var messages = await _graphClient.Me.Messages
.GetAsync(r => r.Options.WithScopes("Mail.Read"));
3. Cache di GraphServiceClient
GraphServiceClient è sicuro da riutilizzare. Registra come singleton o inserisce da DI.
4. Usare select per ridurre le dimensioni della risposta
// 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" });
Risolvere i problemi comuni
Risolvere "Privilegi insufficienti per completare l'operazione"
Causa: l'app non dispone delle autorizzazioni graph necessarie.
Soluzione:
- Aggiungere le autorizzazioni API necessarie nella registrazione dell'app
- Consenso amministratore necessario per le autorizzazioni dell'app
- Consenso utente necessario per le autorizzazioni delegate
Risolvere "AADSTS65001: l'utente o l'amministratore non ha acconsentito"
Causa: l'utente non ha acconsentito agli ambiti richiesti.
Soluzione: usare il consenso incrementale con .WithScopes() per attivare il flusso di consenso.
Risolvere gli errori 404 delle foto
Causa: l'utente non ha una foto del profilo.
Soluzione: gestire correttamente 404 e fornire avatar predefinito.
Risolvere gli errori delle richieste batch
Causa: le singole richieste in batch possono avere esito negativo in modo indipendente.
Soluzione: controllare ogni risposta in batch per verificare la presenza di errori:
var userResponse = await batchResponse.GetResponseByIdAsync<User>(userRequestId);
if (userResponse == null)
{
// Handle individual request failure
}
Contenuti correlati
- Documentazione di Microsoft Graph
- Guida alla migrazione di Graph SDK v5
- Panoramica della chiamata alle API downstream
- Chiamate dalle App Web
- Chiamata da API Web
Successivi passaggi: Scopri come chiamare gli SDK di Azure o usare le API personalizzate.