WCF bajo contrato (1/4)
Como continuación al artículo Servicios Web con WCF: Una sencilla introducción a Servicios Web con WCF, la presente serie abordará de manera sencilla los principales tipos de contratos que se emplean para definir los Servicios de WCF. La finalidad es dar una introducción práctica a los mismos, sin pretender ser exhaustivo. Así que partiendo del servicio Hello del artículo anterior, comencemos por el Contrato de Servicio.
Un contrato de servicio permite la comunicación con el mismo, al proporcionar la siguiente información:
- Listado de las operaciones que ofrece y la ubicación de las mismas
- Firma de las operaciones, en términos de intercambio de mensajes
- Tipos de datos empleados en los mensajes.
- Protocolos concretos y formatos de serialización.
Para esto se utilizan los documentos WSDL y XSD, que son lenguajes estándar para describir servicios web. No obstante WSDL y XSD son difíciles de utilizar, por ello las aplicaciones WCF utilizan atributos, interfaces y clases para definir la estructura de un servicio e implementar un servicio.
En el artículo anterior se define el contrato del servicio utilizando la clase de implementación del mismo. Sin embargo es recomendable utilizar una interfaz para ello, ya que, al igual que un contrato de servicios, las interfaces solo definen la agrupación de métodos (operaciones) con ciertas firmas sin especificar ningún detalle sobre su implementación. Además, todas las ventajas de las interfaces administradas se aplican a las interfaces de contrato de servicio:
- Las interfaces pueden extender cualquier número de interfaces.
- Una única clase puede implementar cualquier número de interfaces.
- Puede modificar la implementación de una interfaz, mientras la interfaz siga siendo la misma.
Por lo que adicionalmente se puede controlar la versión del 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.
Así que, tras realizar refactoring al Servicio WCF Hello, el contrato y su implementación ahora quedan de la siguiente forma:
Listado de Hello.cs
using System;
using System.Text;
using System.ServiceModel;
namespace hello
{
[ServiceContract(Namespace="http://hello/")]
interface IHello
{
[OperationContract()]
string SayHello(string name);
}
[ServiceBehavior(Namespace="http://hello/", Name="HelloService")]
public class Hello : IHello
{
private StringBuilder message = new StringBuilder("Hola ");
public string SayHello(string name)
{
if (name == null || name.Length == 0)
throw new FaultException("Parámetro 'name' nulo o vacío.");
string msg = message.Append(name + "!").ToString();
System.Console.WriteLine(msg);
return msg;
}
}
}
Observe que ahora los atributos ServiceContract y OperationContract se aplican a la interfaz, y que a la clase de implementación sólo se la aplica el atributo ServiceBehavior. Recuerde que los dos primeros atributos definen el contrato del servicio y sus operaciones, mientras que el tercero define comportamientos concretos de la implementación. En el ejemplo los utilizamos para marcar las responsabilidades de cada una, así como el namespace a utilizar en la exportación hacia el WSDL.
Tras compilar el servicio:
>csc /t:library Hello.cs /r:System.ServiceModel.dll
Continuamos con el host. El código de HelloHost.cs permanece sin cambios, pero tenemos que hacer un ligero cambio al archivo de configuración HelloHost.exe.config, de modo ahora el atributo contract del endpoint señale a la interfaz:
Listado de HelloHost.exe.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="hello.Hello" behaviorConfiguration="enableMetadata">
<endpoint contract="hello.IHello" binding="basicHttpBinding" address=""
bindingNamespace="http://hello/" name="HelloPort" />
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="enableMetadata" >
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Tras ejecutar el host:
>HelloHost.exe
El Servicio Web Hello se esta ejecutando...
WSDL: http://localhost:8080/Hello?wsdl
Presione una tecla para cerrarlo.
Ahora vamos con el cliente. Primero debemos regenerar los artefactos cliente con svcutil:
>svcutil http://localhost:8080/hello?wsdl /out:Hello.cs /config:Client.exe.config /n:http://hello/,hello
Después modificamos una línea del código de la aplicación cliente:
Listado de Client.cs
using System;
public class Client
{
static void Main(string[] args)
{
String arg = null;
String result = null;
if (args.Length > 0) {
arg = args[0];
}
else {
arg = "Anónimo";
}
try {
hello.IHello service = new hello.HelloClient();
result = service.SayHello(arg);
}
catch (System.ServiceModel.FaultException fault)
{
result = fault.Message;
}
catch (Exception ex) {
result = ex.ToString();
}
finally{
System.Console.WriteLine(result);
}
}
}
Compilamos tanto el proxy como la aplicación:
>csc /t:library Hello.cs /r:System.ServiceModel.dll
>csc /t:exe Client.cs /r:System.ServiceModel.dll,Hello.dll
Y la ejecución será la misma que la última vez:
>Client Willy
Hola Willy.