Exchange Web Service(EWS) 协议同步邮件

来源:互联网 发布:大数据时代知乎 编辑:程序博客网 时间:2024/04/29 01:33

Exchange Web Service(EWS) 协议

EWS是微软实现的一种客户端和服务器之间的交换信息的协议。Exchange Server提供了包括从电子邮件、会议安排、团体日程管理、任务管理、文档管理、实时会议和工作流等丰富的协作应用。

EWS基于标准的web service, 使用HTTP+XML+SOAP来传输消息的。一条 SOAP 消息就是一个普通的 XML 文档,包含下列元素:

  • 必需的 Envelope 元素,可把此 XML 文档标识为一条 SOAP 消息
  • 可选的 Header 元素,包含头部信息
  • 必需的 Body 元素,包含所有的调用和响应信息
  • 可选的 Fault 元素所发生错误的信息,提供有关在处理此消息

EWS的SOAP消息,Header中指明使用的EWS版本信息:

<s:Header>    <t:RequestServerVersion s:mustUnderstand="1" Version="Exchange2010_SP1"/></s:Header>

Body中是真正的命令信息,指明要调用的接口和使用的参数:

<s:Body>    <m:GetFolder>        <m:FolderShape>            <t:BaseShape>IdOnly</t:BaseShape>            <t:AdditionalProperties>                <t:FieldURI FieldURI="folder:DistinguishedFolderId"/>            </t:AdditionalProperties>        </m:FolderShape>        <m:FolderIds>            <t:DistinguishedFolderId Id="deleteditems"/>        </m:FolderIds>    </m:GetFolder></s:Body>

当请求成功是,响应的内容也是放在Body当中:

<s:Body> <m:GetFolderResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">        <m:ResponseMessages>            <m:GetFolderResponseMessage ResponseClass="Success">                <m:ResponseCode>NoError</m:ResponseCode>                <m:Folders>                    <t:Folder>                        <t:FolderId Id="AQMkADAwATMwMAItZDAyNi05ODYxLTAwAi0wMAoALgAAA428HhUZWjFJpUSRJeFqnBwBAHQq8eP3dM1JkMChdMfyLSgAAAIBCgAAAA==" ChangeKey="AQAAABYAAAB0KvHj93TNSZDAoXTH8i0oAAB17L8t" />                    </t:Folder>                </m:Folders>            </m:GetFolderResponseMessage>        </m:ResponseMessages>    </m:GetFolderResponse></s:Body>

EWS有多个实现版本,每个版本支持的请求都描述在以下三个文件中:

  • services.wsdl : 定义了客户端和服务器之间支持的消息协议
  • Messages.xds: 定义了SOAP消息请求和响应的信息类型
  • Types.xds:定义了SOAP消息使用的数据类型

要访问EWS服务器, 可以用这三个文件自动生成soap访问接口,但是生成的接口可读性比较差,使用起来很不方便。所以在我的项目中,我直接拼装SOAP消息来构建我的请求和解析服务器的响应。

在邮箱客户端中, 主要涉及到的操作如下表所示:

folder操作 item操作 FindFolder operation CopyItem operation GetFolder operation CreateItem operation CreateFolder operation FindItem operation UpdateFolder operation GetItem operation DeleteFolder operation MoveItem operation SyncFolderHierar operation - - SyncFolderItems operation - SendItem operation - UpdateItem operation - GetItem operation

可以看到,EWS支持对邮件folder和邮件对象的增删改查,还支持对folder和item的增量同步。这几个操作是邮箱功能的核心操作,从EWS一开始的版本就开始支持,并不存在版本兼容性的问题。

EWS对每一个folder和item对象都维护了一个changeKey,来表示对象的版本。因此,可以通过changeKey是否变化来判断folder或者item是否发生了改变。EWS为了可以方便的让客户端知道服务器有哪些对象发生了改变,对folder提供了SyncFolderHierar接口用于增量同步folder,对item提供了SyncFolderItems用于增量同步item。这两个增量同步的接口使用了SyncState用于标识同步状态, 如果服务器上的数据发生了改变, 则在响应只返回变更的对象,并将最新的SyncState下发给客户端。下面是这两个命令的具体请求和返回数据示例:

SyncFolderHierar Request

<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">  <soap:Body>    <SyncFolderHierarchy  xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">      <FolderShape>        <t:BaseShape>AllProperties</t:BaseShape>      </FolderShape>      <SyncState>H4sIA=</SyncState>    </SyncFolderHierarchy>  </soap:Body></soap:Envelope>

SyncFolderHierar Response

<?xml version="1.0" encoding="utf-8" ?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                xmlns:xsd="http://www.w3.org/2001/XMLSchema">  <soap:Header>    <t:ServerVersionInfo MajorVersion="8" MinorVersion="0"                          MajorBuildNumber="628" MinorBuildNumber="0"                          xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" />  </soap:Header>  <soap:Body>    <SyncFolderHierarchyResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"                                  xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"                                  xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">      <m:ResponseMessages>        <m:SyncFolderHierarchyResponseMessage ResponseClass="Success">          <m:ResponseCode>NoError</m:ResponseCode>          <m:SyncState>H4sIAAA==</m:SyncState>          <m:IncludesLastFolderInRange>true</m:IncludesLastFolderInRange>          <m:Changes>            <t:Create>              <t:Folder>                <t:FolderId Id="AQApAHR=" ChangeKey="AQAAABY" />                <t:ParentFolderId Id="AQApA=" ChangeKey="AQAAAA==" />                <t:FolderClass>IPF.Note</t:FolderClass>                <t:DisplayName>NewFolder</t:DisplayName>                <t:TotalCount>0</t:TotalCount>                <t:ChildFolderCount>0</t:ChildFolderCount>                <t:UnreadCount>0</t:UnreadCount>              </t:Folder>            </t:Create>          </m:Changes>        </m:SyncFolderHierarchyResponseMessage>      </m:ResponseMessages>    </SyncFolderHierarchyResponse>  </soap:Body></soap:Envelope>

其中IncludesLastFolderInRange节点是告知是否已经返回了所有的变更信息,如果没有,则需要再用新的SyncState再请求多一次,直到IncludesLastFolderInRange节点为true。

SyncFolderItems Request

<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages">    <s:Header>        <t:RequestServerVersion s:mustUnderstand="1" Version="Exchange2010_SP1" />    </s:Header>    <s:Body>        <m:SyncFolderItems>            <m:ItemShape>                <t:BaseShape>IdOnly</t:BaseShape>                <t:IncludeMimeContent>false</t:IncludeMimeContent>                <t:AdditionalProperties>                    <t:FieldURI FieldURI="item:DateTimeSent" />                </t:AdditionalProperties>            </m:ItemShape>            <m:SyncFolderId>                <t:FolderId Id="AQMkADAwATMwMAItZDAyNi05ODYxLTAwAi0wMAoALgAAA428HhUZWjFJpUSRJeFqnBwBAHQq8eP3dM1JkMChdMfyLSgAAAIBCQAAAA==" />            </m:SyncFolderId>            <m:SyncState>H4sIAAAAAAAEAGNgYGcAAotqE0tHE2NTA0ddZ3NHC10TR2djXSdnJ2ddNyMnC2cnczdLU1OD2vBgveDKvOTgksSSVOfEvMSiSgYr0nW65eekpBZ5pjBYkq43LLWoODM/j8GaaK3+QMuKS4JSk1Mzy1JTQjJzU0nwrU9icYlnXnFJYl5yqncqKb71zS9K9SxJzS32zwtOLSpLLSLByXDfhgNxUW5iUTYklrgYGISA0tDwAxkOUskgCJQyAGI9kJrePXKiklGGnktdJqo+zJojw8hQovXx8feSs54TDiwsOf5JVwOkipETiBn4GJhBHG6GoCfHOrpTrngwCAFFeYEYaN1aRgYGX8cAT19HP5AiBjdTtzCQ8gAgVmVAgH9AnIvELwXiTgZMkA00zwyJb2BgYAzEBropBkZmupYWZoa6YK4BEsBwOxMDAwsDg/MDZnZn86B9BwJYpFr185nl2yrt43452fdfahanntrERWEWD</m:SyncState>            <m:MaxChangesReturned>20</m:MaxChangesReturned>        </m:SyncFolderItems>    </s:Body></s:Envelope>

SyncFolderItems Response

<?xml version="1.0"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">    <s:Header>        <h:ServerVersionInfo xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="947" MinorBuildNumber="19" Version="V2017_01_07" />    </s:Header>    <s:Body>        <m:SyncFolderItemsResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">            <m:ResponseMessages>                <m:SyncFolderItemsResponseMessage ResponseClass="Success">                    <m:ResponseCode>NoError</m:ResponseCode>                    <m:SyncState>H4sIAAAAAAAEAGNgYGcAAotqE0tHE2NTA0ddZ3NHC10TR2djXSdnJ2ddNyMnC2cnczdLU1OD2vBgveDKvOTgksSSVOfEvMSiSgYr0nW65eekpBZ5pjBYkq43LLWoODM/j8GaaK3+QMuKS4JSk1Mzy1JTQjJzU0nwrU9icYlnXnFJYl5yqncqKb71zS9K9SxJzS32zwtOLSpLLSLByXDfhgNxUW5iUTYklrgYGISA0tDwAxkOUskgCJQyAGI9kJrePXKiklGGnktdJqo+zJojw8hQovXx8feSs54TDiwsOf5JVwOkipETiBn4GJhBHG6GoCfHOrpTrngwCAFFeYEYaN1aRgYGX8cAT19HP5AiBjdTtzCQ8gAgVmVAgH9AnIvELwXiTgZMkA00zwyJb2BgYAzEBropBkZmupYWZoa6YK4BEsBwOxMDAwsDg/MDZnZn86B9BwJYpFr185nlwxyDzC0CWBQuJ2YxK6W7BvHwBzCretwLausPYLZeEhJ0/EQAs8s9saBbtwNY3DZHvGH2U4oIEpUMYA58eDzI1S2AOWTZsiA3zwDm8HtNQU6uAcwRLPeCGpsCmKPcHgd9+h4QgM0hzNx7vwU9egK0YZOnkxdTAJP93qAVf1cCrXZ5X+50lwlo1VG/X8x+5hlOrauAVv2ocZJiAlq1b7fTOi2gVd+2OjEB6QhZU6elQPGoBHenGasYF2G3LAgUaSVT3HcGMNwLYd+tVs/hO1n/X9VcpR972cDBychQzMDQAADqPT+hzwMAAA==</m:SyncState>                    <m:IncludesLastItemInRange>true</m:IncludesLastItemInRange>                    <m:Changes>                        <t:Create>                            <t:MeetingRequest>                                <t:ItemId Id="AQMkADAwATMwMAItZDAyNi05ODYxLTAwAi0wMAoARgAAA428HhUZWjFJpUSRJeFqnBwHAHQq8eP3dM1JkMChdMfyLSgAAAIBCQAAAHQq8eP3dM1JkMChdMfyLSgAAAAHQze+AAAA" ChangeKey="CwAAABYAAAB0KvHj93TNSZDAoXTH8i0oAAALvfbi" />                                <t:DateTimeSent>2016-09-26T04:36:53Z</t:DateTimeSent>                            </t:MeetingRequest>                        </t:Create>                        <t:Update>                            <t:MeetingRequest>                                <t:ItemId Id="AQMkADAwATMwMAItZDAyNi05ODYxLTAwAi0wMAoARgAAA428HhUZWjFJpUSRJeFqnBwHAHQq8eP3dM1JkMChdMfyLSgAAAIBCQAAAHQq8eP3dM1JkMChdMfyLSgAAAAHQze/AAAA" ChangeKey="CwAAABYAAAB0KvHj93TNSZDAoXTH8i0oAAALvfbj" />                                <t:DateTimeSent>2016-09-26T04:38:57Z</t:DateTimeSent>                            </t:MeetingRequest>                        </t:Update>                        <t:Delete>                            <t:MeetingRequest>                                <t:ItemId Id="AQMkADAwATMwMAItZDAyNi05ODYxLTAwAi0wMAoARgAAA428HhUZWjFJpUSRJeFqnBwHAHQq8eP3dM1JkMChdMfyLSgAAAIBCQAAAHQq8eP3dM1JkMChdMfyLSgAAABaRuP3AAAA&quot;" ChangeKey="CQAAABYAAAB0KvHj93TNSZDAoXTH8i0oAABaYEei" />                            </t:MeetingRequest>                        </t:Delete>                    </m:Changes>                </m:SyncFolderItemsResponseMessage>            </m:ResponseMessages>        </m:SyncFolderItemsResponse>    </s:Body></s:Envelope>

其中IncludesLastItemInRange节点是告知是否已经返回了所有的变更信息,如果没有,则需要再用新的SyncState再请求多一次,直到IncludesLastFolderInRange节点为true。

使用这两个同步的接口,就可以方便的获取到服务器变更的内容。
理论上,同步folder中的邮件列表,使用SyncFolderItems这个命令就足够了,在将SyncState更新到最新状态时,客户端已经同步到了folder中所有的邮件的id,之后便可以拉取所有的邮件的摘要了。但是在移动客户端中,下载所有的邮件并不是非常理智的行为,非常耗费流量和存储空间。因此在移动客户端中,初始化folder时对邮件的同步数目需要进行限制。为了解决这个问题,在移动客户端中,对邮件里列表的同步分为一下三个操作:

  • 初始化阶段:获取最新的SyncState,对这个过程中返回的所有邮件id截取最新的n个id进行邮件下载,存储。
  • 下拉更新阶段:使用当前存储的SyncState拉取到所有变化的邮件id,全部进行下载存储。
  • 加载更多阶段:用当前存储的最旧邮件的发送时间time1,使用findItem命令搜索不晚于time1时间发送的最近n封邮件邮件id,进行下载存储。

结合SyncFolderItems和findItem两个请求,就可以不需要在初始化邮件列表时中同步所有邮件,但是又可以支持加载旧邮件的需求。


0 0
原创粉丝点击