воскресенье, октября 22, 2006

Web сервис и протокол HTTPS (SSL)

Подключение к ASP.NET web сервису по протоколу HTTPS (SSL)

(.Net Framework 1.1)



Введение


Наиболее простым и надежным способом обеспечения безопасного взаимодействия с XML web сервисом, разработанном на ASP.NET является подключение по протоколу HTTPS (SSL). Данный способ обеспечивает полное шифрование трафика на уровне транспортного протокола и не требует от разработчика web сервиса принятия, каких либо дополнительных мер для обеспечения его поддержки.


Однако при разработке клиентской части требуется определенные усилия для установления соединения с web сервисом через HTTPS. Требуется соответствующим образом сконфигурировать прокси класс web сервиса, а также необходимо проинсталлировать клиентский сертификат.


Использование ASP.NET web сервисов совместно с протоколом HTTPS открывает нам большие возможности построения распределенных систем с хорошим уровнем защищенности без особых затрат. Однако на этом пути нас поджидает масса неожиданностей и подводных камней, заботливо расставленных парнями из Редмонта, которые запросто могут поставить нас в тупик. Данная статья обобщает опыт автора в данной области. Предполагается, что читатель уже знаком с web сервисами ASP.NET и имеет некоторый опыт практического использования их.


Включаем HTTPS на сервере.


Прежде всего, для того, чтобы иметь возможность работать с HTTPS протоколом, необходимо включить его поддержку на сервере IIS. Делается это очень просто, однако есть одна сложность, которая может показаться на первый взгляд непреодолимой – необходимо получить серверный сертификат


Руководство рекомендует открыть оснастку MMC “Internet Information Services”, выбрать сайт и открыть окно его свойств. На вкладке “Directory security” есть кнопка “Server Certificate…”, которая запускает мастера запроса нового сертификата. Этот мастер, задав вам массу вопросов, сформирует файл запроса, который предлагается отправить в “certification authority” для получения серверного сертификата. Беда в том, что фирмы, занимающиеся выдачей сертификатов, требуют за это деньги, и вам придется долго объяснять в бухгалтерии для чего необходимо перечислить 20 долларов в какой-то там VeriSign


Возможно, в локальной сети вашей компании развернута служба “Enterprise Certificate Services”. В таком случае вам сильно повезло, и вам остается обратиться к администратору и получить заветный сертификат.


Ну и, наконец, есть третий способ. Специально для того, чтобы разработчики могли тестировать свои продукты, Microsoft выпустила “ IIS6 Resource Kit Tools”, в состав которого входит утилита SelfSSL (скачать можно здесь http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499&DisplayLang=en). Несмотря на то, что Resource Kit предназначен для сервера IIS6 большинство входящих в него утилит (и SelfSSL в том числе) прекрасно работают и с IIS5.1, который входит в состав Windows XP Prof. Сертификаты, которые генерирует эта утилита, вполне пригодны для целей тестирования.


Итак, для того, чтобы установить серверный сертификат, вам необходимо проинсталлировать “IIS Resource Kit”. Далее запускаем утилиту: меню Start -> All Programs -> IIS Resources ->SelfSSL. Например, следующая команда создаст сертификат с именем компьютера, для дефолтного сайта со сроком действия в один год:



selfssl.exe /s:1 /v:365

Умная утилита не только создаст сертификат, но и установит его на сайт. Чтобы убедиться в этом, открываем оснастку MMC “Internet Information Services”, выбираем “Default web site” и открываем окно его свойств. На вкладке “Directory security” теперь доступна не только кнопка “Server Ccertificate…” но и “View certificate…”. Нажав на нее, мы можем увидеть наш свежий сертификат.





Значок сертификата будет помечен красным крестиком и сопровожден надписью “This CA Root certificate is not trusted because it is not in the Trusted Root Certification Authorities store”. Пока мы не станем обращать на это внимание, потому что это не мешает нам работать с нашим сертификатом.


После того, как серверный сертификат установлен на сайте, нам остается включить поддержку SSL. Делается это на той же вкладке “Directory security” на странице свойств сайта. В разделе “Security Communications” нажимаем кнопку “Edit” (она тоже стала доступна после установки сертификата). В появившемся диалоге выставляем флажок “Require secure communications” и жмем “Ok”.





Все, поддержка SSL включена, можно приступать к тестированию web сервиса over HTTPS.


Конфигурирование клиентского прокси класса.


В данной статье я не буду рассматривать вопросы создания web сервиса и клиентского прокси для него. Предположим, web сервис уже существует, и для его использования вы уже добавили web reference web сервис в свой проект. Рассмотрим, как сконфигурировать клиентский прокси класс web сервиса для работы с ним по HTTPS.


Прежде всего, URL web сервиса должен начинаться с “https://”



WebServiceProxy service = new
WebServiceProxy(); service.Url =”https://server/webservice.asmx”;



Очевидно также, что если вам необходимо защищенное соединение с web сервисом, доступ к нему должен предоставляться только аутентифицированным пользователям. Для этого мы должны установить удостоверения пользователя (Credentials) от имени которого будет отправлен запрос к web сервису. Существует два способа установки удостоверений. Первый, вызов от имени текущего пользователя.



service.credentials = CredentialCache.DefaultCredentials;


Текущим пользователем будет являться пользователь, который запустил ваше приложение. Этот режим аутентификации называется “Windows Integrated”, его например, активно использует Internet Explorer при работе с ASP и ASP.NET web приложениями под IIS. При этом пользователю не надо вводить свое имя и пароль при посещении сайта. Для аутентификации используются имя и пароль, введенные при входе в домен Active Directory или NT.


Данный способ аутентификации хорош для консольного или Windows Forms приложения. Однако если вы разрабатываете web приложение, или windows службу CredentialCache.DefaultCredentials вернет вам удостоверения учетной записи, под которой исполняется серверный процесс. Ею может оказаться учетная запись ASPNET, SYSTEM, NETWORK SERVICE или какая ни будь другая, и это будет совсем не то, что нужно вам. В этих случаях необходимо явно указать удостоверения пользователя:



service.credentials = new
NetworkCredential(“user”, “password”, “domain”);


При этом в качестве “domain” может быть указано имя компьютера, если используется учетная запись локального пользователя, либо ничего не казано. В последнем случае используем перегруженный конструктор NetworkCredential с двумя параметрами.


И, наконец, третье условие - это клиентский сертификат. Строго говоря, для установления SSL соединения клиентский сертификат не нужен, достаточно серверного. Но некторые web серверы могут быть сконфигурированы так, чтобы требовать обязательное наличие клиентского сертификата. В этом случае у вас должен быть файл клиентского сертификата вашего сервера, и вы должны указать его прокси классу. К сожалению, .Net Framework версии 1.1, о котором мы ведем речь, не поддерживает работу с хранилищем сертификатов Windows. Поэтому нам придется загружать сертификат из файла:



service.ClientCertificates.Add( System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromCertFile(“c:\\test.cer”));

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



При установлении SSL соединения возможно возникновение ошибок сертификата, которые сопровождаются разрывом соединения и исключением с текстом «Underlined connection was closed». Вы можете обрабатывать эти ошибки. Для этого надо создать свой класс, реализующий интерфейс ICertificatePolicy. Вот пример такого класса, который игнорирует все ошибки сертификатов



public class AcceptAllCertificatePolicy: ICertificatePolicy
{
// Перечень возможных ошибок сертификата
public enum CertificateProblem : long
{
CertEXPIRED = 0x800B0101,
CertVALIDITYPERIODNESTING = 0x800B0102,
CertROLE = 0x800B0103,
CertPATHLENCONST = 0x800B0104,
CertCRITICAL = 0x800B0105,
CertPURPOSE = 0x800B0106,
CertISSUERCHAINING = 0x800B0107,
CertMALFORMED = 0x800B0108,
CertUNTRUSTEDROOT = 0x800B0109,
CertCHAINING = 0x800B010A,
CertREVOKED = 0x800B010C,
CertUNTRUSTEDTESTROOT = 0x800B010D,
CertREVOCATION_FAILURE = 0x800B010E,
CertCN_NO_MATCH = 0x800B010F,
CertWRONG_USAGE = 0x800B0110,
CertUNTRUSTEDCA = 0x800B0112
}

// Этот метод вызывается при возникновении ошибки сертификата.
// Если он возвращает true, то обработка запроса продолжается
// Если он возвращает false, то обработка запроса прекращается
// и выдается исключение.
public bool CheckValidationResult(ServicePoint srvPoint,
X509Certificate certificate,
WebRequest request,
int certificateProblem)
{
// этот метод выдает диагностическое сообщение на консоль
// и игнорирует все ошибки сертификатов
// Это только пример. Не делайте так в своих приложениях.
CertificateProblem problem = (CertificateProblem)certificateProblem;
Console.WriteLine("Certificate Problem with accessing {0} " +
request.RequestUri);
Console.WriteLine("Problem is 0x{0:X8} ‘{1}’ \n",
certificateProblem, Enum.GetName(problem.GetType(),problem));
return true;
}
}


Данный класс нужно назначить в качестве текущего обработчика в классе ServicePointManager:


ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy();


Сделать это необходимо до первого вызова web метода.


Получение серверного сертификата X.509


HTTPS для установления защищенного соединения использует протокол SSL, которому для шифрования данных требуется открытый ключ, который в свою очередь и содержится в серверном сертификате X.509. Как получить доступ к серверному сертификату? Для этого воспользуемся Internet Explorer-ом. Введем в адресной строке URL вашего web сервиса начинающегося с https. С большой вероятностью, при этом мы увидим окошко “Security alert”.






При нажатии кнопку “View Certificate” откроется уже знакомое нам окно сертификата. В случае если предупреждение не отображается на экране, мы все равно можем увидеть сертификат, щелкнув по иконке замочка в правом нижнем углу окна IE.





В появившемся окне “Certificate” выбираем вторую вкладку “Details” и нажимаем кнопку “Copy to file…”. Появится “Certificate Export Wizard”





Выбираем опцию “DER encoded X.509 (.CER)”, вводим имя файла на следующем экране визарда и сохраняем сертификат в файл. Теперь файл сертификата у нас есть.


Остается обратить внимание на два существенных момента.


Первое. Сертификат содержит имя сервера, которому он выдан.


ПРЕДУПРЕЖДЕНИЕ

Будьте внимательны! URL сервиса должен содержать именно это имя в своем составе, иначе соединение не будет установлено.


Будьте внимательны. Например, у вас есть сервер Myserver.Company.com и вам выдан сертификат на имя Myserver. При этом соединение по адресу
https://Myserver/service.asmx
будет успешно установлено, а по адресу
https://Myserver.Company.com/service.asmx
вызовет ошибку. Помните об этом.


Второе. Серверный сертификат, о котором говорилось выше, должен быть проинсталлирован в хранилище сертификатов локальной машины. В противном случае при попытке установить соединение мы получим невнятное сообщение об ошибке “Underlined connection was closed”. Чтобы проинсталлировать сертификат откроем его файл, и на первой вкладке окна “Certificate” нажмем кнопку “Install Certificate…”





Откроется окно “Certificate Import Wizard”. Нам надо указать, что инсталлировать сертификат надо именно в хранилище локального компьютера (а не локального пользователя). Нажмем кнопку “Next” выберем опцию “Place all certificates in the following store” и жмем кнопку “Browse…”. В появившемся окне “Select Certificate Story” надо отметить флаг “Show physical stores”, а в дереве выбрать “Trusted Root Certification Authorities” -> “Local Computer”. Нажимаем “Ok”.


В окне “Certificate Import Wizard” нажимаем "Next” и “Finish”.


Заключение


Итак, мы рассмотрели, как обеспечить безопасное и защищенное соединение с web сервисом, используя для этого SSL. Мы узнали, как получить и установить серверный сертификат и включить на сайте использование HTTPS (SSL). Рассмотрели, как сконфигурировать клиентский прокси класс web сервиса для соединения по HTTPS. Рассмотрели самый простой способ получения файла серверного сертификата X.509 на клиенте. А также узнали, как проинсталлировать клиентский сертификат в хранилище локальной машины.



Обзор архитектуры Windows Communication Foundation

Данная статья представляет собой авторский перевод статьи MSDN "Windows Communication Foundation Architecture Overview". Оригинал статьи расположен по адресу http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnlong/html/wcfarch.asp Все права на данную статью принадлежат Microsoft Corporation


Обзор архитектуры Windows Communication Foundation

Microsoft Corporation
March 2006
Summary: Получить высокоуровневый обзор архитектуры Windows Communication Foundation (WCF) и ее ключевых концепций. Примеры кода демонстрируют контракты (contracts), конечные точки (endpoints), и поведения (behaviors).
Contents
Введение
Основы WCF
Примеры кода
Заключительный обзор
Введение
Этот документ дает высокоуровневый обзор архитектуры Windows Communication Foundation (WCF). Он призван описать ключевые концепции, заложенные в WCF, и то, как они связаны друг с другом. Документ содержит примеры кода, которые иллюстрируют основные концепции, но не являются руководством по кодированию в WCF.
Документ состоит из двух основных секций:
· Основы WCF: описывает ключевые концепции WCF, термины и компоненты архитектуры. · Примеры кода: Представляют несколько кратких примеров, которые иллюстрируют и демонстрируют применение концепций изложенных в предыдущем разделе.
Основы WCF
Сервис WCF – это программа, которая публикует коллекцию конечных точек (Endpoints). Каждая конечная точка представляет собой портал для сообщения с внешним миром.
Клиент – это программа, которая обменивается сообщениями с одной или несколькими конечными точками. Клиент также может публиковать конечную точку для приема сообщений от сервиса для реализации шаблона дуплексного обмена сообщениями.
Следующие секции раскрывают эти понятия более детально.
Конечные точки
Конечная точка сервиса имеет адрес (Address), привязку (Binding), и контракт (Contract).
Адрес конечной точки – это сетевой адрес ее расположения. Класс EndpointAddress представляет адрес конечной точки.
Привязка конечной точки определяет, как она сообщается с внешним миром, включая такие параметры, как транспортный протокол (напр., TCP, HTTP), кодировка (напр., text, binary), и параметры безопасности (напр., SSL, SOAP message security). Класс Binding представляет привязку WCF.
Контракт конечной точки определяет, что составляет предмет общения конечной точки, и по существу представляет собой коллекцию сообщений собранных в операции в соответствии с базовыми шаблонами обмена сообщениями (basic Message Exchange Patterns - MEPs) такими, как симплекс, дуплекс, запрос/ответ. Класс ContractDescription представляет WCF контракт.
Класс ServiceEndpoint представляет конечную точку и содержит классы EndpointAddress, Binding, и ContractDescription представляющие адрес, привязку и контракт соответствующей конечной точки. (см. Рис. 1).
Рис. 1. Каждая конечная точка содержит EndpointAddress, Binding и Contract представленный посредством ContractDescription.
EndpointAddress
EndpointAddress содержит URI, удостоверение (Identity), и коллекцию необязательных заголовков (headers), как показано на рисунке 2.
Удостоверение конечной точки обычно привязано к ее URI, однако в некоторых сценариях использования удостоверение можно задать явно, вне зависимости от URI, используя свойство адреса Identity.
Опциональные заголовки представляют дополнительную адресную информацию помимо URI конечной точки. Например заголовки адреса используются определения конкретной конечной точки из нескольких, которые используют одинаковый URI.


Рис. 2. Класс EndpointAddress содержит URI, Identity и коллекцию AddressHeaders.

Привязки
Привязка имеет имя, пространство имен, а также коллекцию компонуемых элементов привязки (BindingElement) (рис. 3). Имя и пространство имен привязки уникально идентифицируют ее а пределах сервиса. Каждый элемент привязки описывает как конечная точка общается с вешним миром. Рис. 3. Класс Binding и его члены.
Например, на рисунке 4 показана коллекция из трех элементов привязки. Наличие каждого из этих элементов привязки описывает конкретный аспект взаимодействия в конечной точкой. TcpTransportBindingElement показывает что конечная точка будет взаимодействовать с внешним миром, используя TCP в качестве транспортного протокола. ReliableSessionBindingElement говорит о том, что конечная точка использует гарантированную доставку сообщений. SecurityBindingElement указывает на то, что конечная точка использует механизм защиты на уровне сообщений SOAP. Каждый элемент привязки обычно имеет свойства которые более конкретно описывают как взаимодействует конечная точка с внешним миром. Например, ReliableSessionBindingElement имеет свойство Assurances, которое определяет уровень требований по гарантированной доставке каждого сообщения, такой как негарантированная доставка (none), не менее одного раза, не более одного раза, только один раз.

Figure 4. Пример привязки из трех элементов
Порядок следования элементов привязки является значимым, потому что коллекция элементов используется для построения коммуникационного стека в соответствии с порядком элементов. Элемент привязки, последним добавленный в коллекцию, соответствует нижнему компоненту коммуникационного стека, а тот элемент который был добавлен первым, соответствует верхнему компоненту стека. Входящие сообщения проходят коммуникационный стек от нижнего компонента к верхнему, а исходящие наоборот, от верхнего к нижнему. Таким образом порядок элементов привязки в коллекции оказывает непосредственное влияние на то, как порядок в котором компоненты коммуникационного стека обрабатывают сообщения. WCF предоставляет набор предопределенных привязок, которые могут быть использованы для в большинства сценариев взаимодействия без определения специфических пользовательских привязок.

Контракты
Контракт WCF это коллекция операций, которые определяют, как конечная точка общается с внешним миром. Каждая операция представляет собой единицу обмена сообщениями, например, односторонний обмен сообщениями или обмен по схеме запрос/ответ.
Класс ContractDescription используется для описания контракта WCF и его операций. Внутри ContractDescription, каждая операция представлена соотвтествующим экземпляром класса OperationDescription, который описывает различные аспекты операции, такие как будет ли операция однонаправленной или по схеме запрос/ответ. Каждый OperationDescription содержит коллекцию MessageDescriptions, кторая описывает сообщения данной операции.
ContractDescription обычно создается на основе типа или интерфейса который определяет контракт используя программную модель WCF. Этот тип описывается с помощью атрибута ServiceContractAttribute, а его методы, которые соответствуют операциям конечной точки помечаются атрибутом OperationContractAttribute. Вы также можете создать ContractDescription в ручную без использования CLR типа, подписанного соответствующими атрибутами.
Дуплексный (duplex) контракт определяет два логических набора операций: набор операций, которые сервис предоставляет клиенту для вызова, и набор операций, которые клиент предоставляет для вызова сервису. Программная модель дуплексного контракта определяет каждый набор операций в виде отдельного класса (или интерфейса), при этом класс определяющий контракт сервиса помечается атрибутом ServiceContractAttribute, кторый в свою очередь ссылается на класс, определяющий контракт клиентских (callback) операций. Поэтому ContractDescription содержит ссылки на оба типа реализующие клиентский и сервисный наборы операций, группируя таким образом их в один дуплексный контракт.
Подобно привязкам, каждый контракт имеет имя и пространство имен, которые уникально идентифицируют его в метаданных сервиса.
Каждый контракт также имеет коллекцию ContractBehaviors, содержащую модули, котрые расширяют или модифицируют поведение контракта. Следующая секция описывает поведения более детально.Рис. 5. Класс ContractDescription описывает контракт WCF
Поведения
Поведения это типы, которые расширяют или модифицируют функциональность сервиса или клиента. Например ServiceMetadataBehavior управляет поведением сервиса при публикации метаданных. Подобным образом поведение безопасности управляет имперсонацией и авторизацией, поведение транзакций управляет участием в транзакциях и их авто-завершением.
Поведения также принимают участие в создании канала и поэтому могут модифицировать его на основе пользовательских настроек, а также управлять другими аспектами сервиса или канала.
Поведение сервиса это тип, который реализует интерфейс IServiceBehavior и применяется к сервису. Поведение канала это тип который реализует IChannelBehavior и применяется к клиентским каналам.

Описатели сервисов и каналов
Класс ServiceDescription представляет собой хранимую в оперативной памяти структуру, которая описывает сервис WCF включая его конечные точки, поведения, применяемые к этому сервису, а также тип (класс) который реализует сервис (см. Рис. 6). ServiceDescription используется для создания метаданных, конфигурации и каналов.
Вы можете создавать описатели сервисов в ручную. Также вы можете создать его из типов, помеченных определенными атрибутами WCF, и это является наиболее предпочтительным сценарием. Код для этого типа может быть написан в ручную или сгенерирован из WSDL с использованием инструмента svcutil.exe.
Несмотря на то, что объекты описателей сервисов могут быть созданы и наполнены явным образом, часто они создаются за кулисами как часть процесса исполнения сервиса.
Figure 6. Объектная модель описателей сервиса
Точно также на клиентской стороне ChannelDescription описывает клиентский канал WCF для конкретной конечной точки (рис. 7). Класс ChannelDescription содержит коллекцию IСhannelBehavior, представляющую поведения, применяемые к этому каналу. От также содержит ServiceEndpoint, котрый описывает конечную точку с которой этот канал будет взаимодействовать.
Заметьте, что в отличие от ServiceDescription, ChannelDescription описывает только одну конечную точку, с которой канал будет взаимодействовать.






Рис. 7. Объектная модель ChannelDescription

Среда исполнения WCF
Среда исполнения WCF это набор объектов, отвечающих за прием и передачу сообщений. Например, такие вещи, как форматирование сообщений, обеспечение безопасности, передача и прием сообщений с использованием различных транспортных протоколов, а также диспетчеризация принимаемых сообщений между соответствующими операциям, все это обеспечивается средой исполнения WCF.

Сообщение
Сообщение WCF это единица обмена данных между клиентом и конечной точкой сервиса. По своей сути Message WCF являeтся представлением сообщений SOAP InfoSet. Отметим что сообщения не имеют жесткой привязки к формату текстового представления XML. Вместо этого, в зависимости от используемого механизма кодировки, сообщение может быть сериализовано в двоичный формат WCF, текстовый XML, или любой другой формат специальный формат.

Каналы
Каналы представляют собой базовую абстракцию для механизма передачи сообщений в и приема сообщений из конечной точки. Вообще говоря, существует две категории каналов: первая, это транспортные каналы, которые принимают или передают бинарные потоки, используя для этого транспортные протоколы, такие как TCP, UDP или MSMQ. Вторая категория, это каналы протокола, которые реализуют базирующиеся на SOAP протоколы обработки и модификации сообщений. Например, канал безопасности (security channel) обеспечивает обработку специальных заголовков SOAP сообщений, а также может модифицировать тело сообщения, шифруя его. Каналы компонуются таким образом, чтобы обеспечить последовательную обработку сообщения несколькими каналами.

EndpointListener
Класс EndpointListener реализует конечную точку (ServiceEndpoint) во время исполнения. Свойства EndpointAddress, Contract, и Binding класса ServiceEndpoint (определяющие где, что и каким образом), соотносятся с такими свойствами EndpointListener-а, как адрес, диспетчер и фильтры сообщений и стек каналов соответственно. EndpointListener содержит стек каналов, который отвечает за отправку и прием сообщений.

ServiceHost и ChannelFactory
Среда исполнения WCF обычно создается при вызове метода ServiceHost.Open. ServiceHost (рис. 8) управляет созданием описателя сервиса (ServiceDescription) на основе типа реализующего сервис и заполнением коллекции конечных точек (ServiceEndpoint) определенных в коде и/или в конфигурационном файле. Затем ServiceHost использует полученный таким образом ServiceDescription при создании стека каналов в каждом из EndpointListener, которые создаются для каждой конечной точки (ServiceEndpoint) описателя сервиса (ServiceDescription).

Рис. 8. Объектная модель ServiceHost
Подобным образом на клиентской стороне, клиентская среда исполнения создается посредством класса ChannelFactory, который является клиентским эквивалентом класса ServiceHost.
ChannelFactory управляет созданием описателя канала (ChannelDescription), используя для этого тип, реализующий клиентский контракт, привязку и адрес конечной точки. Созданный описатель канала (ChannelDescription) используется для построения стека каналов.
В отличие от среды исполнения сервиса, клиентская среда исполнения не содержит EndpointListener-ов, потому что клиент всегда сам инициирует соединение с сервисом и ему не нужно «прослушивать» входящие соединения.

Примеры кода

Эта секция представляет примеры кода, которые иллюстрируют как создаются сервисы и клиенты. Данные примеры предназначены для иллюстрации основных концепций, а не для обучения приемам программирования в WCF.
Определение и реализация контракта
Как упоминалось выше, простейшим способом определить контракт является создание интерфейса или класса, помеченного атрибутом ServiceContractAttribute, позволяющее системе легко создать на его основе описатель контракта (ContractDescription).
Когда мы используем класс или интерфейс для определения контракта, каждый метод, который является членом контракта должен быть помечен атрибутом OperationContractAttribute. Например:

using System.ServiceModel;

//конракт WCF определен с использованием интерфейса
[ServiceContract]
public interface IMath
{
[OperationContract]
int Add(int x, int y);
}
Реализация контракта в данном случае сводится к созданию класса, реализующего IMath. Этот класс становится классом сервиса WCF. Например:

//этот класс сервиса реализует интерфейс
public class MathService : IMath
{
public int Add(int x, int y)
{ return x + y; }
}

Определение конечных точек и запуск сервиса
Конечные точки могут быть определены в коде или в конфигурационном файле. В примере, приведенном ниже, метод DefineEndpointImperatively демонстрирует простейший способ определения конечной точки в коде и запуска сервиса.
Метод DefineEndpointInConfig, демонстрирует определение аналогичной конечной точки при помощи конфигурационного файла (пример конфигурационного файла приведен ниже)

public class WCFServiceApp
{
public void
DefineEndpointImperatively()
{
//создаем хост для сервиса MathService
ServiceHost sh = new
ServiceHost(typeof(MathService));

//используем метод AddEndpoint для
//создания конечной точки
ServiceEndpoint и включения ее
//в описание
сервиса ServiceDescription
sh.AddServiceEndpoint(
typeof(IMath), //тип,
реализующий контракт
new WSHttpBinding(), //одна из стандартных привязок
"http://localhost/MathService/Ep1"); //адрес конечной точки

//создаем и открываем среду исполнения сервиса
sh.Open();

}

public void DefineEndpointInConfig()
{
создаем хост для сервиса
MathService
ServiceHost sh = new ServiceHost (typeof(MathService));

//создаем и открываем среду исполнения сервиса
sh.Open();

}
}


<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.servicemodel>
<services>

<service type="MathService">

<endpoint contract="IMath" binding="wsHttpBinding" address="http://localhost/MathService/Ep1">
</service>
</services>
</system.servicemodel>
</configuration>




Отправка сообщений конечной точке сервиса
Код, приведенный ниже, демонстрирует два способа отправки сообщения кончной точке сервиса IMath. Метод SendMessageToEndpoint скрывает создание канала, которое происходит за кулисами вызова, а метод SendMessageToEndpointUsingChannel демонстрирует явное создание канала.
Первый пример (SendMessageToEndpoint) использует утилиту svcutil.exe и метаданные сервиса для генерации контракта (IMath в этом примере), прокси класса (MathProxy), который реализует контракт, и связанного с ним конфигурационного файла (не показан в примере). В этом случае контракт, определяемый интерфейсом IMath определяет что (т.е. опрации, которые могут быть выполнены), в то время как сгенерированный конфигурационный файл содержит привязку (каким образом) и адрес (где).
Использовать этот прокси класс, означает создать его экземпляр и вызвать метод Add. За кулисами прокси класс создаст канал и использует его для связи с конечной точкой сервиса.
Во втором примере, в методе SendMessageToEndpointsUsingChannel демонстрируется создание связи с конечной точкой сервиса путем прямого использования ChannelFactory. В этом примере, вместо использования прокси класса и конфигурационного файла, канал создается явно методом ChannelFactory.CreateChannel. Также взамен использования конфигурационного файла для определения адреса и привязки конечной точки, эта информация передается в конструктор ChannelFactory в виде параметров. Третья часть информации, необходимая для определения кончной точки сервиса, известная как контракт, передается как тип T.

using System.ServiceModel;
//этот контракт сгенерирован утилитой svcutil.exe
//из метаданных сервиса
public interface IMath
{
[OperationContract]
public int Add(int x, int y)
{ return x + y; }
}


//этот класс сгенерирован утилитой svcutil.exe
//из
метаданных сервиса
//сгенерированный config не показан здесь
public
class MathProxy : IMath
{
...
}

public class WCFClientApp
{
public void SendMessageToEndpoint()
{
//используем прокси
класс, который бвл
//создан утилитой svcutil.exe на основе метаданных
сервиса
MathProxy proxy = new MathProxy();

int result =
proxy.Add(35, 7);
}
public void SendMessageToEndpointUsingChannel()
{
//используем ChannelFactory чтобы создать канал
//вы должны
указать адрес, привязку и
//тип, реализующий контракт (IMath)
ChannelFactory factory=new ChannelFactory(
new
WSHttpBinding(),
new EndpointAddress("http://localhost/MathService/Ep1"));
IMath channel=factory.CreateChannel();
int result=channel.Add(35,7);
factory.Close();

}
}

Определение специального поведения
Определение специального поведения заключается в реализации интерфейса IServiceBehavior (или IChannelBehavior в случае клиентского поведения). Код, приведенный ниже, демонстрирует пример поведения, которе реализует интерфейс IServiceBehavior. В реализации метода IServiceBehavior.ApplyBehavior код просматривает описатель сервиса ServiceDescription и выводит адрес, привязку и контракт для каждой конечной точки сервиса, а также имена всех поведений в ServiceDescription.
Это конкретное поведение использует также атрибут (унаследованный от System.Attribute) для того, чтобы сделать возможным декларативный стиль своего применения, как будет показано ниже. Однако, в целом, поведения не требуют использования атрибутов.

[AttributeUsageAttribute(
AttributeTargets.Class,
AllowMultiple=false,
Inherited=false)]
public class
InspectorBehavior : System.Attribute,
System.ServiceModel.IServiceBehavior
{
public void ApplyBehavior(
ServiceDescription description,
Collection behaviors)
{
Console.WriteLine("-------- Конечные точки ---------");
foreach
(ServiceEndpoint endpoint in description.Endpoints)
{
Console.WriteLine("--> Конечная точка");
Console.WriteLine("Endpoint
Address: {0}",
endpoint.Address);
Console.WriteLine("Endpoint Binding:
{0}",
endpoint.Binding.GetType().Name);
Console.WriteLine("Endpoint
Contract: {0}",
endpoint.Contract.ContractType.Name);
Console.WriteLine();
}
Console.WriteLine("-------- Поведения сервиса
--------");
foreach (IServiceBehavior behavior in description.Behaviors)
{
Console.WriteLine("--> Поведение");
Console.WriteLine("Behavior: {0}", behavior.GetType().Name);
Console.WriteLine();
}
}
}
Применение специального поведения
Все поведения могут быть применены имплементативно, путем добавления экземпляра поведения в ServiceDescription (или в ChannelDescription на клиентской стороне). Например,для имплементативного применения поведения InspectorBehavior используем следующий код:

ServiceHost sh = new
ServiceHost(typeof(MathService));
sh.AddServiceEndpoint(
typeof(IMath),
new WSHttpBinding(),
"http://localhost/MathService/Ep1");
//Добавляем поведение явно
InspectorBehavior behavior = new
InspectorBehavior();
sh.Description.Behaviors.Add(behavior);
sh.Open();

Дополнительно, поведения, которые унаследованы от System.Attribute могут быть применены к сервису декларативно. Например, поскольку InspectorBehavior унаследован от System.Attribute, он может быть применен декларативно, как показано здесь:

[InspectorBehavior]
public class MathService :
IMath
{
public int Add(int x, int y)
{ return x + y; }
}

Заключительный обзор
Сервис WCF публикует коллекцию конечных точек, где каждая конечная точка является порталом для взаимодействия с внешним миром. Каждая конечная точка имеет адрес, привязку, и контракт (анг. аббревиатура - ABC). Адрес определяет где расположена конечная точка, привязка определяет каким образом можно связаться с конечной точкой, и контракт определяет что представляет из себя конечная точка.
Для каждого сервиса существует описатель сервиса (ServiceDescription) который содержит коллекцию описателей конечных точек публикуемых этим сервисом. На основании этого описателя ServiceHost создает среду исполнения сервиса, которая содержит EndpointListener для каждой конечной точки сервиса. Адресу, привязке и контракту конечной точки, соответствуют адрес, диспетчер и фильтр сообщений и стек каналов EndpointListener-а соответственно.
Аналогичным образом, на клиенте, описатель канала содержит одну конечную точку, с которой связывается клиент. Используя описатель канала, ChannelFactory создает стек каналов который может общаться с конечной точкой сервиса.