WCF bajo Contrato (3/4)


Contrato de Mensajes

Hasta ahora se ha redefinido el contrato de servicio utilizando una interfaz, así como empleado tipos personalizados con contrato de datos definidos, lo cual es suficiente en la mayoría de los casos.

Pero cuando se desea tener mayor control sobre la estructura de los mensajes SOAP debemos crear un contrato de mensajes el cual permite utilizar un tipo para el parámetro y el valor devuelto que se serialicen en los mensajes SOAP de solicitud y respuesta que se necesiten.

A éste estilo de modelado de las operaciones se le conoce como estilo de mensajería, en contraposición al estilo de llamada de procedimiento remoto (RPC). Las firmas de las operaciones de estilo de mensajería tienen hasta un parámetro y un valor de retorno y ambos deben ser tipos de mensaje que se serializan directamente en una estructura del mensaje SOAP especificado.

Para definir el contrato de mensajes debemos aplicar el atributo MessageContract a las clases que vayan ser los tipos de mensaje. Así también debemos aplicar el atributo MessageBodyMember a sus miembros (que pueden ser campos, propiedades o eventos) que serán los elementos del cuerpo del mensaje SOAP, o bien el atributo MessageHeader para especificar el encabezado del mensaje.

Podriamos haber empleado el contrato de mensajes desde la versión del servicio que empleaba tipos simples, en cuyo caso podría ser algo como:

[MessageContract()]
public class SayHelloRequest
{
  [MessageBodyMember()]
  public string name
}

[MessageContract()]
public class SayHelloResponse
{
  [MessageBodyMember()]
  public string greeting
}

Pero continuando con nuestro ejemplo, los contratos de mensaje para la operación SayHello quedan de la siguiente forma:

Listado de Hello.cs (fragmento)

using hello.data;

  [MessageContract()]
  public class SayHelloRequest
  {
    private Person person;

    [MessageBodyMember(Name="Person")]
    public Person PersonParameter
    {
      get { return person; }
      set { person = value; }
    }
  }

  [MessageContract()]
  public class SayHelloResponse
  {
    private HelloCard helloCard;

    [MessageBodyMember(Name="HelloCard")]
    public HelloCard HelloCardResult
    {
      get { return helloCard; }
      set { helloCard = value; }
    }
  }

Nótese que se trata de clases “planas” con atributos, y que los elementos del cuerpo de los mensajes son los tipos personalizados con contratos de datos definidos anteriormente en HelloData.cs.

Para utilizar los tipos de mensaje ahora la firma del contrato de servicio debe redefinirse como sigue:

Listado de Hello.cs (fragmento)

[ServiceContract(Namespace="http://hello/", Name="Hello")]
interface IHello
{
  [OperationContract()]
  SayHelloResponse SayHello(SayHelloRequest request);
}

Por lo que también debemos reescribir su implementación. El listado completo de Hello.cs queda entonces de la siguiente forma:

Listado de Hello.cs

using System;
using System.Text;
using System.ServiceModel;
using hello.data;

namespace hello
{

  [MessageContract()]
  public class SayHelloRequest
  {
    private Person person;

    [MessageBodyMember(Name="Person")]
    public Person PersonParameter
    {
      get { return person; }
      set { person = value; }
    }
  }

  [MessageContract()]
  public class SayHelloResponse
  {
    private HelloCard helloCard;

    [MessageBodyMember(Name="HelloCard")]
    public HelloCard HelloCardResult
    {
      get { return helloCard; }
      set { helloCard = value; }
    }
  }
  
  [ServiceContract(Namespace="http://hello/", Name="Hello")]
  interface IHello
  {
    [OperationContract()]
    SayHelloResponse SayHello(SayHelloRequest request);
  }

  [ServiceBehavior(Namespace="http://hello/", Name="HelloService")]
  public class Hello : IHello
  {
    private StringBuilder message = new StringBuilder("¡Hola ");

    public SayHelloResponse SayHello(SayHelloRequest request)
    {
      if (request.PersonParameter == null ||
          request.PersonParameter.FirstName == null ||
          request.PersonParameter.FirstName.Length == 0)
        throw new FaultException(
            "Parámetro Person inválido.");

      message.Append(request.PersonParameter.FirstName + "!");
      string msg = message.ToString();
      SayHelloResponse response = new SayHelloResponse();
      HelloCard card = new HelloCard();
      card.Greeting = msg;
      response.HelloCardResult = card;
      System.Console.WriteLine(msg);      
      return response;
    }
  }
}

Las clases de los archivos HelloData.cs y HelloHost.cs permanecen sin cambios. Por lo que del lado del servicio únicamente compilamos el archivo Hello.cs:

>csc /t:library Hello.cs /r:System.ServiceModel.dll,HelloData.dll

…y ejecutamos la aplicación host.

Continuemos con el cliente. Primero debemos (una vez más) regenerar los artefactos cliente con svcutil.exe, y después modificar el código de Client.cs como se muestra a continuación:

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.Hello service = new hello.HelloClient();         
      hello.data.Person person = new hello.data.Person();
      person.FirstName = arg;
      hello.SayHelloRequest request = new hello.SayHelloRequest();
      request.Person = person;
      hello.SayHelloResponse response = service.SayHello(request);
      result = response.HelloCard.Greeting;
    }
    catch (System.ServiceModel.FaultException fault)
    {
      result = "Fault: " + fault.Message;
    }
    catch (Exception ex) {
      result = ex.ToString();
    }
    finally{
      System.Console.WriteLine(result);
    }
  }
}

Nótese que antes del llamado al método SayHello se deben crear una instancia del tipo de mensaje SayHelloRequest, así como el tipo Person para su miembro correspondiente. La invocación retorna una instancia del tipo de mensaje SayHelloResponse mediante la cual se recupera el mensaje a desplegar. Note también que los elementos de los mensajes tienen los nombres especificados en el argumento por nombre “Name” del atributo MessageBodyMember correspondiente.

Para finalizar compilamos el proxy y la aplicación cliente, y ejecutamos Client.exe como hasta ahora.

Dado que en el presente ejemplo no hemos empleado el contrato de mensajes para nada extraordinario, el uso del mismo pareciera una sobrecarga innecesaria. Pero tenga presente que es útil para aquellos casos en los cuales se requiere tener control sobre los elementos o cabecera del mensaje, tales como la resolución de problemas de seguridad del mensaje o problemas de interoperabilidad.

Acerca de Willy Mejia

Developer, Techie, Human... http://about.me/willyxoft
Esta entrada fue publicada en .NET, NetFx3, WCF. Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s