我正在尝试复制将Outlook.COM与控制台应用程序一起使用的Microsoft示例。因此,我创建了一个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>
我修改了我的主控制台类:
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);
}
}
}
我创建了我的
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}";
}
}
}
}
}
以上是基于WPF的示例。我只是修改了它,因为我认为对于控制台应用程序来说还可以。但是当我运行它时,我得到一个例外:
引发异常:Microsoft.Identity.Client.dll中的“ Microsoft.Identity.Client.MsalUiRequiredException”
引发异常:mscorlib.dll中的'Microsoft.Identity.Client.MsalUiRequiredException'
引发异常:mscorlib.dll中的'Microsoft.Identity.Client.MsalUiRequiredException'
我已经阅读了MsalUiRequiredException,并且可以理解地指出:
该异常类用于通知开发人员,成功进行身份验证需要UI交互。
那么,我到底该如何工作呢?我认为它将显示一个浏览器控件,其中包含与用户进行交互所需的资源。
我无法弄清楚如何使其能够工作以进行身份验证。根据代码,一切都可以。
更新资料
我尝试更改:
static void Main(string[] args)
{
Outlook oOutlook = new Outlook();
new Task(oOutlook.AquireToken).Start();
if (oOutlook.ResultsText != "")
Console.WriteLine(oOutlook.ResultsText);
}
没有区别。
最佳答案
问题是AquireToken()
被标记为async void
,除非它是事件处理程序,否则应避免使用。调用async void
方法导致无法捕获异常。
另外,由于您不必等待创建的任务,因此很有可能在任务完成之前退出应用程序。
解决此问题的一种方法是让主应用创建Task
并等待其完成。 Main
不能为async
,因此您需要显式阻止该任务,而不是使用await
。
static void Main(string[] args)
{
var program = new Program();
var task = Task.Run(program.Run);
task.Wait();
}
...然后将此新方法添加到
Program
:private async Task Run()
{
Outlook oOutlook = new Outlook();
await oOutlook.AquireToken();
if (oOutlook.ResultsText != "")
Console.WriteLine(oOutlook.ResultsText);
}
从以下位置更改
AquireToken
签名:public async void AquireToken()
...至:
public async Task AquireToken()
现在,当您运行控制台应用程序时,您将看到登录窗口。在这里,您可以看到它已经在获取我的缓存帐户名:
告诉我更多有关异步/等待的信息
Async/Await - Best Practices in Asynchronous Programming
关于c# - 将Microsoft.Graph与控制台应用程序一起使用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45895799/