如何通过MS Graph API回复邮件并保留历史邮件线程内容
问题描述
我正在通过UI使用MS Graph API发送回复邮件。用户会选择要回复的邮件,撰写新邮件内容并添加所需收件人(To、Cc、Bcc)。我捕获目标邮件的Message ID进行回复,邮件虽在Outlook中显示为回复链,但正文未像Outlook原生回复那样包含历史邮件线程内容。请问是否有方法实现该效果,或是我的操作存在错误?
当前实现代码:
public async Task SendReplyAsync(string EmailAccount, Email email, string MessageId) { var tries = 0; var maxRetries = 1; while (tries <= maxRetries) { tries++; try { var ToRec = new List<Recipient>(); foreach (var address in email.To) { ToRec.Add( new Recipient() { EmailAddress = new EmailAddress { Address = address.EmailAddress, }, }); } var CcRec = new List<Recipient>(); foreach (var address in email.Cc) { CcRec.Add( new Recipient() { EmailAddress = new EmailAddress { Address = address.EmailAddress, }, }); } var BccRec = new List<Recipient>(); foreach (var address in email.Bcc) { BccRec.Add( new Recipient() { EmailAddress = new EmailAddress { Address = address.EmailAddress, }, }); } var attachments = new MessageAttachmentsCollectionPage(); foreach (var attachment in email.FileAttachments) { attachments.Add(new Microsoft.Graph.FileAttachment { Name = attachment.FileName, ContentType = attachment.ContentType, ContentBytes = attachment.ContentBytes }); } var message = new Message { Body = new ItemBody { ContentType = Microsoft.Graph.BodyType.Html, Content = email.Body, }, ToRecipients = ToRec, CcRecipients = CcRec, BccRecipients = BccRec, Attachments = attachments, }; await _graphServiceClient.Users[EmailAccount].Messages[MessageId].Reply(message, "").Request().PostAsync(); tries = maxRetries + 1; } catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge")) { try { Console.WriteLine($"{svcex}"); string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders); _consentHandler.ChallengeUser(initialScopes, claimChallenge); } catch (Exception ex2) { _consentHandler.HandleException(ex2); } } } }
解决方案
你的操作核心问题是:MS Graph API的Reply方法仅负责关联邮件线程(自动设置In-Reply-To和References头),不会自动插入原邮件的历史内容。要实现Outlook原生回复的效果,需要手动获取原邮件内容并拼接到回复正文中。
修改后的代码示例:
public async Task SendReplyAsync(string EmailAccount, Email email, string MessageId) { var tries = 0; var maxRetries = 1; while (tries <= maxRetries) { tries++; try { // 1. 获取原邮件的关键信息:正文、发件人、接收时间 var originalMessage = await _graphServiceClient.Users[EmailAccount].Messages[MessageId] .Request() .Select(m => new { m.Body, m.From, m.ReceivedDateTime, m.Subject }) .GetAsync(); // 2. 构建符合Outlook样式的原邮件引用内容 string originalContent = string.Empty; if (originalMessage.Body.ContentType == BodyType.Html) { // HTML格式原邮件的引用样式 originalContent = $@"<hr> <p><strong>发件人:</strong> {originalMessage.From.EmailAddress.Name} <{originalMessage.From.EmailAddress.Address}></p> <p><strong>发送时间:</strong> {originalMessage.ReceivedDateTime?.ToString("yyyy-MM-dd HH:mm:ss")}</p> <p><strong>主题:</strong> {originalMessage.Subject}</p> <br> {originalMessage.Body.Content}"; } else { // 纯文本格式原邮件的引用样式 originalContent = $@" ---------------------- 发件人: {originalMessage.From.EmailAddress.Name} <{originalMessage.From.EmailAddress.Address}> 发送时间: {originalMessage.ReceivedDateTime?.ToString("yyyy-MM-dd HH:mm:ss")} 主题: {originalMessage.Subject} {originalMessage.Body.Content}"; } // 3. 拼接用户撰写的新内容和原邮件引用 string replyBody = $"{email.Body}{originalContent}"; // 以下收件人、附件处理逻辑保持不变 var ToRec = new List<Recipient>(); foreach (var address in email.To) { ToRec.Add( new Recipient() { EmailAddress = new EmailAddress { Address = address.EmailAddress, }, }); } var CcRec = new List<Recipient>(); foreach (var address in email.Cc) { CcRec.Add( new Recipient() { EmailAddress = new EmailAddress { Address = address.EmailAddress, }, }); } var BccRec = new List<Recipient>(); foreach (var address in email.Bcc) { BccRec.Add( new Recipient() { EmailAddress = new EmailAddress { Address = address.EmailAddress, }, }); } var attachments = new MessageAttachmentsCollectionPage(); foreach (var attachment in email.FileAttachments) { attachments.Add(new Microsoft.Graph.FileAttachment { Name = attachment.FileName, ContentType = attachment.ContentType, ContentBytes = attachment.ContentBytes }); } var message = new Message { Body = new ItemBody { ContentType = Microsoft.Graph.BodyType.Html, Content = replyBody, // 使用拼接后的完整内容 }, ToRecipients = ToRec, CcRecipients = CcRec, BccRecipients = BccRec, Attachments = attachments, }; await _graphServiceClient.Users[EmailAccount].Messages[MessageId].Reply(message, "").Request().PostAsync(); tries = maxRetries + 1; } catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge")) { try { Console.WriteLine($"{svcex}"); string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders); _consentHandler.ChallengeUser(initialScopes, claimChallenge); } catch (Exception ex2) { _consentHandler.HandleException(ex2); } } } }
关键说明
- 必须主动调用Graph API获取原邮件的
Body、From、ReceivedDateTime等信息,才能构建历史线程内容。 - 代码中模拟了Outlook默认的回复引用样式,你可以根据需求调整HTML结构或纯文本格式,比如修改分隔线样式、字体颜色等。
- 注意区分原邮件的格式(HTML/纯文本),避免格式冲突导致显示异常。
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

