EWS 客户端应用程序入门Get started with EWS client applications

使用 Exchange 中的 Exchange Web 服务(EWS),创建你的第一个应用程序。Create your first application by using Exchange Web Services (EWS) in Exchange.

EWS 是一种全面的服务,应用程序可以使用 EWS 来访问几乎所有存储在 Exchange Online、作为 Office 365 一部分的 Exchange Online 或 Exchange 本地邮箱中的信息。EWS is a comprehensive service that your applications can use to access almost all the information stored in an Exchange Online, Exchange Online as part of Office 365, or Exchange on-premises mailbox. EWS 使用标准 Web 协议,对 Exchange 服务器提供访问权限;诸如 EWS 托管 API 等库会环绕 EWS 操作,提供面向对象的界面。EWS uses standard web protocols to provide access to an Exchange server; libraries like the EWS Managed API wrap the EWS operations to provide an object-oriented interface. 运行本文中的示例后,你将基本了解可以使用 EWS 执行的操作。After you've run the examples in this article, you will have a basic understanding of what you can do with EWS.

可从任意操作系统或语言中调用 EWS 操作,因为 EWS 请求和响应使用 SOAP 协议。You can call EWS operations from any operating system or language, because the EWS requests and responses use the SOAP protocol. 本文中的示例编写自 C# ,并利用了 .NET Frameork HttpWebRequestHttpWebResponse 对象;但是,代码的重要部分是用于构建 EWS 请求的 XML,以及从服务器返回的 XML 响应。The examples in this article are written using C# and make use of the .NET Framework HttpWebRequest and HttpWebResponse objects; however, the important part of the code is the XML used to make the EWS request and the XML response returned from the server. 代码示例强调 XML 事务,和不处理 XML。The code examples emphasize the XML transactions and not processing the XML.

你将需要 Exchange 服务器You'll need an Exchange server

如果你已经有一个 Exchange 邮箱帐户,则可以跳过此步骤。If you already have an Exchange mailbox account, you can skip this step. 否则,你必须具备以下选项来为密的首个 EWS 应用程序设置 Exchange 邮箱:Otherwise, you have the following options for setting up an Exchange mailbox for your first EWS application:

在你确认可以从 Exchange 服务器发送和接收电子邮件后,你便可以设置你的开发环境了。After you've verified that you can send and receive email from your Exchange server you are ready to set up your development environment. 可以使用 Outlook Web App 来验证你是否可以发送电子邮件。You can use Outlook Web App to verify that you can send email.

你还需要了解服务器的 EWS 端点 URL。You'll also need to know the URL of the EWS endpoint for your server. 在制作应用程序的过程中,你会使用 “自动发现” 来确定 EWS URL。In a production application, you'd use Autodiscover to determine the EWS URL. 本文中的示例使用 Office 365 EWS 端点 URL,https://outlook.office365.com/EWS/Exchange.asmxThe examples in this article use the Office 365 EWS endpoint URL, https://outlook.office365.com/EWS/Exchange.asmx. 等你准备好以后,“后续步骤” 部分中有更多关于“自动发现”详细信息的链接。The Next steps section has links to more information about Autodiscover when you're ready.

如果使用具有默认自签名证书的 Exchange 服务器测试应用程序,则需要创建符合组织安全要求的 证书验证方法If you are testing your application using an Exchange server that has the default self-signed certificate, you'll need to create a certificate validation method that meets the security requirements of your organization.

设置开发环境Set up your development environment

用于创建首个 EWS 应用程序的工具取决于所使用的操作系统和语言,这大多只是个人喜好问题。The tools that you use to create your first EWS application depend on the operating system and language that you use, and are mostly a matter of taste. 如果你想跟着沿用本文中的 C# 示例,你将需要:If you want to follow along with the C# examples in this article, you'll need:

  • 支持 .NET Framework 4.0 的任何 Visual Studio 版本。Any version of Visual Studio that supports the .NET Framework 4.0.

  • 可让开发计算机联系 Exchange 服务器的 Internet 连接。An Internet connection that your development machine can use to contact your Exchange server. 如果你可以使用 Outlook Web App 和 DNS 名称,而不是 IP 地址来连接 Exchange 服务器的话,则代表你已经设置好了。If you can use Outlook Web App with a DNS name rather than an IP address to connect to your Exchange server, you are set up.

创建你的第一个 EWS 应用程序Create your first EWS application

要创建的 EWS 应用程序会显示使用 EWS 的两种典型方案:The EWS application that you will create shows two typical scenarios for using EWS:

  1. 从 Exchange 邮箱获取信息并向用户显示该信息。Get information from an Exchange mailbox and display that information to the user.

  2. 执行一个操作(如发送电子邮件),然后检查响应以查看操作是否成功。Perform an action, such as sending an email, and check the response to see if the action succeeded.

让我们开始吧。Let's get started.

设置解决方案Set up the solution

首先,使用 Visual Studio 创建一个新的控制台应用程序解决方案。First, create a new console application solution using Visual Studio. 解决方案准备就绪后,创建名为 Tracing.cs 的新对象。When the solution is ready, create a new object called Tracing.cs. 使用此对象向控制台和日志文件写入信息,这样你就可以在运行代码后查看结果。Use this object to write information to both the console and a log file so that you can review the results after you run your code. 将以下代码粘贴到 Tracing.cs 文件中。Paste the following code into the Tracing.cs file.

using System;
using System.IO;
namespace Microsoft.Exchange.Samples.EWS
{
  class Tracing
  {
    private static TextWriter logFileWriter = null;
    public static void OpenLog(string fileName)
    {
      logFileWriter = new StreamWriter(fileName);
    }
    public static void Write(string format, params object[] args)
    {
      Console.Write(format, args);
      if (logFileWriter != null)
      {
        logFileWriter.Write(format, args);
      }
    }
    public static void WriteLine(string format, params object[] args)
    {
      Console.WriteLine(format, args);
      if (logFileWriter != null)
      {
        logFileWriter.WriteLine(format, args);
      }
    }
    public static void CloseLog()
    {
      logFileWriter.Flush();
      logFileWriter.Close();
    }
  }
}

接下来,打开 Program.cs 文件。Next, open the Program.cs file. 将此示例中代码的其余部分放入此文件中。You will put the rest of the code for the example in this file.

首先,设置程序的外壳。First, set up the shell of the program. 程序将:The program will:

  1. 创建日志文件,以便可将请求和响应写入磁盘,供日后研究。Create a log file so that the request and response can be written to disk for later study.

  2. 获取你将访问的帐户的电子邮件地址和密码。Get the email address and password of the account that you'll access.

  3. 调用示例方法。Call the sample methods.

将 Program.cs 中的 Main 方法替换成以下代码。Replace the Main method in the Program.cs with the following code.

    static void Main(string[] args)
    {
      // Start tracing to console and a log file.
      Tracing.OpenLog("./GetStartedWithEWS.log");
      Tracing.WriteLine("EWS sample application started.");
      var isValidEmailAddress = false;
      Console.Write("Enter an email address: ");
      var emailAddress = Console.ReadLine();
      
        isValidEmailAddress = (emailAddress.Contains("@") && emailAddress.Contains("."));
      if (!isValidEmailAddress)
      {
        Tracing.WriteLine("Email address " + emailAddress + " is not a valid SMTP address. Closing program.");
        return;
      }
      SecureString password = GetPasswordFromConsole();
      if (password.Length == 0)
      {
        Tracing.WriteLine("Password empty, closing program.");
      }
      NetworkCredential userCredentials = new NetworkCredential(emailAddress, password);
      // These are the sample methods that demonstrate using EWS.
      // ShowNumberOfMessagesInInbox(userCredentials);
      // SendTestEmail(userCredentials);
     
      Tracing.WriteLine("EWS sample application ends.");
      Tracing.CloseLog();
      Console.WriteLine("Press enter to exit: ");
      Console.ReadLine();
    }
    // These method stubs will be filled in later.
    private static void ShowNumberOfMessagesInInbox(NetworkCredential userCredentials)
    {
    }
    private static void SendTestEmail(NetworkCredential userCredentials)
    {
    }

最后要做的事就是添加 GetPasswordFromConsole 静态方法。The last thing that you need to do is add the GetPasswordFromConsole static method. 此方法会返回一个 SecureString 对象,它包含在控制台中键入的密码。This method returns a SecureString object that contains a password typed at the console.

    private static SecureString GetPasswordFromConsole()
    {
      SecureString password = new SecureString();
      bool readingPassword = true;
      Console.Write("Enter password: ");
      while (readingPassword)
      {
        ConsoleKeyInfo userInput = Console.ReadKey(true);
        switch (userInput.Key)
        {
          case (ConsoleKey.Enter):
            readingPassword = false;
            break;
          case (ConsoleKey.Escape):
            password.Clear();
            readingPassword = false;
            break;
          case (ConsoleKey.Backspace):
            if (password.Length > 0)
            {
              password.RemoveAt(password.Length - 1);
              Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
              Console.Write(" ");
              Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
            }
            break;
          default:
            if (userInput.KeyChar != 0)
            {
              password.AppendChar(userInput.KeyChar);
              Console.Write("*");
            }
            break;
        }
      }
      Console.WriteLine();
      password.MakeReadOnly();
      return password;
    }

获取“收件箱”中的新邮件数Get the number of new messages in an Inbox

EWS 应用程序中的常见操作是获取有关电子邮件、约会、会议和存储它们的文件夹的信息。A common operation in an EWS application is to get information about email messages, appointments, meetings, and the folders that store them. 此示例会获取帐户“收件箱”中的邮件数量,并显示邮件总数和未读邮件数。This example gets the number of messages in an account's Inbox and displays the total number of messages and the number of unread messages. 它演示了以下这些 EWS 应用程序的常见操作:It demonstrates the following common actions for EWS applications:

  • 向 Exchange 服务器发出 EWS 请求。Making an EWS request to the Exchange server.

  • 为所请求的信息分析返回的 XML 响应。Parsing the returned XML response for the requested information.

  • 处理常见异常和错误消息。Handling common exceptions and error messages.

将下面的代码添加到 ShowNumberOfMessagesInInbox 方法中,该方法是主方法后的存根。Add the following code to the ShowNumberOfMessagesInInbox method that was stubbed out after the main method. 运行应用程序时,它将打印帐户“收件箱”中的邮件数和“收件箱”中的未读邮件数。When you run the application, it will print the number of messages in the account's Inbox and the number of unread messages in the Inbox. 运行应用程序后,可以打开 GetStartedWithEWS.log 文件,查看发送到 Exchange 服务器的 XML 请求和服务器返回的响应。After you run the application, you can open the GetStartedWithEWS.log file to see the XML request that was sent to the Exchange server and the response that the server returned.

      /// This is the XML request that is sent to the Exchange server.
      var getFolderSOAPRequest =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<soap:Envelope xmlns:soap=\"https://schemas.xmlsoap.org/soap/envelope/\"\n" +
"   xmlns:t=\"https://schemas.microsoft.com/exchange/services/2006/types\">\n" +
"<soap:Header>\n" +
"    <t:RequestServerVersion Version=\"Exchange2007_SP1\" />\n" +
"  </soap:Header>\n" +
"  <soap:Body>\n" +
"    <GetFolder xmlns=\"https://schemas.microsoft.com/exchange/services/2006/messages\"\n" +
"               xmlns:t=\"https://schemas.microsoft.com/exchange/services/2006/types\">\n" +
"      <FolderShape>\n" +
"        <t:BaseShape>Default</t:BaseShape>\n" +
"      </FolderShape>\n" +
"      <FolderIds>\n" +
"        <t:DistinguishedFolderId Id=\"inbox\"/>\n" +
"      </FolderIds>\n" +
"    </GetFolder>\n" +
"  </soap:Body>\n" +
"</soap:Envelope>\n";
      // Write the get folder operation request to the console and log file.
      Tracing.WriteLine("Get folder operation request:");
      Tracing.WriteLine(getFolderSOAPRequest);
      var getFolderRequest = WebRequest.CreateHttp(Office365WebServicesURL);
      getFolderRequest.AllowAutoRedirect = false;
      getFolderRequest.Credentials = userCredentials;
      getFolderRequest.Method = "POST";
      getFolderRequest.ContentType = "text/xml";
      var requestWriter = new StreamWriter(getFolderRequest.GetRequestStream());
      requestWriter.Write(getFolderSOAPRequest);
      requestWriter.Close();
      try
      {
        var getFolderResponse = (HttpWebResponse)(getFolderRequest.GetResponse());
        if (getFolderResponse.StatusCode == HttpStatusCode.OK)
        {
          var responseStream = getFolderResponse.GetResponseStream();
          XElement responseEnvelope = XElement.Load(responseStream);
          if (responseEnvelope != null)
          {
            // Write the response to the console and log file.
            Tracing.WriteLine("Response:");
            StringBuilder stringBuilder = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(stringBuilder, settings);
            responseEnvelope.Save(writer);
            writer.Close();
            Tracing.WriteLine(stringBuilder.ToString());
            // Check the response for error codes. If there is an error, throw an application exception.
            IEnumerable<XElement> errorCodes = from errorCode in responseEnvelope.Descendants
                                               ("{https://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode")
                                               select errorCode;
            foreach (var errorCode in errorCodes)
            {
              if (errorCode.Value != "NoError")
              {
                switch (errorCode.Parent.Name.LocalName.ToString())
                {
                  case "Response":
                    string responseError = "Response-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(responseError);
                  case "UserResponse":
                    string userError = "User-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(userError);
                }
              }
            }
            // Process the response.
            IEnumerable<XElement> folders = from folderElement in
                                              responseEnvelope.Descendants
                                              ("{https://schemas.microsoft.com/exchange/services/2006/messages}Folders")
                                            select folderElement;
            foreach (var folder in folders)
            {
              Tracing.Write("Folder name:     ");
              var folderName = from folderElement in
                                 folder.Descendants
                                 ("{https://schemas.microsoft.com/exchange/services/2006/types}DisplayName")
                               select folderElement.Value;
              Tracing.WriteLine(folderName.ElementAt(0));
              Tracing.Write("Total messages:  ");
              var totalCount = from folderElement in
                                 folder.Descendants
                                   ("{https://schemas.microsoft.com/exchange/services/2006/types}TotalCount")
                               select folderElement.Value;
              Tracing.WriteLine(totalCount.ElementAt(0));
              Tracing.Write("Unread messages: ");
              var unreadCount = from folderElement in
                                 folder.Descendants
                                   ("{https://schemas.microsoft.com/exchange/services/2006/types}UnreadCount")
                               select folderElement.Value;
              Tracing.WriteLine(unreadCount.ElementAt(0));
            }
          }
        }
      }
      catch (WebException ex)
      {
        Tracing.WriteLine("Caught Web exception:");
        Tracing.WriteLine(ex.Message);
      }
      catch (ApplicationException ex)
      {
        Tracing.WriteLine("Caught application exception:");
        Tracing.WriteLine(ex.Message);
      }

发送电子邮件消息Send an email message

EWS 应用程序的另一个常见操作是发送电子邮件或会议请求。Another common operation for an EWS application is to send email messages or meeting requests. 此示例使用之前所输入的用户凭据,创建并发送电子邮件。This example creates and sends an email message using the user credentials that were entered earlier. 它演示了这些常见的 EWS 应用程序任务:It demonstrates these common EWS application tasks:

  • 创建和发送电子邮件。Creating and sending an email.

  • 分析返回的 XML 响应,确定是否已正确发送电子邮件。Parsing the returned XML response to determine if the email was correctly sent.

  • 处理常见异常和错误消息。Handling common exceptions and error messages.

将下面的代码添加到 SendTestEmail 方法中,该方法是主方法后的存根。Add the following code to the SendTestEmail method that was stubbed out after the main method. 运行应用程序后,可以打开 GetStartedWithEWS.log 文件,查看发送到 Exchange 服务器的 XML 请求和服务器返回的响应。After you run the application, you can open the GetStartedWithEWS.log file to see the XML request that was sent to the Exchange server and the response that the server returned.

var createItemSOAPRequest =
      "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
      "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" +
      "               xmlns:m=\"https://schemas.microsoft.com/exchange/services/2006/messages\" \n" +
      "               xmlns:t=\"https://schemas.microsoft.com/exchange/services/2006/types\" \n" +
      "               xmlns:soap=\"https://schemas.xmlsoap.org/soap/envelope/\">\n" +
      "  <soap:Header>\n" +
      "    <t:RequestServerVersion Version=\"Exchange2007_SP1\" />\n" +
      "  </soap:Header>\n" +
      "  <soap:Body>\n" +
      "    <m:CreateItem MessageDisposition=\"SendAndSaveCopy\">\n" +
      "      <m:SavedItemFolderId>\n" +
      "        <t:DistinguishedFolderId Id=\"sentitems\" />\n" +
      "      </m:SavedItemFolderId>\n" +
      "      <m:Items>\n" +
      "        <t:Message>\n" +
      "          <t:Subject>Company Soccer Team</t:Subject>\n" +
      "          <t:Body BodyType=\"HTML\">Are you interested in joining?</t:Body>\n" +
      "          <t:ToRecipients>\n" +
      "            <t:Mailbox>\n" +
      "              <t:EmailAddress>sadie@contoso.com</t:EmailAddress>\n" +
      "              </t:Mailbox>\n" +
      "          </t:ToRecipients>\n" +
      "        </t:Message>\n" +
      "      </m:Items>\n" +
      "    </m:CreateItem>\n" +
      "  </soap:Body>\n" +
      "</soap:Envelope>\n";
      // Write the create item operation request to the console and log file.
      Tracing.WriteLine("Get folder operation request:");
      Tracing.WriteLine(createItemSOAPRequest);
      var getFolderRequest = WebRequest.CreateHttp(Office365WebServicesURL);
      getFolderRequest.AllowAutoRedirect = false;
      getFolderRequest.Credentials = userCredentials;
      getFolderRequest.Method = "POST";
      getFolderRequest.ContentType = "text/xml";
      var requestWriter = new StreamWriter(getFolderRequest.GetRequestStream());
      requestWriter.Write(createItemSOAPRequest);
      requestWriter.Close();
      try
      {
        var getFolderResponse = (HttpWebResponse)(getFolderRequest.GetResponse());
        if (getFolderResponse.StatusCode == HttpStatusCode.OK)
        {
          var responseStream = getFolderResponse.GetResponseStream();
          XElement responseEnvelope = XElement.Load(responseStream);
          if (responseEnvelope != null)
          {
            // Write the response to the console and log file.
            Tracing.WriteLine("Response:");
            StringBuilder stringBuilder = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(stringBuilder, settings);
            responseEnvelope.Save(writer);
            writer.Close();
            Tracing.WriteLine(stringBuilder.ToString());
            // Check the response for error codes. If there is an error, throw an application exception.
            IEnumerable<XElement> errorCodes = from errorCode in responseEnvelope.Descendants
                                               ("{https://schemas.microsoft.com/exchange/services/2006/messages}ResponseCode")
                                               select errorCode;
            foreach (var errorCode in errorCodes)
            {
              if (errorCode.Value != "NoError")
              {
                switch (errorCode.Parent.Name.LocalName.ToString())
                {
                  case "Response":
                    string responseError = "Response-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(responseError);
                  case "UserResponse":
                    string userError = "User-level error getting inbox information:\n" + errorCode.Value;
                    throw new ApplicationException(userError);
                }
              }
            }
            Tracing.WriteLine("Message sent successfully.");
          }
        }
      }
      catch (WebException ex)
      {
        Tracing.WriteLine("Caught Web exception:");
        Tracing.WriteLine(ex.Message);
      }
      catch (ApplicationException ex)
      {
        Tracing.WriteLine("Caught application exception:");
        Tracing.WriteLine(ex.Message);
      }

后续步骤Next steps

现在你已编写了你的首个 EWS 应用程序,这就代表你已经准备好发现使用 EWS 的其他方法了。Now that you've written your first EWS application, you're ready to discover other ways to use EWS. 以下是一些帮助你开始使用的思路:Here are some ideas to get you started:

如果你的应用程序遇到任何问题,请尝试在论坛中发布问题或评论(别忘了阅读第一个帖子)。If you run into any issues with your application, try posting a question or comment in the forum (and don't forget to read the first post).

另请参阅See also