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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 06:05:56