简体   繁体   English

将Microsoft.Graph与控制台应用程序一起使用

[英]Using Microsoft.Graph with a Console application

I am trying to replicate the Microsoft example for using Outlook.COM with a console app. 我正在尝试复制将Outlook.COM与控制台应用程序一起使用的Microsoft示例。 So I created a C# console app and added the needed packages: 因此,我创建了一个C#控制台应用程序并添加了所需的软件包:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.Graph" version="1.5.1" targetFramework="net461" />
  <package id="Microsoft.Graph.Core" version="1.6.1" targetFramework="net461" />
  <package id="Microsoft.Identity.Client" version="1.1.0-preview" targetFramework="net461" />
  <package id="Newtonsoft.Json" version="6.0.1" targetFramework="net461" />
  <package id="System.Net.Http" version="4.3.1" targetFramework="net461" />
  <package id="System.Security.Cryptography.Algorithms" version="4.3.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
  <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
</packages>

I amended my main console class: 我修改了我的主控制台类:

using System;
using Microsoft.Identity.Client;

namespace OutlookCalIFConsole
{
    class Program
    {
        //Below is the clientId of your app registration. 
        //You have to replace the below with the Application Id for your app registration
        private static string ClientId = "xxxxx";

        public static PublicClientApplication PublicClientApp = new PublicClientApplication(ClientId);

        static void Main(string[] args)
        {
            Outlook oOutlook = new Outlook();

            oOutlook.AquireToken();
            if (oOutlook.ResultsText != "")
                Console.WriteLine(oOutlook.ResultsText);
        }
    }
}

And I created my Outlook class: 我创建了Outlook类:

using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace OutlookCalIFConsole
{
    class Outlook
    {

        //Set the API Endpoint to Graph 'me' endpoint
        string _graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";

        //Set the scope for API call to user.read
        string[] _scopes = new string[] { "user.read" };

        public string ResultsText { get { return strResultsText; } }
        string strResultsText = "";

        public async void AquireToken()
        {
            AuthenticationResult authResult = null;

            try
            {
                if (authResult == null)
                {
                    authResult = await Program.PublicClientApp.AcquireTokenSilentAsync(_scopes, Program.PublicClientApp.Users.FirstOrDefault());
                }
            }
            catch (MsalUiRequiredException ex)
            {
                // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
                System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");

                try
                {
                    authResult = await Program.PublicClientApp.AcquireTokenAsync(_scopes);
                }
                catch (MsalException msalex)
                {
                    strResultsText = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
                }
            }
            catch (Exception ex)
            {
                strResultsText = $"Error Acquiring Token Silently:{System.Environment.NewLine}{ex}";
                return;
            }

            if (authResult != null)
            {
                strResultsText = await GetHttpContentWithToken(_graphAPIEndpoint, authResult.AccessToken);
                Console.WriteLine(strResultsText);

                DisplayBasicTokenInfo(authResult);

                SignOut();
                if (strResultsText != "")
                    Console.WriteLine(strResultsText);
            }
        }

        /// <summary>
        /// Perform an HTTP GET request to a URL using an HTTP Authorization header
        /// </summary>
        /// <param name="url">The URL</param>
        /// <param name="token">The token</param>
        /// <returns>String containing the results of the GET operation</returns>
        public async Task<string> GetHttpContentWithToken(string url, string token)
        {
            var httpClient = new System.Net.Http.HttpClient();
            System.Net.Http.HttpResponseMessage response;
            try
            {
                var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
                //Add the token in Authorization header
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                response = await httpClient.SendAsync(request);
                var content = await response.Content.ReadAsStringAsync();
                return content;
            }
            catch (Exception ex)
            {
                return ex.ToString();
            }
        }

        private void DisplayBasicTokenInfo(AuthenticationResult authResult)
        {
            string strTokenInfoText = "";
            if (authResult != null)
            {
                strTokenInfoText += $"Name: {authResult.User.Name}" + Environment.NewLine;
                strTokenInfoText += $"Username: {authResult.User.DisplayableId}" + Environment.NewLine;
                strTokenInfoText += $"Token Expires: {authResult.ExpiresOn.ToLocalTime()}" + Environment.NewLine;
                strTokenInfoText += $"Access Token: {authResult.AccessToken}" + Environment.NewLine;
                Console.WriteLine(strTokenInfoText);
            }
        }

        public void SignOut()
        {
            strResultsText = "";
            if (Program.PublicClientApp.Users.Any())
            {
                try
                {
                    Program.PublicClientApp.Remove(Program.PublicClientApp.Users.FirstOrDefault());
                    Console.WriteLine("User has signed-out");
                }
                catch (MsalException ex)
                {
                    strResultsText = $"Error signing-out user: {ex.Message}";
                }
            }
        }
    }
}

The above is based on the examples for WPF. 以上是基于WPF的示例。 I just adapted it as I thought would be Ok for a console app. 我只是修改了它,因为我认为对于控制台应用程序来说还可以。 But when I run it I get an exception: 但是当我运行它时,我得到一个例外:

Exception thrown: 'Microsoft.Identity.Client.MsalUiRequiredException' in Microsoft.Identity.Client.dll Exception thrown: 'Microsoft.Identity.Client.MsalUiRequiredException' in mscorlib.dll Exception thrown: 'Microsoft.Identity.Client.MsalUiRequiredException' in mscorlib.dll 抛出的异常:Microsoft.Identity.Client.dll中的“ Microsoft.Identity.Client.MsalUiRequiredException”抛出的异常:mscorlib.dll中的“ Microsoft.Identity.Client.MsalUiRequiredException”抛出的异常:mscorlib中的“ Microsoft.Identity.Client.MsalUiRequiredException” .DLL

I have read up on MsalUiRequiredException and understandably it states: 我已经阅读了MsalUiRequiredException ,可以理解的是:

This exception class is to inform developers that UI interaction is required for authentication to succeed. 该异常类用于通知开发人员,成功进行身份验证需要UI交互。

So, how exactly am I supposed to get this to work? 那么,我到底该如何工作呢? I thought it would show a browser control with the needed resources for the user to interact with. 我认为它将显示一个浏览器控件,其中包含与用户进行交互所需的资源。

I can't work out how to get it to work so that I can authenticate. 我无法弄清楚如何使其能够工作以进行身份​​验证。 According to the code it should all be OK. 根据代码,一切都可以。

Update 更新

I tried changing: 我尝试更改:

static void Main(string[] args)
{
    Outlook oOutlook = new Outlook();

    new Task(oOutlook.AquireToken).Start();

    if (oOutlook.ResultsText != "")
        Console.WriteLine(oOutlook.ResultsText);
}

Makes no difference. 没有区别。

The problem is that AquireToken() is marked as async void which you should avoid unless it is an event handler. 问题在于, AquireToken()被标记为async void ,除非它是事件处理程序,否则应避免使用。 It is not possible to catch an exception as a result of calling an async void method. 由于调用async void方法而无法捕获异常。

Also, because you don't wait on the tasks created, it's entirely possible your app is exiting before the tasks complete. 另外,由于您不必等待创建的任务,因此完全有可能在任务完成之前退出您的应用。

One way to fix this is to have the main app create a Task and wait for it to complete. 解决此问题的一种方法是让主应用创建一个Task然后等待其完成。 Main can't be async so you need to explicitly block on the task rather than using await . Main不能async因此您需要显式阻止任务,而不是使用await

static void Main(string[] args)
{
    var program = new Program();
    var task = Task.Run(program.Run);
    task.Wait();
}

...then add this new method to Program : ...然后将此新方法添加到Program

private async Task Run()
{
    Outlook oOutlook = new Outlook();
    await oOutlook.AquireToken();
    if (oOutlook.ResultsText != "")
        Console.WriteLine(oOutlook.ResultsText);
}

Change AquireToken signature from: 从以下位置更改AquireToken签名:

public async void AquireToken()

...to: ...至:

public async Task AquireToken()  

Now when you run your console app you will see the login window. 现在,当您运行控制台应用程序时,您将看到登录窗口。 Here you can see it is already picking up my cached account name: 在这里,您可以看到它已经在获取我的缓存帐户名:

在此处输入图片说明

Tell me more about async/await 告诉我更多有关异步/等待的信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM