
Здравствуйте, читатели Хабрахабр!
В рамках серии этих постов я хочу рассказать о такой технологии как 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. т.к. ИМХО данный способ наиболее простой и понятный из всех с которыми мне приходилось работать.
Спасибо, за внимание.
Продолжение следует...