Начало работы с клиентскими приложениями EWS

Создание первого приложения с помощью веб-служб Exchange (EWS) в Exchange.

EWS — это комплексная служба, которую приложения могут использовать для доступа практически ко всей информации, хранящейся в Exchange Online, Exchange Online в составе Office 365 или локального почтового ящика Exchange. EWS использует стандартные веб-протоколы для предоставления доступа к серверу Exchange Server; такие библиотеки, как управляемый API EWS , обтекают операции EWS для предоставления объектно-ориентированного интерфейса. После выполнения примеров, приведенных в этой статье, вы получите базовое представление о том, что можно делать с помощью EWS.

Вы можете вызывать операции EWS из любой операционной системы или языка, так как запросы и ответы EWS используют протокол SOAP. Примеры в этой статье написаны на C# и используют платформа .NET Framework объектов HttpWebRequest и HttpWebResponse. Однако важной частью кода является XML-код, используемый для создания запроса EWS и XML-ответа, возвращенного сервером. В примерах кода акцент делается на транзакциях XML, а не на обработке XML.

Примечание.

Мы удаляем возможность использования обычной проверки подлинности в Exchange Online для EWS с октября 2022 г.: Прекращение поддержки обычной проверки подлинности в Exchange Online. Вместо этого следует использовать проверку подлинности OAuth. Проверка подлинности приложения EWS с помощью OAuth

Вам потребуется сервер Exchange Server

Если у вас уже есть учетная запись почтового ящика Exchange, вы можете пропустить этот шаг. Если это не так, ниже приведены несколько вариантов настройки почтового ящика Exchange для вашего первого приложения EWS.

Когда вы проверите, можно ли отправлять сообщения на сервер Exchange Server и получать их с него, приступайте к настройке среды разработки. Проверить отправку сообщений можно с помощью Outlook Web App.

Необходимо знать URL-адрес конечной точки EWS для сервера. Для определения этого URL-адреса в рабочем приложении используйте функцию автообнаружения. В примерах из этой статьи используется URL-адрес конечной точки EWS в Office 365 (https://outlook.office365.com/EWS/Exchange.asmx. Ссылки на дополнительные сведения о функции автообнаружения можно найти в разделе Дальнейшие действия.

Если вы будете тестировать приложение на сервере Exchange Server, который использует самозаверяющий сертификат по умолчанию, вам потребуется создать метод проверки сертификата в соответствии с требованиями вашей организации к обеспечению безопасности.

Настройка среды разработки

Выбор инструментов для создания вашего первого приложения EWS зависит от операционной системы и языка, которые вы используете, но в основном это дело вкуса. Если вы хотите воспользоваться примерами на языке C# из этой статьи, вам понадобится следующее:

  • Любая версия Visual Studio, поддерживающая .NET Framework 4.0.
  • Подключение к Интернету, необходимое для связи компьютера для разработки с сервером Exchange Server. Если вы можете использовать Outlook Web App и DNS-имя вместо IP-адреса для соединения с сервером Exchange Server, все настроено.

Создание первого приложения EWS

Созданное приложение EWS продемонстрирует два стандартных сценария использования EWS:

  1. Получение данных из почтового ящика Exchange и предоставление их пользователю.
  2. Выполнение какого-либо действия (например, отправки электронного сообщения) и проверка отклика для подтверждения успешного завершения.

Теперь можно приступать к работе.

Настройка решения

Сначала создайте решение консольного приложения с помощью Visual Studio. Когда решение будет готово, создайте новый объект с именем Tracing.cs. Используйте этот объект для записи сведений как в консоль, так и в файл журнала, чтобы просмотреть результаты после выполнения кода. Вставьте следующий код в файл Tracing.cs.

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. В него будет помещена оставшаяся часть кода, используемого в качестве примера.

Сначала необходимо настроить программную оболочку. Программа выполнит следующее:

  1. Создаст файл журнала для записи на диск запросов и откликов, чтобы позже их можно было изучить.
  2. Получит доступ к электронному адресу и паролю учетной записи, которые вы будете использовать.
  3. Вызовет примеры методов.

Замените метод Main в файле Program.cs указанным ниже кодом.

    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 метод. Этот метод возвращает объект SecureString , содержащий введенный в консоль пароль.

    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;
    }

Число новых сообщений в папке "Входящие"

Получение сведений об электронных сообщениях, собраниях, встречах, а также папках, в которых эти сведения хранятся, — типовые операции в приложении EWS. Этот пример поможет узнать, сколько сообщений находится в папке "Входящие" учетной записи, а также общее число сообщений и число непрочитанных сообщений. Пример демонстрирует следующие типовые действия для приложений EWS:

  • отправка запроса EWS на сервер Exchange;
  • анализ возвращенного XML-отклика на наличие запрошенной информации;
  • обработка типовых исключений и сообщений об ошибке.

Добавьте следующий код в ShowNumberOfMessagesInInbox метод, который был вырезан после метода main. При запуске приложения будет показано число непрочитанных сообщений и число сообщений в папке "Входящие" учетной записи. Запустив приложение, вы сможете открыть файл GetStartedWithEWS.log и просмотреть XML-запрос, отправленный на сервер Exchange Server, и возвращенный сервером отклик.

      /// 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);
      }

Отправка электронных сообщений

Отправка электронных сообщений или приглашений на собрания — это еще одна типовая операция приложения EWS. Данный пример позволяет создать и отправить сообщение с помощью введенных ранее учетных данных пользователя. Пример демонстрирует следующие типовые задачи приложений EWS:

  • создание и отправка электронных сообщений;

  • анализ возвращенного XML-отклика для определения того, было ли сообщение отправлено корректно;

  • обработка типовых исключений и сообщений об ошибке.

Добавьте указанный ниже код в метод SendTestEmail, заглушенный вслед за основным. Запустив приложение, вы можете открыть файл GetStartedWithEWS.log и просмотреть XML-запрос, отправленный на сервер Exchange Server, и возвращенный сервером отклик.

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);
      }

Дальнейшие действия

Теперь, когда вы написали свое первое приложение EWS, можно переходить к другим способам использования EWS. Вот несколько идей для начала работы:

Если с приложением возникли проблемы, попробуйте опубликовать вопрос или комментарий на форуме (и не забудьте прочитать первую запись).

См. также