我使用下面的代码对 Exchange-365 服务器进行身份验证并发送电子邮件,但我得到的结果是“从服务收到的响应不包含有效的 XML”。
代码是根据此处的代码示例获取的:https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application -通过使用-oauth
我的 Entra 应用程序设置正确。
using System.Diagnostics;
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Identity.Client;
using Task = System.Threading.Tasks.Task;
namespace My.Sandbox.CoreConsoleApp;
internal class Program
{
// ReSharper disable once IdentifierTypo
private const string MyEntraAppClientId = "client-id";
// ReSharper disable once IdentifierTypo
private const string MyEntraAppTenantId = "tenant-id";
private const string MyClientSecretValue = "client-secret-value";
// ReSharper disable once IdentifierTypo
private const string MyEntraAppRedirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient";
private const string Outlook365EndpointUrl = "https://outlook.office365.com/EWS/Exchange.asmx";
/// <summary>
/// Specifies the primary mailbox against which operations should be performed. Useful in scenarios involving
/// multiple mailboxes or delegate access. Ensures accurate handling of tasks by directing requests
/// to the intended mailbox, facilitating secure and contextually relevant operations within Exchange environments.
/// </summary>
// ReSharper disable once InconsistentNaming
// ReSharper disable once IdentifierTypo
private const string Headers_XAnchormailbox = "X-AnchorMailbox";
private static void Main(string[] args)
{
Task.Run(() => ConnectUsingAppOnlyAuthentication("[email protected]")).Wait();
}
private static async Task ConnectUsingAppOnlyAuthentication(string accountAddress)
{
try
{
var authResult = GetToken();
// Configure the ExchangeService with the access token
var ewsClient = new ExchangeService
{
Url = new Uri(MyEntraAppRedirectUri),
Credentials = new OAuthCredentials(authResult.AccessToken),
ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, accountAddress)
};
// ReSharper disable once CommentTypo
//Include x-anchormailbox header
ewsClient.HttpHeaders.Add(Headers_XAnchormailbox, accountAddress);
var email = new EmailMessage(ewsClient);
email.ToRecipients.Add("[email protected]");
email.Subject = "HelloWorld";
email.Body = new MessageBody("This is the first email I've sent by using the EWS Managed API.");
await email.SendAndSaveCopy();
}
catch (MsalException ex)
{
Console.WriteLine($@"Error acquiring access token: {ex}");
}
catch (Exception ex)
{
Console.WriteLine($@"Error: {ex}");
}
if (Debugger.IsAttached)
{
Console.WriteLine(@"Hit any key to exit...");
Console.ReadKey();
}
}
private static AuthenticationResult GetToken()
{
// Using Microsoft.Identity.Client 4.22.0
var applicationBuilder = ConfidentialClientApplicationBuilder
.Create(MyEntraAppClientId)
.WithClientSecret(MyClientSecretValue)
.WithTenantId(MyEntraAppTenantId)
.Build();
// The permission scope required for EWS access
var ewsScopes = new string[] { Scope.Outlook365_Default.Value };
// Make the token request
return applicationBuilder.AcquireTokenForClient(ewsScopes).ExecuteAsync().Result;
}
/// <summary>
/// Represents an OAuth scope, which determines the resources and application can access
/// and the actions it can perform.
/// </summary>
[DebuggerDisplay("{Value}")]
internal class Scope
{
#region constructors
private Scope(string value)
{
Value = value;
}
#endregion
#region properties
public string Value { get; }
#endregion
#region static members
// ReSharper disable once InconsistentNaming
public static readonly Scope Outlook365_Ews_AccessAsUser_All =
new("https://outlook.office365.com/EWS.AccessAsUser.All");
/// <summary>
/// The default scope for Exchange Online ("https://outlook.office365.com/.default")
/// </summary>
public static readonly Scope Outlook365_Default =
new("https://outlook.office365.com/.default");
#endregion
}
}
我遇到的例外是:
Microsoft.Exchange.WebServices.Data.ServiceRequestException: The response received from the service didn't contain valid XML. ---> System.Xml.XmlException: Root element is missing.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlAsyncCheckReader.Read()
at System.Xml.XmlCharCheckingReader.Read()
at Microsoft.Exchange.WebServices.Data.EwsXmlReader.Read()
at Microsoft.Exchange.WebServices.Data.EwsXmlReader.Read(XmlNodeType nodeType)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadXmlDeclaration(EwsServiceXmlReader reader)
--- End of inner exception stack trace ---
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadXmlDeclaration(EwsServiceXmlReader reader)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadPreamble(EwsServiceXmlReader ewsXmlReader)
at Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadResponse(EwsServiceXmlReader ewsXmlReader, WebHeaderCollection responseHeaders)
at Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.ReadResponseXml(Stream responseStream, WebHeaderCollection responseHeaders)
at Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.ReadResponse(IEwsHttpWebResponse response)
at Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.<InternalExecuteAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Exchange.WebServices.Data.MultiResponseServiceRequest`1.<ExecuteAsync>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.Exchange.WebServices.Data.Item.<InternalCreate>d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.Exchange.WebServices.Data.EmailMessage.<InternalSend>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
var ewsClient = new ExchangeService
{
Url = new Uri(MyEntraAppRedirectUri),
Credentials = new OAuthCredentials(authResult.AccessToken),
ImpersonatedUserId =
new ImpersonatedUserId(ConnectingIdType.SmtpAddress, accountAddress)
};
不正确,您在 ExchangeService 构造函数中使用了不正确的 URL,我认为您打算使用
URL = new Uri(Outlook365EndpointUrl),
因为您使用了 MyEntraAppRedirectUri,所以您对该端点的任何 SOAP 请求返回的响应都将是 HTML 错误。