使用MS Graph API发送邮件后复制至文件夹时偶现复制草稿的问题排查
问题分析与解决方案
核心问题原因
你遇到的偶发现象,本质是邮件发送操作的异步处理延迟:调用CreateReply生成草稿后,执行Send操作仅触发了后台发送流程,并非等待邮件完全转换为已发送状态并完成系统同步。即使你await了Send.PostAsync,此时用草稿ID执行复制操作,仍可能在状态未同步的瞬间,复制到的还是草稿状态的邮件。
另外你的代码存在一处小错误:BccRecipients的赋值误用了email.Cc,应改为email.Bcc,该错误会导致Bcc与Cc收件人重复,但不是引发主问题的原因。
针对性解决方案
方案1:发送后查询已发送邮件的真实ID
发送完成后,不再直接使用草稿ID,而是通过邮件的internetMessageId(全局唯一标识)去已发送文件夹中查询对应的已发送邮件,用查询到的正确ID执行复制:
public async Task<Models.Email?> SendReplyToEmailAsync(string mailBox, string messageId, AddEmailDTO email) { var requestBody = new CreateReplyPostRequestBody { Message = new Message { ToRecipients = email.To?.Select(contact => new Recipient { EmailAddress = new Microsoft.Graph.Models.EmailAddress { Address = contact.EmailAddress, Name = contact.Name } }).ToList(), CcRecipients = email.Cc?.Select(contact => new Recipient { EmailAddress = new Microsoft.Graph.Models.EmailAddress { Address = contact.EmailAddress, Name = contact.Name } }).ToList(), // 修正Bcc赋值错误 BccRecipients = email.Bcc?.Select(contact => new Recipient { EmailAddress = new Microsoft.Graph.Models.EmailAddress { Address = contact.EmailAddress, Name = contact.Name } }).ToList(), Subject = email.Subject, Body = new ItemBody { ContentType = BodyType.Html, Content = email.Body, }, }, }; var draft = await _graphServiceClient.Users[mailBox].Messages[messageId].CreateReply.PostAsync(requestBody, rc => { rc.Headers.Add("Prefer", "IdType=\"ImmutableId\""); }); if(draft != null) { await _graphServiceClient.Users[mailBox].Messages[draft.Id].Send.PostAsync( rc => { rc.Headers.Add("Prefer", "IdType=\"ImmutableId\""); }); // 从已发送文件夹查询对应邮件 var sentMailResult = await _graphServiceClient.Users[mailBox].MailFolders["sentitems"].Messages .GetAsync(request => { request.QueryParameters.Filter = $"internetMessageId eq '{draft.InternetMessageId}'"; request.Headers.Add("Prefer", "IdType=\"ImmutableId\""); }); var sentMail = sentMailResult.Value?.FirstOrDefault(); return sentMail != null ? await GetEmailAsync(mailBox, sentMail.Id!) : null; } else { return null; } }
方案2:跳过草稿环节,直接发送回复
如果不需要保留草稿,可以直接使用SendMail接口完成回复,彻底避免草稿状态的中间环节:
public async Task<Models.Email?> SendReplyToEmailAsync(string mailBox, string messageId, AddEmailDTO email) { // 获取原邮件信息,用于构建回复引用 var originalMessage = await _graphServiceClient.Users[mailBox].Messages[messageId].GetAsync(); var sendMailBody = new SendMailPostRequestBody { Message = new Message { ToRecipients = email.To?.Select(contact => new Recipient { EmailAddress = new Microsoft.Graph.Models.EmailAddress { Address = contact.EmailAddress, Name = contact.Name } }).ToList(), CcRecipients = email.Cc?.Select(contact => new Recipient { EmailAddress = new Microsoft.Graph.Models.EmailAddress { Address = contact.EmailAddress, Name = contact.Name } }).ToList(), BccRecipients = email.Bcc?.Select(contact => new Recipient { EmailAddress = new Microsoft.Graph.Models.EmailAddress { Address = contact.EmailAddress, Name = contact.Name } }).ToList(), Subject = email.Subject, Body = new ItemBody { ContentType = BodyType.Html, Content = email.Body, }, // 添加回复引用,保持邮件线程关联 InReplyTo = originalMessage.InternetMessageId, References = originalMessage.References ?? originalMessage.InternetMessageId }, SaveToSentItems = true // 强制保存到已发送文件夹 }; await _graphServiceClient.Users[mailBox].SendMail.PostAsync(sendMailBody, rc => { rc.Headers.Add("Prefer", "IdType=\"ImmutableId\""); }); // 查询刚发送的邮件 var sentMailResult = await _graphServiceClient.Users[mailBox].MailFolders["sentitems"].Messages .GetAsync(request => { request.QueryParameters.Filter = $"internetMessageId eq '{sendMailBody.Message.InternetMessageId}'"; request.Headers.Add("Prefer", "IdType=\"ImmutableId\""); request.QueryParameters.Orderby = new[] {"receivedDateTime desc"}; request.QueryParameters.Top = 1; }); var sentMail = sentMailResult.Value?.FirstOrDefault(); return sentMail != null ? await GetEmailAsync(mailBox, sentMail.Id!) : null; }
额外建议
- 临时缓解可在复制操作前添加短暂延迟(如500ms),但此方法不可靠,仅作为过渡方案。
- 检查
GetEmailAsync方法,确保其从已发送文件夹而非默认收件箱/草稿箱获取邮件。
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

