我正在尝试通过Microsoft Exchange 2010 服务器从C++应用程序中发送电子邮件。为此,我使用库 ews-cpp 的 EWS 协议。
以下基于 https://github.com/otris/ews-cpp/blob/master/examples/send_message.cpp 示例的简单示例失败(从我的实际代码中删除了邮件地址和凭据(:
#include <iostream>
#include <ews/ews.hpp>
struct {
std::string domain = "mailserver";
std::string username = "...";
std::string password = "...";
std::string server_uri = "https://mailserver/EWS/Exchange.asmx";
} env;
int main()
{
ews::set_up();
try
{
auto service = ews::service(env.server_uri, env.domain, env.username,
env.password);
service.set_request_server_version(ews::server_version::exchange_2010_sp2);
auto message = ews::message();
message.set_subject("Test mail from outer space");
std::vector<ews::mailbox> recipients;
recipients.push_back(ews::mailbox("..."));
message.set_to_recipients(recipients);
auto text = ews::body("This is a test.");
message.set_body(text);
service.create_item(message,
ews::message_disposition::send_and_save_copy);
}
catch (ews::schema_validation_error& exc)
{
std::cout << exc.what() << std::endl;
std::cout << exc.violation() << std::endl;
}
ews::tear_down();
return 0;
}
原始 SOAP 请求如下(我添加的缩进(:
<m:CreateItem MessageDisposition="SendAndSaveCopy">
<m:Items>
<t:Message>
<t:Subject>Test mail from outer space</t:Subject>
<t:ToRecipients>
<t:Mailbox>
<t:EmailAddress>...</t:EmailAddress>
</t:Mailbox>
</t:ToRecipients>
<t:Body BodyType="Text">This is a test.</t:Body>
</t:Message>
</m:Items>
</m:CreateItem>
该函数create_item
抛出一个ews::schema_validation_error
,该被捕获在上面的代码中,打印:
The request failed schema validation
The element 'Message' in namespace ‘http://schemas.microsoft.com/exchange/services/2006/types’ has invalid child element 'Body' in namespace 'http://schemas.microsoft.com/exchange/services/2006/types'. List of possible elements expected: 'CcRecipients, BccRecipients, IsReadReceiptRequested, IsDeliveryReceiptRequested, ConversationIndex, ConversationTopic, From, InternetMessageId, IsRead, IsResponseRequested, References, ReplyTo, ReceivedBy, ReceivedRepresenting' in Namespace 'http://schemas.microsoft.com/exchange/services/2006/types'.
换句话说,EWS 不希望在 <t:Message>
内<t:Body>
。
因此,当在 SOAP 请求中省略该元素(注释掉message.set_body(text)
(时,邮件会顺利发送。但是,没有正文的邮件没有多大意义,不是吗?
我认为问题可能是ews-cpp
是为Exchange 2013编写的(并且架构在这方面在2010年至2013年之间发生了变化(。所以我深入研究了每个 Exchange 服务器在/ews/types.xsd
提供的模式,以查看架构是否允许这样的子元素。
在模式定义文件中,我找到了类型MessageType
的定义(即我们正在谈论的Message
元素的类型(:
<xs:complexType name="MessageType">
<xs:complexContent>
<xs:extension base="t:ItemType">
<xs:sequence>
<xs:element name="Sender" minOccurs="0" type="t:SingleRecipientType"/>
<xs:element name="ToRecipients" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="CcRecipients" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="BccRecipients" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="IsReadReceiptRequested" type="xs:boolean" minOccurs="0"/>
<xs:element name="IsDeliveryReceiptRequested" type="xs:boolean" minOccurs="0"/>
<xs:element name="ConversationIndex" type="xs:base64Binary" minOccurs="0"/>
<xs:element name="ConversationTopic" type="xs:string" minOccurs="0"/>
<xs:element name="From" type="t:SingleRecipientType" minOccurs="0"/>
<xs:element name="InternetMessageId" type="xs:string" minOccurs="0"/>
<xs:element name="IsRead" type="xs:boolean" minOccurs="0"/>
<xs:element name="IsResponseRequested" type="xs:boolean" minOccurs="0"/>
<xs:element name="References" type="xs:string" minOccurs="0"/>
<xs:element name="ReplyTo" type="t:ArrayOfRecipientsType" minOccurs="0"/>
<xs:element name="ReceivedBy" type="t:SingleRecipientType" minOccurs="0"/>
<xs:element name="ReceivedRepresenting" type="t:SingleRecipientType" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
如您所见,没有Body
子元素,但MessageType
基于 ItemType
,其定义如下(摘录(:
<xs:complexType name="ItemType">
<xs:sequence>
<xs:element name="MimeContent" type="t:MimeContentType" minOccurs="0"/>
<xs:element name="ItemId" type="t:ItemIdType" minOccurs="0"/>
<xs:element name="ParentFolderId" type="t:FolderIdType" minOccurs="0"/>
<xs:element name="ItemClass" type="t:ItemClassType" minOccurs="0"/>
<xs:element name="Subject" type="xs:string" minOccurs="0"/>
<xs:element name="Sensitivity" type="t:SensitivityChoicesType" minOccurs="0"/>
<xs:element name="Body" type="t:BodyType" minOccurs="0"/>
<xs:element name="Attachments" type="t:NonEmptyArrayOfAttachmentsType" minOccurs="0"/>
[...]
</xs:sequence>
</xs:complexType>
如您所见,它接受 Body
元素作为子元素。
请注意,Subject
元素也是在 ItemType
中定义的,而不是在 MessageType
中定义的,并且在上面的代码片段中被 EWS 接受。
这种奇怪的验证失败的原因可能是什么?
自己找到了解决方案:
对于元素Message
,它基于根据架构定义的Item
,EWS 首先需要 Item
的所有子元素,然后是特定于Message
的子元素。
这听起来很疯狂(对我来说(,但在<t:Message>
元素中交换<t:ToRecipients>
和<t:Body>
确实解决了问题。
我通过在代码中交换两个相应的资源库来做到这一点:
auto message = ews::message();
// First, set properties of the general "item":
message.set_subject("Test mail from outer space");
auto text = ews::body("This is a test.");
message.set_body(text);
// Then, set properties of the concrete "message":
std::vector<ews::mailbox> recipients;
recipients.push_back(ews::mailbox("..."));
message.set_to_recipients(recipients);
我不确定这是ews-cpp
,EWS/Exchange服务器还是EWS架构定义中的错误。