Diseño de contratos de servicios
En este tema se describe qué son los contratos de servicios, cómo se definen, qué operaciones están disponibles (y las implicaciones para los intercambios de mensajes subyacentes), qué tipos de datos se utilizan y otras cuestiones que le ayudan a diseñar operaciones que satisfagan adecuadamente los requisitos de su escenario.
Crear un contrato de servicios
Los servicios son grupos de operaciones. Para crear un contrato de servicios debe modelar las operaciones y especificar su agrupación. En las aplicaciones Windows Communication Foundation (WCF), defina las operaciones creando un método y marcándolo con el atributo OperationContractAttribute. A continuación, para crear un contrato de servicios, agrupe sus operaciones, declarándolas dentro de una interfaz marcada con el atributo ServiceContractAttribute, o bien definiéndolas en una clase marcada con el mismo atributo. (Para obtener un ejemplo básico, vea Definición de un contrato de servicio de Windows Communication Foundation.)
Los métodos que no tienen un atributo OperationContractAttribute no son operaciones de servicio y no están expuestos para ser utilizados por los clientes de servicios WCF. Como cualquier método administrado, solo pueden ser llamados por objetos dentro de su ámbito de acceso declarado.
De una manera similar, también es válido crear una clase de contrato de servicios o interfaz que no declare operaciones de servicios –el efecto es igual que una clase o interfaz sin métodos. Cualquier servicio creado utilizando un contrato de este tipo no expone ninguna operación para que los clientes la utilicen. Este tema describe los puntos de decisión siguientes al diseñar un contrato de servicios:
Si deben utilizarse clases o interfaces.
Cómo especificar los tipos de datos que desea intercambiar.
Los tipos de modelos de intercambio que puede utilizar.
Si puede hacer que los requisitos de seguridad explícitos sean parte del contrato.
Las restricciones para las entradas y salidas de la operación.
Clases o interfaces
Tanto las clases como las interfaces representan una agrupación de funcionalidad y, por consiguiente, ambas se pueden utilizar para definir un contrato de servicios WCF. Sin embargo, se recomienda que utilice las interfaces porque modelan directamente los contratos de servicios. Sin una implementación, las interfaces no hacen más que definir una agrupación de métodos con ciertas firmas. Igualmente, un contrato de servicios sin una implementación define una agrupación de operaciones con ciertas firmas. Implemente una interfaz de contrato de servicio y habrá implementado un servicio WCF.
Todas las ventajas de las interfaces administradas se aplican a las interfaces de contrato de servicio:
Las interfaces del contrato de servicio pueden extender cualquier número de otras interfaces del contrato de servicio.
Una única clase puede implementar cualquier número de contratos de servicios implementando esas interfaces del contrato de servicio.
Puede modificar la implementación de un contrato de servicios cambiando la implementación de la interfaz, mientras el contrato de servicios sigue siendo el mismo.
Puede controlar la versión de su servicio implementando la interfaz antigua y la nueva. Los clientes antiguos se conectan a la versión original, mientras los clientes más nuevos pueden conectarse a la versión más nueva.
Nota: |
---|
Al heredar de otras interfaces del contrato de servicio, no puede invalidar las propiedades de operación, como el nombre o espacio de nombres. Si intenta hacerlo, crea una nueva operación en el contrato de servicios actual. |
Para obtener un ejemplo de cómo usar una interfaz para crear un contrato de servicio, consulte Procedimiento para crear un servicio con una interfaz de contrato.
Sin embargo, puede utilizar una clase para definir un contrato de servicios e implementar dicho contrato al mismo tiempo. La ventaja de crear sus servicios aplicando directamente ServiceContractAttribute y OperationContractAttribute a la clase y los métodos en la clase, respectivamente, es la velocidad y la simplicidad. Las desventajas son que las clases administradas no admiten la herencia múltiple, y como resultado solo pueden implementar uno contrato de servicios a la vez. Además, cualquier modificación de las firmas de la clase o del método modifica el contrato público para ese servicio, lo que puede impedir que los clientes no modificados utilicen su servicio. Para obtener más información, vea Implementación de contratos de servicio.
Para obtener un ejemplo donde se utiliza una clase para crear un contrato de servicio y donde se implementa, consulte Cómo: Crear un contrato de Windows Communication Foundation con una clase.
En este punto, debería entender la diferencia entre definir su contrato de servicios utilizando una interfaz y utilizando una clase. El paso siguiente consiste en decidir qué datos se pueden intercambiar entre un servicio y sus clientes.
Parámetros y valores devueltos
Cada operación devuelve un valor y un parámetro, incluso si estos no son más que void. Sin embargo, a diferencia de un método local, en el que puede pasar las referencias a los objetos de un objeto a otro, las operaciones del servicio no pasan las referencias a los objetos. En su lugar, pasan copias de los objetos.
Esto es significativo porque cada tipo utilizado en un parámetro o valor devuelto debe ser serializable; es decir, debe ser posible convertir un objeto de ese tipo en una secuencia de bytes y de una secuencia de bytes en un objeto.
Los tipos primitivos son serializables de forma predeterminada, como muchos tipos en .NET Framework.
Nota: |
---|
El valor de los nombres de parámetro en la firma de la operación forma parte del contrato y distingue entre mayúsculas y minúsculas. Si desea utilizar localmente el mismo nombre de parámetro pero modificar el nombre en los metadatos publicados, vea System.ServiceModel.MessageParameterAttribute. |
Contratos de datos
Las aplicaciones orientadas a servicios como aplicaciones Windows Communication Foundation (WCF) están diseñadas para interoperar con el número más amplio posible de aplicaciones cliente tanto en plataformas Microsoft y como en plataformas no Microsoft. Para obtener la interoperabilidad más amplia posible, se recomienda que marque sus tipos con los atributos DataContractAttribute y DataMemberAttribute para crear un contrato de datos, que es la parte del contrato de servicios que describe los datos que intercambian sus operaciones de servicio.
Los contratos de datos son contratos de estilo de participación: ningún tipo o miembro de datos se serializa a menos que aplique explícitamente el atributo de contrato de datos. Los contratos de datos no están relacionados con el ámbito de acceso del código administrado: los miembros de datos privados se pueden serializar y enviar a otra parte para obtener acceso a ellos públicamente. (Para obtener un ejemplo básico de un contrato de datos, vea Cómo: Crear un contrato de datos básicos para una clase o estructura.) WCF controla la definición de los mensajes SOAP subyacentes que habilitan la funcionalidad de la operación así como la serialización de los tipos de datos dentro y fuera del cuerpo de los mensajes. Siempre y cuando los tipos de datos sean serializables, no necesita pensar en la infraestructura de intercambio de mensajes subyacentes al diseñar las operaciones.
Aunque la aplicación típica WCF utiliza los atributos DataContractAttribute y DataMemberAttribute para crear los contratos de datos para las operaciones, puede utilizar otros mecanismos de serialización. Los mecanismos estándares ISerializable, SerializableAttribute y IXmlSerializable trabajan para administrar la serialización de sus tipos de datos en los mensajes SOAP subyacentes que los llevan de una aplicación a otra. Puede emplear más estrategias de serialización si sus tipos de datos requieren soporte especial. Para las opciones Para obtener más información sobre para la serialización de tipos de datos en aplicaciones WCF, vea Especificación de transferencia de datos en contratos de servicio.
Es importante tener en cuenta que los nombres de CLR en la definición de un contrato de servicio y sus operaciones son significativos y no se deben confundir. Para evitar la confusión de los tipos que se usan para definir un contrato de servicio, use los atributos ObfuscateAssemblyAttribute y ObfuscationAttribute.
Asignar los parámetros y los valores devueltos a los intercambios de mensajes
Las operaciones de servicio están soportadas por un intercambio subyacente de mensajes SOAP que transfiere los datos de la aplicación, además de los datos requeridos por la aplicación para soportar cierta seguridad estándar, transacción y características relacionadas con la sesión. Dado que éste es el caso, la firma de una operación de servicio dicta un cierto modelo de intercambio de mensajes (MEP) subyacente que puede admitir la transferencia de datos y las características que una operación requiere. Puede especificar tres modelos en el modelo de programación WCF: modelos de mensajes de solicitud/respuesta, unidireccionales y dúplex.
Solicitud/Respuesta
Un modelo de solicitud/respuesta es uno en el que un remitente de la solicitud (una aplicación cliente) recibe una respuesta con la que está relacionada la solicitud. Éste es el MEP predeterminado porque soporta tanto una operación en la que uno o más parámetros se pasan a la operación como una devolución y uno o más valores de salida que la operación devuelve al llamador. Por ejemplo, en el ejemplo de código de C# siguiente, se muestra una operación de servicio básica que toma una cadena y devuelve una cadena.
[OperationContractAttribute]
string Hello(string greeting);
A continuación, se muestra el código equivalente en Visual Basic.
<OperationContractAttribute()>
Function Hello (ByVal greeting As String) As String
Esta firma de operación dicta la forma del intercambio de mensajes subyacente. Si no existiera correlación, WCF no podría determinar a qué operación va destinado el valor devuelto.
Tenga en cuenta que a menos que especifique un modelo de mensaje subyacente diferente, incluso las operaciones de servicio que devuelven void del retorno (Nothing en Visual Basic) son intercambios de mensajes de solicitud/respuesta. El resultado para su operación es que a menos que un cliente invoque de forma asincrónica la operación, el cliente detiene el procesamiento hasta que se reciba el mensaje de retorno, aunque ese mensaje esté normalmente vacío. En el ejemplo de código de C# siguiente, se muestra una operación que no regresa hasta que el cliente ha recibido un mensaje vacío como respuesta.
[OperationContractAttribute]
void Hello(string greeting);
A continuación, se muestra el código equivalente en Visual Basic.
<OperationContractAttribute()>
Sub Hello (ByVal greeting As String)
El ejemplo anterior puede desacelerar rendimiento del cliente y la receptividad si la operación tarda mucho tiempo en realizarse, pero hay ventajas para las operaciones de solicitud/respuesta incluso cuando devuelven void. La más obvia es que los errores SOAP pueden devolverse en el mensaje de respuesta, lo que indica que se ha producido alguna condición de error relacionada con el servicio, bien en la comunicación bien en el procesamiento. Los errores SOAP que se especifican en un contrato de servicios se pasan a la aplicación cliente como un objeto FaultException, donde el parámetro de tipo es el tipo especificado en el contrato de servicios. Esto facilita la notificación a los clientes de las condiciones de error en los servicios WCF. Para obtener más información sobre las excepciones, los errores SOAP, y el control de errores, vea Especificación y administración de errores en contratos y servicios. Para ver un ejemplo de servicio de solicitud/respuesta y cliente, vea Creación de un contrato de solicitud‑respuesta. Para obtener más información sobre los problemas con el modelo de solicitud/respuesta, vea Request-Reply Services.
Unidireccional
Si el cliente de una aplicación de servicio WCF no debe esperar a que finalice la operación y no procesa errores SOAP, la operación puede especificar un modelo de mensaje unidireccional. Una operación unidireccional es una en la que un cliente invoca una operación y continúa el procesamiento después de que WCF escribe el mensaje en la red. Normalmente, esto significa que, salvo que los datos que se están enviando en el mensaje saliente sean extremadamente grandes, el cliente sigue ejecutándose de manera prácticamente inmediata (a menos que se produzca un error al enviar los datos). Este tipo de modelo de intercambio de mensajes soporta el comportamiento como evento de un cliente a una aplicación de servicio.
Un intercambio de mensajes en el que se envía un mensaje y no se recibe ninguno no puede soportar una operación de servicio que especifique un valor devuelto distinto de void; en este caso se inicia una excepción InvalidOperationException.
Por lo tanto, ningún mensaje de retorno significa que no puede haber ningún error SOAP devuelto para indicar cualquier error en el procesamiento o la comunicación. (La comunicación de información de error cuando las operaciones son operaciones unidireccionales requiere un modelo de intercambio de mensajes dúplex.)
Para especificar un intercambio de mensajes unidireccional para una operación que devuelve void, establezca la propiedad IsOneWay en true, como en el ejemplo de código de C# siguiente.
[OperationContractAttribute(IsOneWay=true)]
void Hello(string greeting);
A continuación, se muestra el código equivalente en Visual Basic.
<OperationContractAttribute(IsOneWay := True)>
Sub Hello (ByVal greeting As String)
Este método es idéntico al ejemplo de solicitud/respuesta anterior, pero estableciendo la propiedad IsOneWay en true significa que, aunque el método es idéntico, la operación de servicio no envía un mensaje de retorno y los clientes devuelven inmediatamente una vez el mensaje saliente se ha entregado al nivel del canal. Para obtener un ejemplo, vea Cómo crear un contrato unidireccional. Para obtener más información sobre el modelo unidireccional, vea One-Way Services.
Dúplex
Un modelo dúplex se caracteriza por la capacidad tanto del servicio y como del cliente para enviarse mensajes entre sí independientemente de si se está utilizando una mensajería unidireccional o de solicitud/respuesta. Esta forma de comunicación bidireccional es útil para los servicios que deben comunicarse directamente con el cliente, o para proporcionar una experiencia asincrónica a cada lado de un intercambio de mensajes, incluido el comportamiento similar a un evento.
El modelo dúplex es ligeramente más complejo que los modelos de solicitud/respuesta o unidireccionales debido al mecanismo adicional para comunicarse con el cliente.
Para diseñar un contrato dúplex, también debe diseñar un contrato de devolución de llamada y asignar el tipo de ese contrato de devolución de llamada a la propiedad CallbackContract del atributo ServiceContractAttribute que marca el contrato de servicio.
Para implementar un modelo dúplex, debe crear una segunda interfaz que contenga las declaraciones de método a las que se llaman en el cliente.
Para obtener un ejemplo de la creación de un servicio y un cliente que tiene acceso al servicio, vea Creación de un contrato dúplex y Cómo: Obtener acceso a los servicios con un contrato dúplex. Para obtener un ejemplo práctico, vea Dúplex. Para obtener más información sobre los problemas del uso de contratos dúplex, vea Servicios dúplex.
Precaución: |
---|
Cuando un servicio recibe un mensaje dúplex, examina el elemento ReplyTo en ese mensaje entrante para determinar dónde enviar la respuesta. Si no se protege el canal que se utiliza para recibir el mensaje, un cliente que no es de confianza podría enviar un mensaje malintencionado con un equipo de destino ReplyTo, provocando una denegación de servicio (DoS) de ese equipo de destino. |
Parámetros out y ref
En la mayoría de casos puede utilizar parámetros in (ByVal en Visual Basic) y out y parámetros ref (ByRef en Visual Basic). Dado que tanto el parámetro out como ref indican que los datos son devueltos por una operación, una firma de operación como la siguiente especifica que se requiere una operación de solicitud/respuesta aunque la firma de la operación devuelva void.
[ServiceContractAttribute]
public interface IMyContract
{
[OperationContractAttribute]
public void PopulateData(ref CustomDataType data);
}
A continuación, se muestra el código equivalente en Visual Basic.
[Visual Basic]
<ServiceContractAttribute()> _
Public Interface IMyContract
<OperationContractAttribute()> _
Public Sub PopulateData(ByRef data As CustomDataType)
End Interface
Las únicas excepciones son esos casos en los que su firma tiene una estructura determinada. Por ejemplo, solo puede utilizar el enlace NetMsmqBinding para comunicarse con los clientes si el método usado para declarar una operación devuelve void; no puede haber ningún valor de salida, tanto si se trata de un valor devuelto, ref, o bien un parámetro out.
Además, la utilización de los parámetros out o ref requiere que la operación tenga un mensaje de respuesta subyacente para devolver el objeto modificado. Si su operación es unidireccional, se inicia una excepción InvalidOperationException en tiempo de ejecución.
Especificar el nivel de protección del mensaje en el contrato
Al diseñar su contrato, también debe decidir el nivel de protección del mensaje de los servicios que implementa su contrato. Esto solo es necesario si la seguridad del mensaje se aplica al enlace en el extremo del contrato. Si el enlace tiene la seguridad desactivada (es decir, si el enlace proporcionado por el sistema establece System.ServiceModel.SecurityMode en el valor System.ServiceModel.SecurityMode.None) no tiene que decidir sobre el nivel de protección del mensaje para el contrato. En la mayoría de los casos, los enlaces proporcionados por el sistema a los que se aplica la seguridad del nivel de mensaje, ofrecen un nivel de protección suficiente que hace innecesario el nivel de protección para cada operación o mensaje.
El nivel de protección es un valor que especifica si los mensajes (o partes del mensaje) que soportan un servicio están firmados, firmados y cifrados, o si se envían sin firmar o cifrar. El nivel de protección se puede establecer en varios ámbitos: en el nivel del servicio, para una operación determinada, para un mensaje dentro de esa operación, o una parte del mensaje. Los valores establecidos en un ámbito se convierten en el valor predeterminado para los ámbitos menores a menos que se invalide explícitamente. Si una configuración de enlace no puede proporcionar el nivel de protección mínimo necesario para el contrato, se produce una excepción. Y cuando ningún valor de nivel de protección se establece explícitamente en el contrato, la configuración de enlace controla el nivel de protección para todos los mensajes si el enlace tiene seguridad de mensajes. Éste es el comportamiento predeterminado.
Nota: |
---|
Decidir si establecer explícitamente varios ámbitos de un contrato en un nivel de protección inferior al nivel de protección completo de System.Net.Security.ProtectionLevel.EncryptAndSign generalmente es una decisión que canjea cierto grado de seguridad por un aumento del rendimiento. En estos casos, las decisiones girarán en torno a las operaciones y al valor de los datos que intercambian. Para obtener más información, vea Seguridad de servicios. |
Por ejemplo, el ejemplo de código siguiente no establece la propiedad ProtectionLevel o ProtectionLevel en el contrato.
[ServiceContract]
public interface ISampleService
{
[OperationContractAttribute]
public string GetString();
[OperationContractAttribute]
public int GetInt();
}
A continuación, se muestra el código equivalente en Visual Basic.
[Visual Basic]
<ServiceContractAttribute()> _
Public Interface ISampleService
<OperationContractAttribute()> _
Public Function GetString()As String
<OperationContractAttribute()> _
Public Function GetData() As Integer
End Interface
Al interactuar con una implementación ISampleService
en un extremo con un WSHttpBinding predeterminado (el System.ServiceModel.SecurityModepredeterminado, que es Message), todos los mensajes se cifran y firman dado que es el nivel de protección predeterminado. No obstante, cuando se usa un servicio ISampleService
con un BasicHttpBinding predeterminado (el SecurityMode predeterminado, que es None), todos los mensajes se envían como texto, ya que no existe seguridad para este enlace y, por lo tanto, se pasa por alto el nivel de protección (es decir, los mensajes ni se cifran ni se firman). Si se cambia el SecurityMode a Message, estos mensajes se cifrarían y firmarían (dado que ése sería el nivel de protección predeterminado del enlace).
Si desea especificar explícitamente o ajustar los requisitos de protección para su contrato, establezca la propiedad ProtectionLevel (o cualquier de las propiedades ProtectionLevel en un ámbito menor) en el nivel que requiera su contrato de servicios. En este caso, utilizando un valor explícito exige al enlace que soporte ese valor como mínimo para el ámbito utilizado. Por ejemplo, el ejemplo de código siguiente especifica explícitamente un valor ProtectionLevel, para la operación GetGuid
.
[C#]
[ServiceContract]
public interface IExplicitProtectionLevelSampleService
{
[OperationContractAttribute]
public string GetString();
[OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]
public int GetInt();
[OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
public int GetGuid();
}
A continuación, se muestra el código equivalente en Visual Basic.
[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
Un servicio que implementa este contrato IExplicitProtectionLevelSampleService
y tiene un extremo que utiliza el WSHttpBinding predeterminado (el System.ServiceModel.SecurityModepredeterminado, que es Message) tiene el comportamiento siguiente:
Los mensajes de operación
GetString
se cifran y firman.Los mensajes de operación
GetInt
se envían como texto sin cifrar ni firmar (es decir, texto sin formato).La operación
GetGuid
System.Guid se devuelve en un mensaje que se cifra y se firma.
Para obtener más información sobre los niveles de protección y cómo utilizarlos, vea Descripción de los niveles de protección. Para obtener más información sobre la seguridad, vea Seguridad de servicios.
Otros requisitos de firma de operación
Algunas características de aplicación requieren un tipo determinado de firma de la operación. Por ejemplo, el enlace NetMsmqBinding soporta los servicios duraderos y clientes, en los que una aplicación se puede reiniciar en el medio de la comunicación y se puede retomar donde se dejó sin olvidarse ningún mensaje. (Para obtener más información, vea Colas en Windows Communication Foundation.) Sin embargo, las operaciones duraderas deben tomar solo uno parámetro in y no tener ningún valor devuelto.
Otro ejemplo es el uso de los tipos Stream en operaciones. Puesto que el parámetro Stream incluye el cuerpo completo del mensaje, si una entrada o una salida (es decir, parámetro ref, parámetro out o valor devuelto) es del tipo Stream, debe ser la única entrada o salida especificada en su operación. Además, el parámetro o tipo devuelto debe ser Stream, System.ServiceModel.Channels.Message o System.Xml.Serialization.IXmlSerializable. Para obtener más información sobre las secuencias, vea Datos de gran tamaño y secuencias.
Nombres, espacios de nombres y ofuscación
Los nombres y espacios de nombres de los tipos de .NET en la definición de contratos y operaciones son significativos cuando los contratos se convierten en WSDL y cuando los mensajes de contrato se crean y envían. Por lo tanto, es muy recomendable que los nombres y espacios de nombres de los contratos de servicio se establezcan explícitamente mediante las propiedades Namespace y Name de todos los atributos de contrato auxiliares, como ServiceContractAttribute, OperationContractAttribute, DataContractAttribute, DataMemberAttribute y otros atributos de contrato.
Una de las consecuencias es que, si no se establecen explícitamente los nombres y espacios de nombres, el uso de la ofuscación de IL en el ensamblado modifica los nombres y espacios de nombres del tipo de contrato y, por lo tanto, los intercambios de conexión y WSDL modificados generan errores. Si no establece los nombres y espacios de nombres de contrato explícitamente pero tiene pensado utilizar la ofuscación, use los atributos ObfuscateAssemblyAttribute y ObfuscationAttribute para evitar la modificación de los nombres y espacios de nombres del tipo de contrato.
Vea también
Tareas
Creación de un contrato de solicitud‑respuesta
Cómo crear un contrato unidireccional
Creación de un contrato dúplex
Conceptos
Especificación de transferencia de datos en contratos de servicio
Especificación y administración de errores en contratos y servicios
Uso de sesiones
Operaciones sincrónicas y asincrónicas
Servicios de confianza
Servicios y transacciones