Criar aplicativos do Microsoft Teams com o Microsoft Graph
Este tutorial ensina como criar um aplicativo do Microsoft Teams usando o ASP.NET Core e a API do Microsoft Graph para recuperar informações de calendário de um usuário.This tutorial teaches you how to build a Microsoft Teams app using ASP.NET Core and the Microsoft Graph API to retrieve calendar information for a user.
Dica
Se preferir baixar o tutorial concluído, você poderá baixar ou clonar o repositório do GitHub.If you prefer to just download the completed tutorial, you can download or clone the GitHub repository. Consulte o arquivo LEIAme na pasta demonstração para obter instruções sobre como configurar o aplicativo com uma ID de aplicativo e segredo.See the README file in the demo folder for instructions on configuring the app with an app ID and secret.
Pré-requisitosPrerequisites
Antes de iniciar este tutorial, você deve ter o seguinte instalado em sua máquina de desenvolvimento.Before you start this tutorial, you should have the following installed on your development machine.
Você também deve ter uma conta pessoal da Microsoft com uma caixa de correio no Outlook.com ou uma conta corporativa ou de estudante da Microsoft.You should also have either a personal Microsoft account with a mailbox on Outlook.com, or a Microsoft work or school account. Se você não tem uma conta da Microsoft, há algumas opções para obter uma conta gratuita:If you don't have a Microsoft account, there are a couple of options to get a free account:
- Você pode se inscrever para uma nova conta pessoal da Microsoft.You can sign up for a new personal Microsoft account.
- Você pode se inscrever no programa para desenvolvedores do office 365 para obter uma assinatura gratuita do Office 365.You can sign up for the Office 365 Developer Program to get a free Office 365 subscription.
Observação
Este tutorial foi escrito com o .NET Core SDK versão 3.1.402.This tutorial was written with .NET Core SDK version 3.1.402. As etapas deste guia podem funcionar com outras versões, mas que não foram testadas.The steps in this guide may work with other versions, but that has not been tested.
ComentáriosFeedback
Forneça comentários sobre este tutorial no repositório do GitHub.Please provide any feedback on this tutorial in the GitHub repository.
Criar um aplicativo Web do ASP.NET Core MVC
Os aplicativos de guia do Microsoft Teams têm várias opções para autenticar o usuário e chamar o Microsoft Graph.Microsoft Teams tab applications have multiple options to authenticate the user and call Microsoft Graph. Neste exercício, você implementará uma guia que faz logon único para obter um token de autenticação no cliente e, em seguida, usa o fluxo em nome de no servidor para trocar esse token para obter acesso ao Microsoft Graph.In this exercise, you'll implement a tab that does single sign-on to get an auth token on the client, then uses the on-behalf-of flow on the server to exchange that token to get access to Microsoft Graph.
Para outras alternativas, confira o seguinte.For other alternatives, see the following.
- Crie uma guia do Microsoft Teams com o Microsoft Graph Toolkit.Build a Microsoft Teams tab with the Microsoft Graph Toolkit. Este exemplo é completamente do lado do cliente e usa o Microsoft Graph Toolkit para lidar com a autenticação e fazendo chamadas para o Microsoft Graph.This sample is completely client-side, and uses the Microsoft Graph Toolkit to handle authentication and making calls to Microsoft Graph.
- Exemplo de autenticação do Microsoft Teams.Microsoft Teams Authentication Sample. Este exemplo contém vários exemplos que abrangem diferentes cenários de autenticação.This sample contains multiple examples covering different authentication scenarios.
Criar o projetoCreate the project
Comece criando um aplicativo Web do ASP.NET Core.Start by creating an ASP.NET Core web app.
Abra a interface de linha de comando (CLI) em um diretório onde você deseja criar o projeto.Open your command-line interface (CLI) in a directory where you want to create the project. Execute o seguinte comando.Run the following command.
dotnet new webapp -o GraphTutorial
Depois que o projeto for criado, verifique se ele funciona alterando o diretório atual para o diretório GraphTutorial e executando o seguinte comando na sua CLI.Once the project is created, verify that it works by changing the current directory to the GraphTutorial directory and running the following command in your CLI.
dotnet run
Abra o navegador e navegue até
https://localhost:5001
.Open your browser and browse tohttps://localhost:5001
. Se tudo estiver funcionando, você deverá ver uma página padrão do ASP.NET Core.If everything is working, you should see a default ASP.NET Core page.
Importante
Se você receber um aviso de que o certificado para localhost é não confiável, você pode usar a CLI do .NET Core para instalar e confiar no certificado de desenvolvimento.If you receive a warning that the certificate for localhost is un-trusted you can use the .NET Core CLI to install and trust the development certificate. Consulte impor HTTPS no ASP.NET Core para obter instruções para sistemas operacionais específicos.See Enforce HTTPS in ASP.NET Core for instructions for specific operating systems.
Adicionar pacotes NuGetAdd NuGet packages
Antes de prosseguir, instale alguns pacotes NuGet adicionais que serão usados posteriormente.Before moving on, install some additional NuGet packages that you will use later.
- Microsoft. Identity. Web para autenticação e solicitação de tokens de acesso.Microsoft.Identity.Web for authenticating and requesting access tokens.
- Microsoft. Identity. Web. MicrosoftGraph para adicionar o suporte do Microsoft Graph configurado com Microsoft. Identity. Web.Microsoft.Identity.Web.MicrosoftGraph for adding Microsoft Graph support configured with Microsoft.Identity.Web.
- Microsoft. Graph para atualizar a versão deste pacote instalado pelo Microsoft. Identity. Web. MicrosoftGraph.Microsoft.Graph to update the version of this package installed by Microsoft.Identity.Web.MicrosoftGraph.
- Microsoft. AspNetCore. Mvc. NewtonsoftJson para habilitar conversores JSON do Newtonsoft para compatibilidade com o SDK do Microsoft Graph.Microsoft.AspNetCore.Mvc.NewtonsoftJson to enable Newtonsoft JSON converters for compatibility with the Microsoft Graph SDK.
- Timezoneconverter para conversão de identificadores de fuso horário do Windows em identificadores da IANA.TimeZoneConverter for translating Windows time zone identifiers to IANA identifiers.
Execute os seguintes comandos em sua CLI para instalar as dependências.Run the following commands in your CLI to install the dependencies.
dotnet add package Microsoft.Identity.Web --version 1.1.0 dotnet add package Microsoft.Identity.Web.MicrosoftGraph --version 1.1.0 dotnet add package Microsoft.Graph --version 3.16.0 dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson dotnet add package TimeZoneConverter
Projetar o aplicativoDesign the app
Nesta seção, você criará a estrutura básica de interface do usuário do aplicativo.In this section you will create the basic UI structure of the application.
Dica
Você pode usar qualquer editor de texto para editar os arquivos de origem deste tutorial.You can use any text editor to edit the source files for this tutorial. No entanto, o código do Visual Studio fornece recursos adicionais, como depuração e IntelliSense.However, Visual Studio Code provides additional features, such as debugging and Intellisense.
Abra ./Pages/Shared/_Layout. cshtml e substitua todo o conteúdo pelo código a seguir para atualizar o layout global do aplicativo.Open ./Pages/Shared/_Layout.cshtml and replace its entire contents with the following code to update the global layout of the app.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>@ViewData["Title"] - GraphTutorial</title> <link rel="stylesheet" href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/11.0.0/css/fabric.min.css" /> <link rel="stylesheet" href="~/css/site.css" /> </head> <body class="ms-Fabric"> <div class="container"> <main role="main"> @RenderBody() </main> </div> <script src="~/lib/jquery/dist/jquery.min.js"></script> <script src="https://statics.teams.cdn.office.net/sdk/v1.7.0/js/MicrosoftTeams.min.js"></script> <script src="~/js/site.js" asp-append-version="true"></script> @RenderSection("Scripts", required: false) </body> </html>
Isso substitui a inicialização pela interface do usuário fluente, adiciona o SDK do Microsoft Teamse simplifica o layout.This replaces Bootstrap with Fluent UI, adds the Microsoft Teams SDK, and simplifies the layout.
Abra ./wwwroot/js/site.js e adicione o código a seguir.Open ./wwwroot/js/site.js and add the following code.
(function () { // Support Teams themes microsoftTeams.initialize(); // On load, match the current theme microsoftTeams.getContext((context) => { if(context.theme !== 'default') { // For Dark and High contrast, set text to white document.body.style.color = '#fff'; document.body.style.setProperty('--border-style', 'solid'); } }); // Register event listener for theme change microsoftTeams.registerOnThemeChangeHandler((theme)=> { if(theme !== 'default') { document.body.style.color = '#fff'; document.body.style.setProperty('--border-style', 'solid'); } else { // For default theme, remove inline style document.body.style.color = ''; document.body.style.setProperty('--border-style', 'none'); } }); })();
Isso adiciona um manipulador de alteração de tema simples para alterar a cor de texto padrão para temas escuros e de alto contraste.This adds a simple theme change handler to change the default text color for dark and high contrast themes.
Abra ./wwwroot/CSS/site.css e substitua seu conteúdo pelo seguinte.Open ./wwwroot/css/site.css and replace its contents with the following.
:root { --border-style: none; } .tab-title { margin-bottom: .5em; } .event-card { margin: .5em; padding: 1em; border-style: var(--border-style); border-width: 1px; border-color: #fff; } .event-card div { margin-bottom: .25em; } .event-card .ms-Icon { margin-right: 10px; float: left; position: relative; top: 3px; } .event-card .ms-Icon--MapPin { top: 2px; } .form-container { max-width: 720px; } .form-label { display: block; margin-bottom: .25em; } .form-input { width: 100%; margin-bottom: .25em; padding: .5em; box-sizing: border-box; } .form-button { padding: .5em; } .result-panel { display: none; padding: 1em; margin: 1em; } .error-msg { color: red; } .success-msg { color: green; }
Abra ./pages/index.cshtml e substitua seu conteúdo pelo código a seguir.Open ./Pages/Index.cshtml and replace its contents with the following code.
@page @model IndexModel @{ ViewData["Title"] = "Home page"; } <div id="tab-container"> <h1 class="ms-fontSize-24 ms-fontWeight-semibold">Loading...</h1> </div> @section Scripts { <script> </script> }
Abra ./Startup.cs e remova a
app.UseHttpsRedirection();
linha noConfigure
método.Open ./Startup.cs and remove theapp.UseHttpsRedirection();
line in theConfigure
method. Isso é necessário para que o tunelamento do ngrok funcione.This is necessary for ngrok tunneling to work.
Executar ngrokRun ngrok
O Microsoft Teams não oferece suporte a hospedagem local para aplicativos.Microsoft Teams does not support local hosting for apps. O servidor que hospeda o aplicativo deve estar disponível na nuvem usando pontos de extremidade HTTPS.The server hosting your app must be available from the cloud using HTTPS endpoints. Para depuração local, você pode usar o ngrok para criar uma URL pública para seu projeto hospedado localmente.For debugging locally, you can use ngrok to create a public URL for your locally-hosted project.
Abra sua CLI e execute o seguinte comando para iniciar o ngrok.Open your CLI and run the following command to start ngrok.
ngrok http 5000
Após a inicialização do ngrok, copie a URL de encaminhamento HTTPS.Once ngrok starts, copy the HTTPS Forwarding URL. Deve parecer
https://50153897dd4d.ngrok.io
.It should look likehttps://50153897dd4d.ngrok.io
. Você precisará desse valor nas etapas posteriores.You'll need this value in later steps.
Importante
Se você estiver usando a versão gratuita do ngrok, a URL de encaminhamento será alterada toda vez que você reiniciar o ngrok.If you are using the free version of ngrok, the forwarding URL changes every time you restart ngrok. É recomendável deixar o ngrok em execução até que este tutorial seja concluído para manter a mesma URL.It's recommended that you leave ngrok running until you complete this tutorial to keep the same URL. Se for necessário reiniciar o ngrok, você precisará atualizar sua URL em todos os lugares em que ela é usada e reinstalar o aplicativo no Microsoft Teams.If you have to restart ngrok, you'll need to update your URL everywhere that it is used and reinstall the app in Microsoft Teams.
Registrar o aplicativo no portal
Neste exercício, você criará um novo registro de aplicativo Web do Azure AD usando o centro de administração do Azure Active Directory.In this exercise, you will create a new Azure AD web application registration using the Azure Active Directory admin center.
Abra um navegador e navegue até o centro de administração do Azure Active Directory.Open a browser and navigate to the Azure Active Directory admin center. Faça logon usando uma conta pessoal (também conhecida como Conta da Microsoft) ou Conta Corporativa ou de Estudante.Login using a personal account (aka: Microsoft Account) or Work or School Account.
Selecione Azure Active Directory na navegação esquerda e selecione Registros de aplicativos em Gerenciar.Select Azure Active Directory in the left-hand navigation, then select App registrations under Manage.
Selecione Novo registro.Select New registration. Na página registrar um aplicativo , defina os valores da seguinte maneira, onde
YOUR_NGROK_URL
é a URL de encaminhamento ngrok que você copiou na seção anterior.On the Register an application page, set the values as follows, whereYOUR_NGROK_URL
is the ngrok forwarding URL you copied in the previous section.- Defina Nome para
Teams Graph Tutorial
.Set Name toTeams Graph Tutorial
. - Defina Tipos de conta com suporte para Contas em qualquer diretório organizacional e contas pessoais da Microsoft.Set Supported account types to Accounts in any organizational directory and personal Microsoft accounts.
- Em URI de Redirecionamento, defina o primeiro menu suspenso para
Web
e defina o valor comoYOUR_NGROK_URL/authcomplete
.Under Redirect URI, set the first drop-down toWeb
and set the value toYOUR_NGROK_URL/authcomplete
.
- Defina Nome para
Selecione Registrar.Select Register. Na página tutorial de gráfico do teams , copie o valor da ID do aplicativo (cliente) e salve-o, você precisará dele na próxima etapa.On the Teams Graph Tutorial page, copy the value of the Application (client) ID and save it, you will need it in the next step.
Selecione Autenticação em Gerenciar.Select Authentication under Manage. Localize a seção Grant implícita e habilite tokens de acesso e tokens de ID.Locate the Implicit grant section and enable Access tokens and ID tokens. Selecione Salvar.Select Save.
Selecione Certificados e segredos sob Gerenciar.Select Certificates & secrets under Manage. Selecione o botão Novo segredo do cliente.Select the New client secret button. Insira um valor em Descrição e selecione uma das opções para expirar e selecione Adicionar.Enter a value in Description and select one of the options for Expires and select Add.
Copie o valor secreto do cliente antes de sair desta página.Copy the client secret value before you leave this page. Você precisará dele na próxima etapa.You will need it in the next step.
Importante
Este segredo do cliente nunca é mostrado novamente, portanto, copie-o agora.This client secret is never shown again, so make sure you copy it now.
Selecione permissões de API em gerenciar e, em seguida, selecione Adicionar uma permissão.Select API permissions under Manage, then select Add a permission.
Selecione Microsoft Graph e, em seguida, permissões delegadas.Select Microsoft Graph, then Delegated permissions.
Selecione as permissões a seguir e, em seguida, selecione adicionar permissões.Select the following permissions, then select Add permissions.
- Calendars. ReadWrite : isso permitirá que o aplicativo Leia e grave no calendário do usuário.Calendars.ReadWrite - this will allow the app to read and write to the user's calendar.
- MailboxSettings. Read : isso permitirá que o aplicativo obtenha o fuso horário, o formato de data e o formato de hora do usuário de suas configurações de caixa de correio.MailboxSettings.Read - this will allow the app to get the user's time zone, date format, and time format from their mailbox settings.
Configurar logon único do teamsConfigure Teams single sign-on
Nesta seção, você atualizará o registro de aplicativo para dar suporte ao logon único no Teams.In this section you'll update the app registration to support single sign-on in Teams.
Selecione expor uma API.Select Expose an API. Selecione o link definir próximo a URI da ID do aplicativo.Select the Set link next to Application ID URI. Insira o nome de domínio da URL de encaminhamento do ngrok (com uma barra "/" acrescentada ao final) entre as barras duplas de avanço e o GUID.Insert your ngrok forwarding URL domain name (with a forward slash "/" appended to the end) between the double forward slashes and the GUID. A ID inteira deve ser semelhante a:
api://50153897dd4d.ngrok.io/ae7d8088-3422-4c8c-a351-6ded0f21d615
.The entire ID should look similar to:api://50153897dd4d.ngrok.io/ae7d8088-3422-4c8c-a351-6ded0f21d615
.Na seção escopos definidos por esta API , selecione Adicionar um escopo.In the Scopes defined by this API section, select Add a scope. Preencha os campos da seguinte maneira e selecione Adicionar escopo.Fill in the fields as follows and select Add scope.
- Nome do escopo:
access_as_user
Scope name:access_as_user
- Quem pode consentir?: administradores e usuáriosWho can consent?: Admins and users
- Nome para exibição do consentimento do administrador:
Access the app as the user
Admin consent display name:Access the app as the user
- Descrição do consentimento do administrador:
Allows Teams to call the app's web APIs as the current user.
Admin consent description:Allows Teams to call the app's web APIs as the current user.
- Nome para exibição do consentimento do usuário:
Access the app as you
User consent display name:Access the app as you
- Descrição do consentimento do usuário:
Allows Teams to call the app's web APIs as you.
User consent description:Allows Teams to call the app's web APIs as you.
- Estado: habilitadoState: Enabled
- Nome do escopo:
Na seção aplicativos cliente autorizados , selecione Adicionar um aplicativo cliente.In the Authorized client applications section, select Add a client application. Insira uma ID de cliente na lista a seguir, habilite o escopo em escopos autorizados e selecione Adicionar aplicativo.Enter a client ID from the following list, enable the scope under Authorized scopes, and select Add application. Repita esse processo para cada uma das IDs de cliente na lista.Repeat this process for each of the client IDs in the list.
1fec8e78-bce4-4aaf-ab1b-5451cc387264
(Aplicativo móvel/aplicativo de área de trabalho do Microsoft Teams)1fec8e78-bce4-4aaf-ab1b-5451cc387264
(Teams mobile/desktop application)5e3ce6c0-2b1f-4285-8d4b-75ee78787346
(Aplicativo Web do Teams)5e3ce6c0-2b1f-4285-8d4b-75ee78787346
(Teams web application)
Criar manifesto de aplicativo
O manifesto do aplicativo descreve como o aplicativo se integra ao Microsoft Teams e é necessário para instalar aplicativos.The app manifest describes how the app integrates with Microsoft Teams and is required to install apps. Nesta seção, você usará o app Studio no cliente Microsoft Teams para gerar um manifesto.In this section you'll use App Studio in the Microsoft Teams client to generate a manifest.
Se você ainda não tiver o app Studio instalado no Teams, Instale-o agora.If you do not already have App Studio installed in Teams, install it now.
Inicie o app Studio no Microsoft Teams e selecione o Editor de manifesto.Launch App Studio in Microsoft Teams and select the Manifest editor.
Selecione criar um novo aplicativo.Select Create a new app.
Na página detalhes do aplicativo , preencha os campos obrigatórios.On the App details page, fill in the required fields.
Observação
Você pode usar os ícones padrão na seção branding ou carregar seus próprios.You can use the default icons in the Branding section or upload your own.
No menu à esquerda, selecione guias em recursos.On the left-hand menu, select Tabs under Capabilities.
Selecione Adicionar em Adicionar uma guia pessoal.Select Add under Add a personal tab.
Preencha os campos da seguinte maneira, onde
YOUR_NGROK_URL
é a URL de encaminhamento que você copiou na seção anterior.Fill in the fields as follows, whereYOUR_NGROK_URL
is the forwarding URL you copied in the previous section. Selecione salvar quando terminar.Select Save when done.- Nome:
Create event
Name:Create event
- ID da entidade:
createEventTab
Entity ID:createEventTab
- URL do conteúdo:
YOUR_NGROK_URL/newevent
Content URL:YOUR_NGROK_URL/newevent
- Nome:
Selecione Adicionar em Adicionar uma guia pessoal.Select Add under Add a personal tab.
Preencha os campos da seguinte maneira, onde
YOUR_NGROK_URL
é a URL de encaminhamento que você copiou na seção anterior.Fill in the fields as follows, whereYOUR_NGROK_URL
is the forwarding URL you copied in the previous section. Selecione salvar quando terminar.Select Save when done.- Nome:
Graph calendar
Name:Graph calendar
- ID da entidade:
calendarTab
Entity ID:calendarTab
- URL do conteúdo:
YOUR_NGROK_URL
Content URL:YOUR_NGROK_URL
- Nome:
No menu à esquerda, selecione domínios e permissões em concluir.On the left-hand menu, select Domains and permissions under Finish.
Defina a ID do aplicativo AAD para a ID do aplicativo do registro do aplicativo.Set the AAD App ID to the application ID from your app registration.
Defina o campo logon único como o URI da ID do aplicativo do registro do aplicativo.Set the Single-Sign-On field to the application ID URI from your app registration.
No menu à esquerda, selecione testar e distribuir em concluir.On the left-hand menu, select Test and distribute under Finish. Selecione baixar.Select Download.
Crie um novo diretório na raiz do projeto chamado manifest.Create a new directory in the root of the project named Manifest. Extraia o conteúdo do arquivo ZIP baixado para esse diretório.Extract the contents of the downloaded ZIP file to this directory.
Adicionar autenticação do Azure AD
Neste exercício, você estenderá o aplicativo do exercício anterior para suportar a autenticação de logon único com o Azure AD.In this exercise you will extend the application from the previous exercise to support single sign-on authentication with Azure AD. Isso é necessário para obter o token de acesso OAuth necessário para chamar a API do Microsoft Graph.This is required to obtain the necessary OAuth access token to call the Microsoft Graph API. Nesta etapa, você irá configurar a biblioteca Microsoft. Identity. Web .In this step you will configure the Microsoft.Identity.Web library.
Importante
Para evitar o armazenamento da ID do aplicativo e o segredo na fonte, você usará o Gerenciador de segredo do .net para armazenar esses valores.To avoid storing the application ID and secret in source, you will use the .NET Secret Manager to store these values. O gerente secreto é apenas para fins de desenvolvimento, os aplicativos de produção devem usar um Gerenciador de segredo confiável para armazenar segredos.The Secret Manager is for development purposes only, production apps should use a trusted secret manager for storing secrets.
Abra ./appsettings.jsem e substitua seu conteúdo pelo seguinte.Open ./appsettings.json and replace its contents with the following.
{ "AzureAd": { "Instance": "https://login.microsoftonline.com/", "TenantId": "common" }, "Graph": { "Scopes": "https://graph.microsoft.com/.default" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" }
Abra a CLI no diretório onde o GraphTutorial. csproj está localizado e execute os seguintes comandos, substituindo
YOUR_APP_ID
pela ID do aplicativo do portal do Azure eYOUR_APP_SECRET
com o segredo do aplicativo.Open your CLI in the directory where GraphTutorial.csproj is located, and run the following commands, substitutingYOUR_APP_ID
with your application ID from the Azure portal, andYOUR_APP_SECRET
with your application secret.dotnet user-secrets init dotnet user-secrets set "AzureAd:ClientId" "YOUR_APP_ID" dotnet user-secrets set "AzureAd:ClientSecret" "YOUR_APP_SECRET"
Implementar logonImplement sign-in
Primeiro, implemente o logon único no código JavaScript do aplicativo.First, implement single sign-on in the app's JavaScript code. Você usará o SDK do JavaScript do Microsoft Teams para obter um token de acesso que permite que o código JavaScript em execução no cliente do teams faça chamadas AJAX para a API Web que você implementará mais tarde.You will use the Microsoft Teams JavaScript SDK to get an access token which allows the JavaScript code running in the Teams client to make AJAX calls to Web API you will implement later.
Abra ./pages/index.cshtml e adicione o código a seguir dentro da
<script>
marca.Open ./Pages/Index.cshtml and add the following code inside the<script>
tag.(function () { if (microsoftTeams) { microsoftTeams.initialize(); microsoftTeams.authentication.getAuthToken({ successCallback: (token) => { // TEMPORARY: Display the access token for debugging $('#tab-container').empty(); $('<code/>', { text: token, style: 'word-break: break-all;' }).appendTo('#tab-container'); }, failureCallback: (error) => { renderError(error); } }); } })(); function renderError(error) { $('#tab-container').empty(); $('<h1/>', { text: 'Error' }).appendTo('#tab-container'); $('<code/>', { text: JSON.stringify(error, Object.getOwnPropertyNames(error)), style: 'word-break: break-all;' }).appendTo('#tab-container'); }
Isso chama o
microsoftTeams.authentication.getAuthToken
para autenticar silenciosamente como o usuário conectado ao Teams.This calls themicrosoftTeams.authentication.getAuthToken
to silently authenticate as the user that is signed in to Teams. Normalmente, não há nenhum prompt de interface do usuário envolvido, a menos que o usuário tenha que dar o consentimento.There is typically not any UI prompts involved, unless the user has to consent. O código, em seguida, exibe o token na guia.The code then displays the token in the tab.Salve suas alterações e inicie o aplicativo executando o seguinte comando em sua CLI.Save your changes and start your application by running the following command in your CLI.
dotnet run
Importante
Se você tiver reiniciado o ngrok e sua URL do ngrok tiver mudado, certifique-se de atualizar o valor de ngrok no seguinte local antes de testar.If you have restarted ngrok and your ngrok URL has changed, be sure to update the ngrok value in the following place before you test.
- O URI de redirecionamento em seu registro de aplicativoThe redirect URI in your app registration
- O URI da ID do aplicativo em seu registro de aplicativoThe application ID URI in your app registration
contentUrl
em manifest.jsemcontentUrl
in manifest.jsonvalidDomains
em manifest.jsemvalidDomains
in manifest.jsonresource
em manifest.jsemresource
in manifest.json
Crie um arquivo ZIP com manifest.jsem, color.png e outline.png.Create a ZIP file with manifest.json, color.png, and outline.png.
No Microsoft Teams, selecione aplicativos na barra esquerda, selecione carregar um aplicativo personalizado e, em seguida, selecione carregar para mim ou minhas equipes.In Microsoft Teams, select Apps in the left-hand bar, select Upload a custom app, then select Upload for me or my teams.
Navegue até o arquivo ZIP que você criou anteriormente e selecione abrir.Browse to the ZIP file you created previously and select Open.
Revise as informações do aplicativo e selecione Adicionar.Review the application information and select Add.
O aplicativo é aberto no Teams e exibe um token de acesso.The application opens in Teams and displays an access token.
Se você copiar o token, poderá colá-lo no JWT.ms.If you copy the token, you can paste it into jwt.ms. Verifique se a audiência (a aud
declaração) é a ID do aplicativo e o único escopo (a scp
declaração) é o access_as_user
escopo da API que você criou.Verify that the audience (the aud
claim) is your application ID, and the only scope (the scp
claim) is the access_as_user
API scope you created. Isso significa que esse token não concede acesso direto ao Microsoft Graph!That means that this token does not grant direct access to Microsoft Graph! Em vez disso, a API Web que você irá implementar logo precisará trocar esse token usando o fluxo em nome de para obter um token que funcione com as chamadas do Microsoft Graph.Instead, the Web API you will implement soon will need to exchange this token using the on-behalf-of flow to get a token that will work with Microsoft Graph calls.
Configurar a autenticação no aplicativo principal do ASP.NETConfigure authentication in the ASP.NET Core app
Comece adicionando os serviços da plataforma de identidade da Microsoft ao aplicativo.Start by adding the Microsoft Identity platform services to the application.
Abra o arquivo ./Startup.cs e adicione a instrução a seguir
using
à parte superior do arquivo.Open the ./Startup.cs file and add the followingusing
statement to the top of the file.using Microsoft.Identity.Web;
Adicione a seguinte linha antes da
app.UseAuthorization();
linha naConfigure
função.Add the following line just before theapp.UseAuthorization();
line in theConfigure
function.app.UseAuthentication();
Adicione a linha a seguir logo após a
endpoints.MapRazorPages();
linha naConfigure
função.Add the following line just after theendpoints.MapRazorPages();
line in theConfigure
function.endpoints.MapControllers();
Substitua a função
ConfigureServices
existente pelo seguinte.Replace the existingConfigureServices
function with the following.public void ConfigureServices(IServiceCollection services) { // Use Web API authentication (default JWT bearer token scheme) services.AddMicrosoftIdentityWebApiAuthentication(Configuration) // Enable token acquisition via on-behalf-of flow .EnableTokenAcquisitionToCallDownstreamApi() // Specify that the down-stream API is Graph .AddMicrosoftGraph(Configuration.GetSection("Graph")) // Use in-memory token cache // See https://github.com/AzureAD/microsoft-identity-web/wiki/token-cache-serialization .AddInMemoryTokenCaches(); services.AddRazorPages(); services.AddControllers().AddNewtonsoftJson(); }
Este código configura o aplicativo para permitir que as chamadas de APIs da Web sejam autenticadas com base no token de portador JWT no
Authorization
cabeçalho.This code configures the application to allow calls to Web APIs to be authenticated based on the JWT bearer token in theAuthorization
header. Ele também adiciona os serviços de aquisição de token que podem trocar o token por meio do fluxo em nome de.It also adds the token acquisition services that can exchange that token via the on-behalf-of flow.
Criar o controlador de API WebCreate the Web API controller
Crie um novo diretório na raiz do projeto chamado controladores.Create a new directory in the root of the project named Controllers.
Crie um novo arquivo no diretório ./Controllers chamado CalendarController.cs e adicione o código a seguir.Create a new file in the ./Controllers directory named CalendarController.cs and add the following code.
using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Identity.Web; using Microsoft.Identity.Web.Resource; using Microsoft.Graph; using TimeZoneConverter; namespace GraphTutorial.Controllers { [ApiController] [Route("[controller]")] [Authorize] public class CalendarController : ControllerBase { private static readonly string[] apiScopes = new[] { "access_as_user" }; private readonly GraphServiceClient _graphClient; private readonly ITokenAcquisition _tokenAcquisition; private readonly ILogger<CalendarController> _logger; public CalendarController(ITokenAcquisition tokenAcquisition, GraphServiceClient graphClient, ILogger<CalendarController> logger) { _tokenAcquisition = tokenAcquisition; _graphClient = graphClient; _logger = logger; } [HttpGet] public async Task<string> 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 { // TEMPORARY // Get a Graph token via OBO flow var token = await _tokenAcquisition .GetAccessTokenForUserAsync(new[]{ "User.Read", "MailboxSettings.Read", "Calendars.ReadWrite" }); // Log the token _logger.LogInformation($"Access token for Graph: {token}"); return "{ \"status\": \"OK\" }"; } catch (MicrosoftIdentityWebChallengeUserException ex) { _logger.LogError(ex, "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"); return null; } catch (Exception ex) { _logger.LogError(ex, "Error occurred"); return null; } } } }
Isso implementa uma API Web (
GET /calendar
) que pode ser chamada na guia Teams. Por ora, ele simplesmente tenta trocar o token de portador por um token de gráfico.This implements a Web API (GET /calendar
) that can be called from the Teams tab. For now it simply tries to exchange the bearer token for a Graph token. Na primeira vez que um usuário carrega a guia, ele falhará porque ainda não enviou permissão para permitir que o aplicativo acesse o Microsoft Graph em seu nome.The first time a user loads the tab, this will fail because they have not yet consented to allow the app access to Microsoft Graph on their behalf.Abra ./pages/index.cshtml e substitua a
successCallback
função com o seguinte.Open ./Pages/Index.cshtml and replace thesuccessCallback
function with the following.successCallback: (token) => { // TEMPORARY: Call the Web API fetch('/calendar', { headers: { 'Authorization': `Bearer ${token}` } }).then(response => { response.text() .then(body => { $('#tab-container').empty(); $('<code/>', { text: body }).appendTo('#tab-container'); }); }).catch(error => { console.error(error); renderError(error); }); }
Isso chamará a API Web e exibirá a resposta.This will call the Web API and display the response.
Salve suas alterações e reinicie o aplicativo.Save your changes and restart the app. Atualize a guia no Microsoft Teams.Refresh the tab in Microsoft Teams. A página deve ser exibida
consent_required
.The page should displayconsent_required
.Revise o log de saída na sua CLI.Review the log output in your CLI. Observe duas coisas.Notice two things.
- Uma entrada como
Authenticated user: MeganB@contoso.com
.An entry likeAuthenticated user: MeganB@contoso.com
. A API da Web autenticou o usuário com base no token enviado com a solicitação de API.The Web API has authenticated the user based on the token sent with the API request. - Uma entrada como
AADSTS65001: The user or administrator has not consented to use the application with ID...
.An entry likeAADSTS65001: The user or administrator has not consented to use the application with ID...
. Isso é esperado, pois o usuário ainda não foi solicitado a dar o consentimento para os escopos de permissão do Microsoft Graph solicitados.This is expected, since the user has not yet been prompted to consent for the requested Microsoft Graph permission scopes.
- Uma entrada como
Implementar prompt de consentimentoImplement consent prompt
Como a API Web não pode avisar o usuário, a guia Teams precisará implementar um prompt.Because the Web API cannot prompt the user, the Teams tab will need to implement a prompt. Isso só precisará ser feito uma vez para cada usuário.This will only need to be done once for each user. Após um usuário, ele não precisará ser reconsente, a menos que ele explicitamente revogue o acesso ao seu aplicativo.Once a user consents, they do not need to reconsent unless they explicitly revoke access to your application.
Crie um novo arquivo no diretório ./Pages chamado Authenticate.cshtml.cs e adicione o código a seguir.Create a new file in the ./Pages directory named Authenticate.cshtml.cs and add the following code.
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace GraphTutorial.Pages { public class AuthenticateModel : PageModel { private readonly ILogger<IndexModel> _logger; public string ApplicationId { get; private set; } public string State { get; private set; } public string Nonce { get; private set; } public AuthenticateModel(IConfiguration configuration, ILogger<IndexModel> logger) { _logger = logger; // Read the application ID from the // configuration. This is used to build // the authorization URL for the consent prompt ApplicationId = configuration .GetSection("AzureAd") .GetValue<string>("ClientId"); // Generate a GUID for state and nonce State = System.Guid.NewGuid().ToString(); Nonce = System.Guid.NewGuid().ToString(); } } }
Crie um novo arquivo no diretório ./Pages chamado Authenticate. cshtml e adicione o código a seguir.Create a new file in the ./Pages directory named Authenticate.cshtml and add the following code.
@page <!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> @model AuthenticateModel @section Scripts { <script> (function () { microsoftTeams.initialize(); // Save the state so it can be verified in // AuthComplete.cshtml localStorage.setItem('auth-state', '@Model.State'); // Get the context for tenant ID and login hint microsoftTeams.getContext((context) => { // Set all of the query parameters for an // authorization request const queryParams = { client_id: '@Model.ApplicationId', response_type: 'id_token token', response_mode: 'fragment', scope: 'https://graph.microsoft.com/.default openid', redirect_uri: `${window.location.origin}/authcomplete`, nonce: '@Model.Nonce', state: '@Model.State', login_hint: context.loginHint, }; // Generate the URL const authEndpoint = `https://login.microsoftonline.com/${context.tid}/oauth2/v2.0/authorize?${toQueryString(queryParams)}`; // Browse to the URL window.location.assign(authEndpoint); }); })(); // Helper function to build a query string from an object function toQueryString(queryParams) { let encodedQueryParams = []; for (let key in queryParams) { encodedQueryParams.push(key + '=' + encodeURIComponent(queryParams[key])); } return encodedQueryParams.join('&'); } </script> }
Crie um novo arquivo no diretório ./Pages chamado AuthComplete. cshtml e adicione o código a seguir.Create a new file in the ./Pages directory named AuthComplete.cshtml and add the following code.
@page <!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> @section Scripts { <script> (function () { microsoftTeams.initialize(); const hashParams = getHashParameters(); if (hashParams['error']) { microsoftTeams.authentication.notifyFailure(hashParams['error']); } else if (hashParams['access_token']) { // Check the state parameter const expectedState = localStorage.getItem('auth-state'); if (expectedState !== hashParams['state']) { microsoftTeams.authentication.notifyFailure('StateDoesNotMatch'); } else { // State parameter matches, report success localStorage.removeItem('auth-state'); microsoftTeams.authentication.notifySuccess('Success'); } } else { microsoftTeams.authentication.notifyFailure('NoTokenInResponse'); } })(); // Helper function to generate a hash from // a query string function getHashParameters() { let hashParams = {}; location.hash.substr(1).split('&').forEach(function(item) { let s = item.split('='), k = s[0], v = s[1] && decodeURIComponent(s[1]); hashParams[k] = v; }); return hashParams; } </script> }
Abra ./pages/index.cshtml e adicione as seguintes funções dentro da
<script>
marca.Open ./Pages/Index.cshtml and add the following functions inside the<script>
tag.function loadUserCalendar(token, callback) { // Call the API fetch('/calendar', { headers: { 'Authorization': `Bearer ${token}` } }).then(response => { if (response.ok) { // Get the JSON payload response.json() .then(events => { callback(events); }); } else if (response.status === 403) { response.text() .then(body => { // If the API sent 'consent_required' // we need to prompt the user if (body === 'consent_required') { promptForConsent((error) => { if (error) { renderError(error); } else { // Retry API call loadUserCalendar(token, callback); } }); } }); } }).catch(error => { renderError(error); }); } function promptForConsent(callback) { // Cause Teams to popup a window for consent microsoftTeams.authentication.authenticate({ url: `${window.location.origin}/authenticate`, width: 600, height: 535, successCallback: (result) => { callback(null); }, failureCallback: (error) => { callback(error); } }); }
Adicione a seguinte função dentro da
<script>
marca para exibir um resultado bem-sucedido da API Web.Add the following function inside the<script>
tag to display a successful result from the Web API.function renderCalendar(events) { $('#tab-container').empty(); $('<pre/>').append($('<code/>', { text: JSON.stringify(events, null, 2), style: 'word-break: break-all;' })).appendTo('#tab-container'); }
Substitua o existente
successCallback
pelo código a seguir.Replace the existingsuccessCallback
with the following code.successCallback: (token) => { loadUserCalendar(token, (events) => { renderCalendar(events); }); }
Salve suas alterações e reinicie o aplicativo.Save your changes and restart the app. Atualize a guia no Microsoft Teams.Refresh the tab in Microsoft Teams. A guia deve ser exibida
{ "status": "OK" }
.The tab should display{ "status": "OK" }
.Revise a saída do log.Review the log output. Você deve ver a
Access token for Graph
entrada.You should see theAccess token for Graph
entry. Se você analisar esse token, observará que ele contém os escopos do Microsoft Graph configurados em appsettings.js.If you parse that token, you'll notice that it contains the Microsoft Graph scopes configured in appsettings.json.
Armazenar e atualizar tokensStoring and refreshing tokens
Nesse ponto, seu aplicativo tem um token de acesso, que é enviado no Authorization
cabeçalho das chamadas de API.At this point your application has an access token, which is sent in the Authorization
header of API calls. Este é o token que permite ao aplicativo acessar o Microsoft Graph em nome do usuário.This is the token that allows the app to access Microsoft Graph on the user's behalf.
No entanto, esse token é de vida curta.However, this token is short-lived. O token expira uma hora após sua emissão.The token expires an hour after it is issued. É onde o token de atualização se torna útil.This is where the refresh token becomes useful. O token de atualização permite que o aplicativo solicite um novo token de acesso sem exigir que o usuário entre novamente.The refresh token allows the app to request a new access token without requiring the user to sign in again.
Como o aplicativo está usando a biblioteca Microsoft. Identity. Web, você não precisa implementar qualquer lógica de armazenamento ou de atualização.Because the app is using the Microsoft.Identity.Web library, you do not have to implement any token storage or refresh logic.
O aplicativo usa o cache de token na memória, o que é suficiente para aplicativos que não precisam persistir tokens quando o aplicativo é reiniciado.The app uses the in-memory token cache, which is sufficient for apps that do not need to persist tokens when the app restarts. Em vez disso, os aplicativos de produção podem usar as Opções de cache distribuído na biblioteca Microsoft. Identity. Web.Production apps may instead use the distributed cache options in the Microsoft.Identity.Web library.
O GetAccessTokenForUserAsync
método manipula a expiração do token e a atualização para você.The GetAccessTokenForUserAsync
method handles token expiration and refresh for you. Primeiro ele verifica o token em cache e, se ele não tiver expirado, ele o retornará.It first checks the cached token, and if it is not expired, it returns it. Se ele tiver expirado, ele usará o token de atualização em cache para obter um novo.If it is expired, it uses the cached refresh token to obtain a new one.
O GraphServiceClient que os controladores recebem por meio da injeção de dependência é pré-configurado com um provedor de autenticação que usa GetAccessTokenForUserAsync
para você.The GraphServiceClient that controllers get via dependency injection is pre-configured with an authentication provider that uses GetAccessTokenForUserAsync
for you.
Obter um modo de exibição de calendário
Nesta seção, você incorporará o Microsoft Graph no aplicativo.In this section you will incorporate Microsoft Graph into the application. Para este aplicativo, você usará a biblioteca de cliente do Microsoft Graph para .net para fazer chamadas para o Microsoft Graph.For this application, you will use the Microsoft Graph Client Library for .NET to make calls to Microsoft Graph.
Obter um modo de exibição de calendárioGet a calendar view
Um modo de exibição de calendário é um conjunto de eventos do calendário do usuário que ocorrem entre dois pontos de tempo.A calendar view is a set of events from the user's calendar that occur between two points of time. Você o usará para obter os eventos do usuário para a semana atual.You'll use this to get the user's events for the current week.
Abra ./Controllers/CalendarController.cs e adicione a função a seguir à classe 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); }
Adicione a seguinte função para lidar com exceções retornadas pelas chamadas do 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()); } }
Substitua a função
Get
existente pelo seguinte.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; } }
Revise as alterações.Review the changes. Esta nova versão da função:This new version of the function:
- Retorna
IEnumerable<Event>
em vez destring
.ReturnsIEnumerable<Event>
instead ofstring
. - Obtém as configurações de caixa de correio do usuário usando o Microsoft Graph.Gets the user's mailbox settings using Microsoft Graph.
- Usa o fuso horário do usuário para calcular o início e o fim da semana atual.Uses the user's time zone to calculate the start and end of the current week.
- Obtém uma visão de calendárioGets a calendar view
- Usa a
.Header()
função para incluir umPrefer: outlook.timezone
cabeçalho, o que faz com que os eventos retornados tenham seus horários de início e término convertidos no fuso horário do usuário.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. - Usa a
.Top()
função para solicitar no máximo 50 eventos.Uses the.Top()
function to request at most 50 events. - Usa a
.Select()
função para solicitar apenas os campos usados pelo aplicativo.Uses the.Select()
function to request just the fields used by the app. - Usa a
OrderBy()
função para classificar os resultados pela hora de início.Uses theOrderBy()
function to sort the results by the start time.
- Usa a
- Retorna
Salve suas alterações e reinicie o aplicativo.Save your changes and restart the app. Atualize a guia no Microsoft Teams.Refresh the tab in Microsoft Teams. O aplicativo exibe uma lista de JSON dos eventos.The app displays a JSON listing of the events.
Exibir os resultadosDisplay the results
Agora você pode exibir a lista de eventos de forma mais amigável.Now you can display the list of events in a more user friendly way.
Abra ./pages/index.cshtml e adicione as seguintes funções dentro da
<script>
marca.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;' })); }
Substitua a função
renderCalendar
existente pelo seguinte.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'); }); }
Salve suas alterações e reinicie o aplicativo.Save your changes and restart the app. Atualize a guia no Microsoft Teams.Refresh the tab in Microsoft Teams. O aplicativo exibe eventos no calendário do usuário.The app displays events on the user's calendar.
Criar um novo evento
Nesta seção, você adicionará a capacidade de criar eventos no calendário do usuário.In this section you will add the ability to create events on the user's calendar.
Criar a nova guia de eventoCreate the new event tab
Crie um novo arquivo no diretório ./Pages chamado NewEvent. cshtml e adicione o código a seguir.Create a new file in the ./Pages directory named NewEvent.cshtml and add the following code.
@page <!-- Copyright (c) Microsoft Corporation. Licensed under the MIT License. --> @{ ViewData["Title"] = "New event"; } <div class="form-container"> <form id="newEventForm"> <div class="ms-Grid" dir="ltr"> <div class="ms-Grid-row"> <div class="ms-Grid-col ms-sm12"> <label class="ms-fontWeight-semibold form-label" for="subject">Subject</label> <input class="form-input" type="text" id="subject" name="subject" /> </div> </div> <div class="ms-Grid-row"> <div class="ms-Grid-col ms-sm12"> <label class="ms-fontWeight-semibold form-label" for="attendees">Attendees</label> <input class="form-input" type="text" id="attendees" name="attendees" placeholder="Enter email addresses of attendees. Separate multiple with ';'. Leave blank for no attendees." /> </div> </div> <div class="ms-Grid-row"> <div class="ms-Grid-col ms-sm6"> <label class="ms-fontWeight-semibold form-label" for="start">Start</label> <input class="form-input" type="datetime-local" id="start" name="start" /> </div> <div class="ms-Grid-col ms-sm6"> <label class="ms-fontWeight-semibold form-label" for="end">End</label> <input class="form-input" type="datetime-local" id="end" name="end" /> </div> </div> <div class="ms-Grid-row"> <div class="ms-Grid-col ms-sm12"> <label class="ms-fontWeight-semibold form-label" for="body">Body</label> <textarea class="form-input" id="body" name="body" rows="4"></textarea> </div> </div> <input class="form-button" type="submit" value="Create"/> </div> </form> <div class="ms-depth-16 result-panel"></div> </div> @section Scripts { <script> (function () { if (microsoftTeams) { microsoftTeams.initialize(); } $('#newEventForm').on('submit', async (e) => { e.preventDefault(); $('.result-panel').empty(); $('.result-panel').hide(); const formData = new FormData(newEventForm); // Basic validation // Require subject, start, and end const subject = formData.get('subject'); const start = formData.get('start'); const end = formData.get('end'); if (subject.length <= 0 || start.length <= 0 || end.length <= 0) { $('<div/>', { class: 'error-msg', text: 'Subject, Start, and End are required.' }).appendTo('.result-panel'); $('.result-panel').show(); return; } // Get the auth token from Teams microsoftTeams.authentication.getAuthToken({ successCallback: (token) => { createEvent(token, formData); }, failureCallback: (error) => { $('<div/>', { class: 'error-msg', text: `Error getting token: ${error}` }).appendTo('.result-panel'); $('.result-panel').show(); } }); }); })(); async function createEvent(token, formData) { // Convert the form to a JSON payload jsonFormData = formDataToJson(); // Post the payload to the web API const response = await fetch('/calendar', { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, method: 'POST', body: jsonFormData }); if (response.ok) { $('<div/>', { class: 'success-msg', text: 'Event added to your calendar' }).appendTo('.result-panel'); $('.result-panel').show(); } else { const error = await response.text(); $('<div/>', { class: 'error-msg', text: `Error creating event: ${error}` }).appendTo('.result-panel'); $('.result-panel').show(); } } // Helper method to serialize the form fields // as JSON function formDataToJson() { const array = $('#newEventForm').serializeArray(); const jsonObj = {}; array.forEach((kvp) => { jsonObj[kvp.name] = kvp.value; }); return JSON.stringify(jsonObj); } </script> }
Isso implementa um formulário simples e adiciona JavaScript para postar os dados do formulário na API Web.This implements a simple form, and adds JavaScript to post the form data to the Web API.
Implementar a API WebImplement the Web API
Crie um novo diretório chamado modelos na raiz do projeto.Create a new directory named Models in the root of the project.
Crie um novo arquivo no diretório ./Models chamado NewEvent.cs e adicione o código a seguir.Create a new file in the ./Models directory named NewEvent.cs and add the following code.
namespace GraphTutorial.Models { public class NewEvent { public string Subject { get; set; } public string Attendees { get; set; } public string Start { get; set; } public string End { get; set; } public string Body { get; set; } } }
Abra ./Controllers/CalendarController.cs e adicione a instrução a seguir
using
na parte superior do arquivo.Open ./Controllers/CalendarController.cs and add the followingusing
statement at the top of the file.using GraphTutorial.Models;
Adicione a função a seguir à classe CalendarController .Add the following function to the CalendarController class.
[HttpPost] public async Task<string> Post(NewEvent newEvent) { HttpContext.VerifyUserHasAnyAcceptedScope(apiScopes); try { // Get the user's mailbox settings var me = await _graphClient.Me .Request() .Select(u => new { u.MailboxSettings }) .GetAsync(); // Create a Graph Event var graphEvent = new Event { Subject = newEvent.Subject, Start = new DateTimeTimeZone { DateTime = newEvent.Start, TimeZone = me.MailboxSettings.TimeZone }, End = new DateTimeTimeZone { DateTime = newEvent.End, TimeZone = me.MailboxSettings.TimeZone } }; // If there are attendees, add them if (!string.IsNullOrEmpty(newEvent.Attendees)) { var attendees = new List<Attendee>(); var emailArray = newEvent.Attendees.Split(';'); foreach (var email in emailArray) { attendees.Add(new Attendee { Type = AttendeeType.Required, EmailAddress = new EmailAddress { Address = email } }); } graphEvent.Attendees = attendees; } // If there is a body, add it if (!string.IsNullOrEmpty(newEvent.Body)) { graphEvent.Body = new ItemBody { ContentType = BodyType.Text, Content = newEvent.Body }; } // Create the event await _graphClient.Me .Events .Request() .AddAsync(graphEvent); return "success"; } catch (Exception ex) { await HandleGraphException(ex); return null; } }
Isso permite que um HTTP POST para a API da Web com os campos do formulário.This allows an HTTP POST to the Web API with the fields of the form.
Salve todas as suas alterações e reinicie o aplicativo.Save all of your changes and restart the application. Atualize o aplicativo no Microsoft Teams e selecione a guia criar evento . Preencha o formulário e selecione criar para adicionar um evento ao calendário do usuário.Refresh the app in Microsoft Teams, and select the Create event tab. Fill out the form and select Create to add an event to the user's calendar.
Parabéns!
Você concluiu o Microsoft Teams app com o tutorial do Microsoft Graph.You've completed the Microsoft Teams app with Microsoft Graph tutorial. Agora que você tem um aplicativo de trabalho que chama o Microsoft Graph, você pode experimentar e adicionar novos recursos.Now that you have a working app that calls Microsoft Graph, you can experiment and add new features. Visite a visão geral do Microsoft Graph para ver todos os dados que você pode acessar com o Microsoft Graph.Visit the Overview of Microsoft Graph to see all of the data you can access with Microsoft Graph.
ComentáriosFeedback
Forneça comentários sobre este tutorial no repositório do GitHub.Please provide any feedback on this tutorial in the GitHub repository.
Tem algum problema com essa seção? Se tiver, envie seus comentários para que possamos melhorar esta seção.