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

EWS API发送邮件报错:请求的Web方法不可用问题排查

问题描述

我开发了一款Outlook Web加载项,使用EWS API读取邮件属性(包括MIME内容)并在Outlook外部处理数据,该功能运行正常。新增需求为点击任务窗格按钮发送草稿邮件,我使用EmailMessage.SendAndSaveCopy()方法实现,但调用时抛出ServiceResponseException,报错信息为**"The requested web method is unavailable to this caller or application"**。

我排查后得知需要在加载项清单中提升权限至最高级ReadWriteMailbox,已完成此操作,但API调用仍失败。我在拥有完整管理员权限的私有Microsoft 365开发租户中侧加载了该加载项。

我想了解:

  1. 我的操作是否存在错误?若有,正确的做法是什么?
  2. Microsoft是否已弃用EWS的Send方法,转而推动开发者使用Graph API?

加载项清单相关片段

<Hosts>
    <Host Name="Mailbox" />
</Hosts>

<Requirements>
    <Sets>
        <Set Name="Mailbox" MinVersion="1.1" />
    </Sets>
</Requirements>

<Permissions>ReadWriteMailbox</Permissions>

<Rule xsi:type="RuleCollection" Mode="Or">
    <Rule xsi:type="ItemIs" ItemType="Message" FormType="ReadOrEdit" />
</Rule>

<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0">
    <Requirements>
        <bt:Sets DefaultMinVersion="1.3">
            <bt:Set Name="Mailbox" />
        </bt:Sets>
    </Requirements>
    <Hosts>
        <Host xsi:type="MailHost">
            <DesktopFormFactor>
                <ExtensionPoint xsi:type="MessageReadCommandSurface">
                    <OfficeTab id="TabDefault">
                        <Group id="Group.MessageRead">
                            <Label resid="Group.Label" />
                            <Control xsi:type="Button" id="HomeButton.MessageRead">
                                <!-- ... -->
                            </Control>
                        </Group>
                    </OfficeTab>
                </ExtensionPoint>
                <ExtensionPoint xsi:type="MessageComposeCommandSurface">
                    <OfficeTab id="TabDefault">
                        <Group id="Group.MessageCompose">
                            <Label resid="Group.Label" />
                            <Control xsi:type="Button" id="HomeButton.MessageCompose">
                                <!-- ... -->
                            </Control>
                        </Group>
                    </OfficeTab>
                </ExtensionPoint>
            </DesktopFormFactor>
        </Host>
    </Hosts>
</VersionOverrides>

获取邮件数据的JavaScript函数

var _getMailboxItem = function(onSuccess) {
    var ewsItemId = Office.context.mailbox.item.itemId;
    var afterSave = function() {
        var ewsUrl = Office.context.mailbox.ewsUrl;
        Office.context.mailbox.getCallbackTokenAsync({}, function(getCallbackTokenResult) {
            if(getCallbackTokenResult.status !== Office.AsyncResultStatus.Succeeded) {
                _handleOfficeError(getCallbackTokenResult, "Cannot read the mailbox item.");
                return;
            }
            var ewsToken = getCallbackTokenResult.value;
            onSuccess({ url: ewsUrl, token: ewsToken, itemId: ewsItemId });
        });
    };
    if(ewsItemId) {
        // We have the item ID already, it's safe to continue.
        afterSave(onSuccess);
    } else {
        // We must save the item first before we can read data.
        console.log("Saving the item first...");
        Office.context.mailbox.item.saveAsync(function(saveAsyncResult) {
            if(saveAsyncResult.status !== Office.AsyncResultStatus.Succeeded) {
                _handleOfficeError(saveAsyncResult, "Cannot save a draft of the mailbox item.");
                return;
            }
            ewsItemId = saveAsyncResult.value;
            afterSave(onSuccess);
        });
    }
};

发送草稿邮件的ASP.NET MVC 4.8控制器相关代码

private static ExchangeService GetExchangeService(MailboxItemModel mailboxItem) => new ExchangeService {
    Url = new Uri(mailboxItem.Url),
    Credentials = new OAuthCredentials(mailboxItem.Token)
};

private static EmailMessage GetEmailMessage(MailboxItemModel mailboxItem) => EmailMessage.Bind(
    GetExchangeService(mailboxItem),
    mailboxItem.ItemId,
    new PropertySet(BasePropertySet.IdOnly) {
        ItemSchema.IsDraft,
        ItemSchema.IsUnmodified,
        ItemSchema.MimeContent
    });

private static void Send(EmailMessage emailMessage) {
    // Safety check: if the caller didn't pass an email message, then we can't do anything.
    if(emailMessage == null) { return; }
    // Safety check: only send if EWS confirms that the message is a draft.
    if(!emailMessage.IsDraft) { return; }
    emailMessage.SendAndSaveCopy();
}

问题解答

1. 操作中的错误及修正方案

你的操作存在两处关键问题:

(1)回调令牌权限未明确指定

通过getCallbackTokenAsync获取的令牌默认仅授予读取权限,即使清单中设置了ReadWriteMailbox,也需要在调用时显式指定权限范围为ReadWrite。修改JavaScript中的调用代码:

Office.context.mailbox.getCallbackTokenAsync({isRest: false, permission: "ReadWrite"}, function(getCallbackTokenResult) {
    // 原有逻辑不变
});

注:isRest: false明确指定获取EWS专用令牌,避免返回REST格式令牌导致权限不匹配。

(2)EWS绑定的属性集需补充关键字段

在EmailMessage.Bind时,需确保加载足够的权限相关字段,可添加ItemSchema.WebClientReadFormQueryString或扩大属性集范围,确保令牌能获取发送操作的权限验证所需信息。

额外验证步骤

  • 侧加载加载项后,重新登录Outlook Web,确保权限更新生效;
  • 检查租户EWS权限策略:确认未禁用EWS API的发送操作(可通过Exchange管理中心验证);
  • 验证草稿的IsUnmodified状态:如果草稿已被外部修改,SendAndSaveCopy可能失败,可尝试先调用Update方法同步草稿内容。

2. EWS Send方法的弃用情况

Microsoft并未完全弃用EWS的Send方法,但已明确将Graph API作为未来优先开发方向:

  • EWS目前处于维护模式,仅修复严重安全漏洞,不再添加新功能;
  • Graph API提供更统一的跨平台接口,支持更多场景(如团队邮件、跨服务集成等),且权限控制更精细;
  • 对于Outlook加载项,Microsoft推荐使用Graph API的/me/sendMail或/messages/{id}/send端点实现发送功能,搭配加载项SSO令牌获取方式,比EWS更稳定且符合未来生态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 11:07:14