
Здравствуйте, читатели Хабрахабр!
В рамках серии этих постов я хочу рассказать о такой технологии как EWS и о том, как ее использовать для работы с почтой, хранящейся на серверах MS Exchange 2007 — 2010. Я постараюсь показать, как просто и удобно использовать EWS.
Мы начнем с самого начала, с знакомства с это технологией и создания первого проекта, а закончим более сложными манипуляциями с почтой.
Данный пост является вводным и он, скорее всего, окажется неинтересен тем, кто уже знаком с EWS.
Введение
Exchange Web Services (EWS) — это специальный протокол, разработанный MS, и предназначенный для управления почтой и другими компонентами составляющими MS Exchange. В основе протокола лежит XML. Описание поддерживаемых операций и примеры можно найти здесь.
EWS сравнительно молодая технология, появившаяся в Exchange 2007, и пришедшая на смену протоколу WebDAV. MS вкладывает много усилий в разработку и усовершенствование EWS. Сейчас это мощнейший инструмент, с простым и понятным API, а также с прекрасной документацией, позволяющий решать задачи менеджмента объектов MS Exchange.
Архитектура EWS показана на следующей картинке:

Как видно, EWS размещается на серверах имеющих Client Access Server (CAS) роль. Это роль отвечает за обработку запросов от пользователя, т.е. любые клиента, такие как Outlook, MFCMapi, браузер + OWA и т.д., все они подсоединяются к CAS'у. Запрос от клиентского приложения попадает в IIS, где и находится application имплементирующий функциональность EWS. Т.е. EWS «живет» в IIS и выполняется в отдельном процессе или процессах w3wp.exe (процессы в которых исполняются IIS application pool'ы). Помимо EWS там могут «крутится» и другие приложения, например, OWA, ECP и PowerShell.
EWS выступает в виде прослойки между клиентским запросом и внутренностями Exchange. Когда поступает EWS-запрос от клиентского приложения, то он проксируется во внутренние вызовы Exchange, после чего поступает на Mailbox Server role, где уже и производятся сами операции.
Примечание: Немного о внутреннем устройстве Mailbox роли можно прочитать в моей предыдущей статье.
Физически EWS размещен внутри набора .NET сборок. И начиная с Exchange 2010 туда попадают не только EWS вызовы, но и OWA. Т.е. MS, видимо, осознала не удачность идеи нескольких ветвей одинакового по функциональности кода и решила оставить только одну ветку, что должно упростить и ускорить support и разработку.
Общее представление мы получили, можем переходить к программированию.
Разработка
Нам понадобятся:
- MS Exchange Server 2007 — 2010 для тестирования
- Visual Studio 2008 — 2010

После нажатия далее начнется загрузка reference'ов, и если все пройдет хорошо, вам предложат указать имя этого reference'а, и по окончанию он будет добавлен в проект.
Всё, теперь мы можем попытаться подсоединиться к серверу. И первое, что нужно сделать это получить объект ExchangeServiceBinding, это, пожалуй, самый важный объект в EWS, именно через него будут вызываться все допустимые методы и именно он идентифицирует конкретное подключение на стороне клиента. Для создания данного объекта можно воспользоваться следующим методом:
public static ExchangeServiceBinding GetBinding( String server, String domain, String user, String password) { var esb = new ExchangeServiceBinding(); // Указываем используемые креды esb.Credentials = new NetworkCredential(user, password, domain); // Задаем входную точку для EWS esb.Url = "https://" + server + "/EWS/Exchange.asmx"; esb.RequestServerVersionValue = new RequestServerVersion(); // Указываем тип Exchange сервера // Для Exchange 2007 SP1 и 2010 подходит Exchange2007_SP1 // Для Exchange 2007 без SP нужно указать Exchange2007 esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1; return esb; }
Примечание: Если сервер использует self-signed сертификат, то соединиться не получится т.к. такой сертификат не пройдет валидацию. В качестве workaround'а можно добавить дополнительный код apply'щий любые сертификаты:
ServicePointManager.ServerCertificateValidationCallback = (obj, certificate, chain, errors) => true;
Имея ExchangeServiceBinding можно попытаться сделать что-нибудь полезное, например, отправить сообщение. MSDN подсказывает, что для этого нужно воспользоваться объектом CreateItemType. Для этого напишем следующий метод:
public static void SendMessage( ExchangeServiceBinding esb, String to, String subject, String body) { // Создаем CreateItem request // Он одинаковый для создания отправки сообщений // Указываем, что мы хотим его именно отправить, а не сохранить var createItemRequest = new CreateItemType { Items = new NonEmptyArrayOfAllItemsType(), MessageDispositionSpecified = true, MessageDisposition = MessageDispositionType.SendOnly }; // Создаем item типа Message // Указываем recipients, в нашем примере только одного var message = new MessageType(); message.ToRecipients = new EmailAddressType[1]; message.ToRecipients[0] = new EmailAddressType(); message.ToRecipients[0].EmailAddress = to; // Тема письма message.Subject = subject; // Содержимое письма и формат message.Body = new BodyType(); message.Body.BodyType1 = BodyTypeType.Text; message.Body.Value = body; // Связываем наш request с созданным Item'ом createItemRequest.Items.Items = new ItemType[1]; createItemRequest.Items.Items[0] = message; // Выполняем операцию CreateItemResponseType createItemResponse = esb.CreateItem(createItemRequest); // Получаем ответ ArrayOfResponseMessagesType responseMessages = createItemResponse.ResponseMessages; // Бросаем исключение, если ошибка var responseMessage = responseMessages.Items; foreach (var rmt in responseMessage.Where(rmt => rmt.ResponseClass == ResponseClassType.Error)) { throw new Exception(rmt.MessageText); } }
Примечание: В приведенном коде показан самый простой способ отправки сообщения, и мы не видим «мощи» используемых классов, а они предоставляют обширные возможности по кастомизации производимых операций (задание всевозможных свойств, флагов, и т.п.). Подробнее об этом и не только я постараюсь рассказать в следующих статьях.
Вызов этого кода можно сделать так:
static void Main() { ServicePointManager.ServerCertificateValidationCallback = (obj, certificate, chain, errors) => true; try { const String server = "myserver", domain = "mydomain", user = "myuser", password = "mypassword", mailTo = "myuser2@mydomain.local", subject = "read me", body = "ehlo, developers!"; var esb = GetBinding(server, domain, user, password); SendMessage(esb, mailTo, subject, body); Console.WriteLine("Done!"); } catch (Exception e) { if(e.Message != null) Console.WriteLine(e.Message); } }
Если вам показалось, что код выполнился пугающе долго (у меня порядка 5 секунд), не беспокойтесь, так происходит только при первом вызове. Почти все время занимает инициализация выполняющаяся только один раз. Так же, если это был первый EWS-вызов для сервера, то серверу необходимо произвести инициализацию со своей стороны и создать новый instance w3wp для обработки запроса, а это тоже может занять какое-то время.
Заключение
Мы коротко познакомились с технологией EWS и написали простой пример отправки письма адресату.
Как видно код получается очень кратким и понятным. Документация MS достаточно хорошая и почти всегда с примерами. Если вы программировали на C++ MAPI, то вы оцените на сколько данный способ проще.
Если у вас появится задача написания почтового клиента или любые другие задачи, связанные с удаленной работой над объектами Exchange, я надеюсь, что вы воспользуетесь EWS. т.к. ИМХО данный способ наиболее простой и понятный из всех с которыми мне приходилось работать.
Спасибо, за внимание.
Продолжение следует...
