Поделиться через


Проектирование контрактов услуг

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

Создание контракта службы

Службы предоставляют доступ к ряду операций. В приложениях Windows Communication Foundation (WCF) определите операции, создав метод и помечая его атрибутом OperationContractAttribute . Затем, чтобы создать контракт службы, сгруппируйте ваши операции, объявите их в интерфейсе, отмеченном атрибутом ServiceContractAttribute, или определите их в классе, отмеченном тем же атрибутом. (Базовый пример см. в статье "Практическое руководство. Определение контракта службы".)

Любые методы, у которых нет атрибута OperationContractAttribute, не являются операциями службы и не экспонируются службами WCF.

В этом разделе описываются следующие моменты принятия решений при разработке контракта службы:

  • Следует ли использовать классы или интерфейсы.

  • Как указать типы данных, которые требуется обменять.

  • Типы шаблонов обмена, которые можно использовать.

  • Можно ли внести явные требования к безопасности в контракте.

  • Ограничения для входных и выходных данных операций.

Классы или интерфейсы

Классы и интерфейсы представляют группирование функциональных возможностей и, следовательно, можно использовать для определения контракта службы WCF. Однако рекомендуется использовать интерфейсы, так как они напрямую моделиируют контракты служб. Без реализации интерфейсы не более чем определяют группирование методов с определенными сигнатурами. Реализуйте интерфейс контракта службы, после чего вы реализовали службу WCF.

Все преимущества управляемых интерфейсов применяются к интерфейсам контракта службы:

  • Интерфейсы контракта службы могут расширить любое количество других интерфейсов контракта службы.

  • Один класс может реализовать любое количество контрактов службы, реализуя эти интерфейсы контракта службы.

  • Вы можете изменить реализацию контракта службы, изменив реализацию интерфейса, а контракт службы остается неизменным.

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

Замечание

При наследовании от других интерфейсов контракта службы нельзя переопределить свойства операций, такие как имя или пространство имен. Если вы попытаетесь это сделать, создастся новая операция в текущем сервисном контракте.

Пример использования интерфейса для создания контракта службы см. в статье "Практическое руководство. Создание службы с помощью интерфейса контракта".

Однако можно использовать класс для определения контракта службы и реализации этого контракта одновременно. Преимущество создания служб путем применения ServiceContractAttribute и OperationContractAttribute непосредственно к классу и методам класса, соответственно, заключается в скорости и простоте. Недостатки в том, что управляемые классы не поддерживают множественное наследование, и в результате они могут одновременно реализовать только один контракт сервиса. Кроме того, любые изменения в сигнатурах класса или метода изменяют публичный контракт для этого сервиса, что может предотвратить использование сервиса клиентами, которые не подвергались изменениям. Дополнительные сведения см. в разделе "Реализация контрактов службы".

Пример, использующий класс для создания контракта службы и реализующего его одновременно, см. в статье "Практическое руководство. Создание службы с классом контракта".

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

Параметры и возвращаемые значения

Каждая операция имеет возвращаемое значение и параметр, даже если они имеются void. Однако в отличие от локального метода, в котором можно передавать ссылки на объекты из одного объекта в другой, операции службы не передают ссылки на объекты. Вместо этого они передают копии объектов.

Это важно, так как каждый тип, используемый в параметре или возвращаемом значении, должен быть сериализуемым; То есть необходимо преобразовать объект этого типа в поток байтов и из потока байтов в объект.

По умолчанию примитивные типы сериализуются, как и многие типы в .NET Framework.

Замечание

Значения имен параметров в сигнатуре операции являются частью контракта и чувствительны к регистру. Если вы хотите использовать одно и то же имя параметра локально, но измените имя в опубликованных метаданных, см. раздел System.ServiceModel.MessageParameterAttribute.

Контракты данных

Приложения, ориентированные на услуги, такие как приложения Windows Communication Foundation (WCF), предназначены для взаимодействия с самым широким числом клиентских приложений на платформах Microsoft и не Microsoft. Для обеспечения максимально возможной совместимости рекомендуется пометить ваши типы атрибутами DataContractAttribute и DataMemberAttribute для создания контракта данных, который является частью контракта службы и описывает данные, которыми обмениваются операции службы.

Контракты данных являются контрактами с согласия: ни тип, ни член данных не сериализуется, если вы явно не применяете атрибут контракта данных. Контракты данных не связаны с областью доступа управляемого кода: приватные данные могут быть сериализованы и отправлены в другое место для публичного доступа. (Базовый пример контракта данных см. в статье "Практическое руководство. Создание базового контракта данных для класса или структуры".) WCF обрабатывает определение базовых сообщений SOAP, которые обеспечивают функциональность операции, а также сериализацию типов данных в тексте сообщений и из него. Если типы данных сериализуются, вам не нужно думать о базовой инфраструктуре обмена сообщениями при проектировании операций.

Хотя обычное приложение WCF использует DataContractAttribute и DataMemberAttribute атрибуты для создания контрактов данных для операций, можно использовать другие механизмы сериализации. Стандартные ISerializable, SerializableAttribute и IXmlSerializable механизмы предназначены для обработки сериализации ваших типов данных в основные сообщения SOAP, которые передают их из одного приложения в другое. Вы можете использовать дополнительные стратегии сериализации, если для типов данных требуется специальная поддержка. Дополнительные сведения о вариантах сериализации типов данных в приложениях WCF см. в разделе "Указание передачи данных в контрактах служб".

Сопоставление параметров и возвращаемых значений в обмене сообщениями

Операции службы поддерживаются базовым обменом сообщениями SOAP, которые передают данные приложения обратно и вперед, помимо данных, необходимых приложению для поддержки определенных стандартных функций безопасности, транзакций и сеансов. Так как это так, сигнатура операции службы определяет определенный базовый шаблон обмена сообщениями (MEP), который может поддерживать передачу данных и необходимые функции. В модели программирования WCF можно указать три шаблона: запрос и ответ, одностороннюю и дуплексную схему сообщений.

Запрос и ответ

Шаблон запроса и ответа — это шаблон, в котором отправитель запроса (клиентское приложение) получает ответ, с которым сопоставляется запрос. Это MEP по умолчанию, поскольку он поддерживает выполнение операций, в рамках которых один или несколько параметров передаются, а возвращаемое значение передается вызывающему. Например, в следующем примере кода C# показана базовая операция службы, которая принимает одну строку и возвращает строку.

[OperationContractAttribute]  
string Hello(string greeting);  

Ниже приведен эквивалентный код Visual Basic.

<OperationContractAttribute()>  
Function Hello (ByVal greeting As String) As String  

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

Обратите внимание, что если вы не укажете другой базовый шаблон сообщения, даже операции службы, возвращаемые void (Nothing в Visual Basic), обмениваются сообщениями запроса и ответа. Результатом операции является то, что если клиент не вызывает операцию асинхронно, клиент перестает обрабатывать до получения возвращаемого сообщения, даже если это сообщение пусто в обычном случае. В следующем примере кода C# показана операция, которая не возвращается, пока клиент не получит пустое сообщение в ответ.

[OperationContractAttribute]  
void Hello(string greeting);  

Ниже приведен эквивалентный код Visual Basic.

<OperationContractAttribute()>  
Sub Hello (ByVal greeting As String)  

Приведенный выше пример может замедлить производительность клиента и скорость реагирования, если операция занимает много времени, однако операции запроса/ответа имеют свои преимущества, даже если они возвращают void. Наиболее очевидным является то, что ошибки SOAP могут быть возвращены в ответном сообщении, что указывает на то, что произошла ошибка, связанная с службой, независимо от того, произошла ли связь или обработка. Ошибки SOAP, указанные в контракте службы, передаются клиентскому приложению в качестве FaultException<TDetail> объекта, где параметр типа является типом, указанным в контракте службы. Это упрощает уведомление клиентов об ошибках в службах WCF. Дополнительные сведения об исключениях, сбоях SOAP и обработке ошибок см. в разделе "Указание и обработка ошибок" в контрактах и службах. Чтобы увидеть пример службы запроса/ответа и клиента, см. раздел «Как: Создать контракт Request-Reply». Дополнительные сведения о проблемах с шаблоном ответа на запрос см. в разделе Request-Reply Services.

Односторонний

Если клиент приложения службы WCF не должен ожидать завершения операции и не обрабатывает ошибки SOAP, операция может указать шаблон одностороннего сообщения. Односторонняя операция — это операция, в которой клиент вызывает операцию и продолжает обработку после того, как WCF записывает сообщение в сеть. Как правило, это означает, что если данные, отправляемые в исходящем сообщении, не слишком большие, клиент продолжает работу практически немедленно (если ошибка при отправке данных отсутствует). Этот тип шаблона обмена сообщениями поддерживает поведение, подобное событиям от клиента к приложению-службе.

Обмен сообщениями, при котором отправляется одно сообщение и не принимается ни одного, не поддерживает операцию службы, указывающую возвращаемое значение, отличное от void; в этом случае выбрасывается исключение InvalidOperationException.

Нет возвращаемого сообщения также означает, что ошибка SOAP не может быть возвращена, чтобы указать любые ошибки в обработке или обмене данными. (Для обмена данными об ошибках при односторонней операции требуется дуплексный шаблон обмена сообщениями.)

Чтобы указать односторонний обмен сообщениями для операции, которая возвращается void, установите значение IsOneWay для свойства true, как показано в следующем примере кода C#.

[OperationContractAttribute(IsOneWay=true)]  
void Hello(string greeting);  

Ниже приведен эквивалентный код Visual Basic.

<OperationContractAttribute(IsOneWay := True)>  
Sub Hello (ByVal greeting As String)  

Этот метод идентичен предыдущему примеру запроса-ответа, но задание свойства IsOneWay значением true означает, что, несмотря на идентичность метода, операция сервиса не отправляет ответное сообщение, и клиенты получают ответ мгновенно после передачи исходящего сообщения на канал. Пример см. в статье "Практическое руководство. Создание контракта One-Way". Дополнительные сведения о односторонней схеме см. в разделе One-Way Services.

дуплекс

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

Дуплексный шаблон немного сложнее, чем шаблоны запроса или ответа или односторонней связи из-за дополнительного механизма взаимодействия с клиентом.

Чтобы создать дуплексный контракт, необходимо также разработать контракт обратного вызова и назначить тип этого контракта обратного вызова свойству атрибута CallbackContract, который помечает контракт службы ServiceContractAttribute.

Чтобы реализовать дуплексный шаблон, необходимо создать второй интерфейс, содержащий объявления методов, вызываемые на клиенте.

Пример создания службы и клиента, который обращается к этой службе, см. в статье "Практическое руководство. Создание дуплексного контракта и практическое руководство. Доступ к службам с помощью дуплексного контракта". Рабочий пример см. в разделе Дуплекс. Дополнительные сведения о проблемах с использованием дуплексных контрактов см. в разделе Дуплексные службы.

Осторожность

Когда служба получает дуплексное сообщение, она смотрит на ReplyTo элемент в этом входящем сообщении, чтобы определить, куда отправлять ответ. Если канал, используемый для получения сообщения, не защищен, то ненадежный клиент может отправить вредоносное сообщение, содержащее ReplyTo целевого компьютера, что приведет к отказу в обслуживании (DOS) этого целевого компьютера.

Параметры out и ref

В большинстве случаев можно использовать in параметры (ByValв Visual Basic) и outref параметры (ByRefв Visual Basic). Поскольку оба out и ref параметры указывают, что данные возвращаются из операции, подпись операции, например следующая, указывает, что требуется операция запроса или ответа, даже если сигнатура операции возвращается void.

[ServiceContractAttribute]  
public interface IMyContract  
{  
  [OperationContractAttribute]  
  public void PopulateData(ref CustomDataType data);  
}  

Ниже приведен эквивалентный код Visual Basic.

<ServiceContractAttribute()> _  
Public Interface IMyContract  
  <OperationContractAttribute()> _  
  Public Sub PopulateData(ByRef data As CustomDataType)  
End Interface  

Единственными исключениями являются те случаи, в которых подпись имеет определенную структуру. Например, можно использовать привязку NetMsmqBinding для обмена данными с клиентами, только если метод, использованный для объявления операции, возвращает void; не должно быть выходного значения, ни возвращаемого ref значения, ни параметра out.

Кроме того, для использования out или ref параметров требуется, чтобы операция содержит базовое сообщение ответа для возврата измененного объекта. Если операция является односторонним, InvalidOperationException исключение создается во время выполнения.

Указание уровня защиты сообщений в контракте

При разработке контракта необходимо также определить уровень защиты сообщений служб, реализующих контракт. Это необходимо, только если безопасность сообщений применяется к привязке в конечной точке контракта. Если в привязке отключена защита (то есть, если предоставленная системой привязка устанавливает System.ServiceModel.SecurityMode на значение SecurityMode.None), то вам не нужно выбирать уровень защиты сообщений для контракта. В большинстве случаев системные привязки с применением безопасности на уровне сообщений обеспечивают достаточный уровень защиты, и вам не нужно учитывать уровень защиты для каждой операции или для каждого сообщения.

Уровень защиты — это значение, указывающее, подписаны ли сообщения (или части сообщений), поддерживающие службу, подписываются и шифруются или отправляются без подписей или шифрования. Уровень защиты можно задать в различных областях: на уровне обслуживания для определенной операции для сообщения в рамках этой операции или части сообщения. Значения, заданные в одной области, становятся значениями по умолчанию для более мелких областей, если они не переопределены явно. Если конфигурация привязки не может предоставить необходимый минимальный уровень защиты для контракта, создается исключение. И если значения уровня защиты явно не заданы в контракте, конфигурация привязки управляет уровнем защиты для всех сообщений, если привязка имеет безопасность сообщений. Это поведение по умолчанию.

Это важно

Решение о том, следует ли явно устанавливать области действия контракта на уровень защиты ниже полного ProtectionLevel.EncryptAndSign, как правило, является решением, которое обменивает некоторую степень безопасности на повышение производительности. В этих случаях ваши решения должны основываться на ваших операциях и ценности данных, которыми они обмениваются. Дополнительные сведения см. в разделе "Защита служб".

Например, следующий пример кода не задает ни ProtectionLevel, ни ProtectionLevel свойство в контракте.

[ServiceContract]  
public interface ISampleService  
{  
  [OperationContractAttribute]  
  public string GetString();  
  
  [OperationContractAttribute]  
  public int GetInt();
}  

Ниже приведен эквивалентный код Visual Basic.

<ServiceContractAttribute()> _  
Public Interface ISampleService  
  
  <OperationContractAttribute()> _  
  Public Function GetString()As String  
  
  <OperationContractAttribute()> _  
  Public Function GetData() As Integer  
  
End Interface  

При взаимодействии с реализацией в конечной точке с настройками по умолчанию (ISampleService и WSHttpBinding, которые System.ServiceModel.SecurityMode), все сообщения шифруются и подписываются, так как это уровень защиты по умолчанию. Однако, когда служба ISampleService используется с настройками по умолчанию (по умолчанию BasicHttpBinding — это SecurityMode, который является None), все сообщения отправляются в виде текста, так как для этой привязки нет безопасности, поэтому уровень защиты игнорируется (то есть сообщения не шифруются и не подписываются). Если SecurityMode был изменен на Message, то эти сообщения будут зашифрованы и подписаны, поскольку это теперь станет уровнем защиты по умолчанию для привязки.

Если вы хотите явно указать или изменить требования к защите для вашего контракта, задайте свойство ProtectionLevel (или любое из свойств ProtectionLevel в меньшей области) на уровень, требуемый вашим сервисным контрактом. В этом случае использование явной настройки требует, чтобы поддержка этой настройки была обеспечена по крайней мере для соответствующей области применения. Например, в следующем примере кода указывается одно ProtectionLevel значение явно для GetGuid операции.

[ServiceContract]  
public interface IExplicitProtectionLevelSampleService  
{  
  [OperationContractAttribute]  
  public string GetString();  
  
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]  
  public int GetInt();
  [OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]  
  public int GetGuid();
}  

Ниже приведен эквивалентный код Visual Basic.

<ServiceContract()> _
Public Interface IExplicitProtectionLevelSampleService
    <OperationContract()> _
    Public Function GetString() As String
    End Function
  
    <OperationContract(ProtectionLevel := ProtectionLevel.None)> _
    Public Function GetInt() As Integer
    End Function
  
    <OperationContractAttribute(ProtectionLevel := ProtectionLevel.EncryptAndSign)> _
    Public Function GetGuid() As Integer
    End Function
  
End Interface  

Служба, реализующая этот IExplicitProtectionLevelSampleService контракт, и имеет конечную точку, которая использует значение по умолчанию (значение по умолчанию WSHttpBindingSystem.ServiceModel.SecurityMode, которое является Message) имеет следующее поведение:

  • GetString Сообщения об операциях шифруются и подписываются.

  • GetInt Сообщения операции отправляются как незашифрованные и неподписанные (т. е. обычный текст).

  • GetGuid Операция System.Guid возвращается в сообщении, зашифрованном и подписанном.

Дополнительные сведения о уровнях защиты и их использовании см. в разделе "Общие сведения о уровне защиты". Дополнительные сведения о безопасности см. в разделе "Защита служб".

Другие требования к подписи операции

Для некоторых функций приложения требуется определенный тип подписи операции. Например, привязка NetMsmqBinding поддерживает долговечные службы и клиентов, при которых приложение может перезапуститься в середине обмена данными и продолжить работу с того места, где оно остановилось, не теряя ни одного сообщения. (Дополнительные сведения см. в статье Очереди в WCF.) Однако устойчивые операции должны принимать только один in параметр и не иметь возвращаемого значения.

Другим примером является использование типов Stream в операциях. Stream Поскольку параметр включает весь текст сообщения, если входные или выходные данные (то есть параметр, ref параметр, out параметр или возвращаемое значение) имеют типStream, то он должен быть единственным входным или выходным данным, указанным в операции. Кроме того, параметр или возвращаемый тип должен быть Streamлибо , System.ServiceModel.Channels.Messageлибо System.Xml.Serialization.IXmlSerializable. Дополнительные сведения о потоках см. в разделе "Большие данные" и "Потоковая передача".

Имена, пространства имен и обфускация

Имена и пространства имен типов .NET в определении контрактов и операций важны при преобразовании контрактов в WSDL и при создании и отправке сообщений контрактов. Поэтому настоятельно рекомендуется явно задавать имена контрактов и пространства имен служб с помощью свойств Name и Namespace всех вспомогательных атрибутов контракта, таких как ServiceContractAttribute, OperationContractAttribute, DataContractAttribute, DataMemberAttribute и других атрибутов контракта.

Одним из результатов этого является то, что если имена и пространства имен не заданы явным образом, использование маскировки IL в сборке изменяет имена типов контракта и пространства имен и приводит к изменению WSDL и проводных обменов, которые обычно завершаются сбоем. Если имена контрактов и пространства имен не заданы явно, но вы все-таки намерены использовать маскировку, используйте атрибуты ObfuscationAttribute и ObfuscateAssemblyAttribute, чтобы предотвратить изменение имен типов контрактов и пространств имен.

См. также