You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Exchange Online EWS调用返回无效XML错误排查求助

解决EWS OAuth认证发送邮件时的"The response received from the service didn't contain valid XML"错误

问题描述

我使用以下代码通过OAuth认证Exchange Online(Exchange 365)服务器并发送邮件,但收到错误提示:"The response received from the service didn't contain valid XML"。已参考官方文档配置,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("my-email-address@My2.onmicrosoft.com")).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("test-target-email@gmail.com");
            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: 从服务接收的响应不包含有效的XML。 ---> System.Xml.XmlException: 缺少根元素。
   在 System.Xml.XmlTextReaderImpl.Throw(Exception e)
   在 System.Xml.XmlTextReaderImpl.ParseDocumentContent()
   在 System.Xml.XmlTextReaderImpl.Read()
   在 System.Xml.XmlAsyncCheckReader.Read()
   在 System.Xml.XmlCharCheckingReader.Read()
   在 Microsoft.Exchange.WebServices.Data.EwsXmlReader.Read()
   在 Microsoft.Exchange.WebServices.Data.EwsXmlReader.Read(XmlNodeType nodeType)
   在 Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadXmlDeclaration(EwsServiceXmlReader reader)
   --- 内部异常堆栈跟踪结束 ---
   在 Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadXmlDeclaration(EwsServiceXmlReader reader)
   在 Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadPreamble(EwsServiceXmlReader ewsXmlReader)
   在 Microsoft.Exchange.WebServices.Data.ServiceRequestBase.ReadResponse(EwsServiceXmlReader ewsXmlReader, WebHeaderCollection responseHeaders)
   在 Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.ReadResponseXml(Stream responseStream, WebHeaderCollection responseHeaders)
   在 Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.ReadResponse(IEwsHttpWebResponse response)
   在 Microsoft.Exchange.WebServices.Data.SimpleServiceRequestBase.<InternalExecuteAsync>d__1.MoveNext()
--- 之前引发异常的位置的堆栈跟踪结束 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   在 Microsoft.Exchange.WebServices.Data.MultiResponseServiceRequest`1.<ExecuteAsync>d__6.MoveNext()
--- 之前引发异常的位置的堆栈跟踪结束 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   在 Microsoft.Exchange.WebServices.Data.Item.<InternalCreate>d__12.MoveNext()
--- 之前引发异常的位置的堆栈跟踪结束 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   在 Microsoft.Exchange.WebServices.Data.EmailMessage.<InternalSend>d__6.MoveNext()
--- 之前引发异常的位置的堆栈跟踪结束 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.GetResult()

解决方案

  • 核心错误修正:你把OAuth跳转地址MyEntraAppRedirectUri赋值给了ExchangeService.Url,这是错误的。EWS客户端需要指向Exchange Online的EWS端点,把这一行代码修改为:
    Url = new Uri(Outlook365EndpointUrl),
    
  • 优化异步调用:当前GetToken方法用Result同步阻塞获取Token,建议改为异步实现,避免死锁风险:
    private static async Task<AuthenticationResult> GetTokenAsync()
    {
        var applicationBuilder = ConfidentialClientApplicationBuilder
            .Create(MyEntraAppClientId)
            .WithClientSecret(MyClientSecretValue)
            .WithTenantId(MyEntraAppTenantId)
            .Build();
    
        var ewsScopes = new string[] { Scope.Outlook365_Default.Value };
        return await applicationBuilder.AcquireTokenForClient(ewsScopes).ExecuteAsync();
    }
    
    然后在ConnectUsingAppOnlyAuthentication中调用:
    var authResult = await GetTokenAsync();
    
  • 权限确认:因为是应用-only模式,确保Entra应用已添加EWS.AccessAsApp应用权限,并且完成了管理员同意操作。

内容的提问来源于stack exchange,提问作者user884248

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 23:54:58