Servicios Web con WCF


Una sencilla introducción a los Servicios Web con WCF

Compartir

Willy Mejía
Junio 2008

Microsoft .NET Framework 3.0 (aka NetFx3, antes WinFX) introdujo a WCF (Windows Communication Foundation) como una ventanilla única para la creación de todo tipo de servicios distribuidos sobre la plataforma .NET, que incluye pero no se limita a los Servicios Web.

El siguiente cuadro muestra algunas clases importantes para WCF.

Elemento Clase o Interfaz
Address System.Uri
Binding System.ServiceModel.Binding
Contract System.ServiceModel Attributes
Endpoint System.ServiceModel.ServiceEndpoint

El presente artículo muestra cómo utilizar las clases y herramientas de WCF para crear y publicar un sencillo Servicio Web así como su cliente. Para lo cual se requiere tener instalado NetFx3 —o bien .NET Framework 3.5— con su correspondiente SDK.

Nota: Para una breve introducción a los Servicios Web puede leer: Guía Breve de Servicios Web.

Creando un Servicio Web muy simple

Para ilustrar la creación de Servicio con WCF emplearemos el clásico ejemplo de Hola Mundo, el cual luce así:

Listado de Hello.cs

using System;
using System.Text;
using System.ServiceModel;

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

    [OperationContract]
    public string SayHello(string name)
    {
      if (name == null || name.Trim().Length == 0)
        throw new FaultException("El parámetro 'name' es nulo o vacío.");
      string msg = message.Append(name + "!").ToString();
      System.Console.WriteLine(msg);
      return msg;
    }
  }
}

Nota: La parte del código en color gris puede omitirse para simplicidad del ejemplo, no obstante sirve de introducción al tema de excepciones en servicios web.

Nótese que luce como una clase normal pero con algunas adiciones: En primer lugar declaramos la utilización del namespace System.ServiceModel dentro del cual se encuentran las clases atributo con las cuales marcamos la clase Hello como el contrato del servicio (atributo ServiceContract) y al método SayHellocomo una operación del mismo (atributo OperationContract). Con esto habilitamos la clase para que pueda comportarse como un Servicio WCF el cual posteriormente publicaremos como un Servicio Web. Finalmente el atributo ServiceBehavior especifica el comportamiento de ejecución de una implementación del contrato de servicio, es éste caso sólo lo empleamos para especificar lo que será el «targetNamespace» y «name» del servicio para la exportación de la definición del mismo a través de WSDL.

Por último note la excepción de tipo FaultException utilizada para enviar un mensaje de error (un mensaje «SOAP Fault») hacia el cliente cuando el mismo envíe el parámetro name nulo o vacío.

Solo resta compilar la clase, asegurándonos de referenciar al ensamblado System.ServiceModel.dll:

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

Con lo anterior ya tenemos un sencillo Servicio WCF casi listo para ser alojado y desplegado como un Servicio Web dentro de un Sitio Web ASP.NET, no obstante podemos probarlo sin necesidad de recurrir a IIS (o Cassini) si creamos un Endpoint dentro de una aplicación de consola que actuará como host del servicio, tal como se ilustra en el siguiente código:

Nota: Para revisar otras opciones de «alojamiento» de servicios WCF consultar: Alojando Servicios.

Listado de HelloHost.cs

using System;
using System.ServiceModel;

class HelloHost
{
  static void Main(string[] args)
  {
    string endpointAddress = "http://localhost:8080/hello";
    ServiceHost myServiceHost = 
      new ServiceHost(typeof(hello.Hello), new Uri(endpointAddress));
    myServiceHost.Open();
    System.Console.WriteLine("El Servicio Web Hello se está ejecutando...");
    System.Console.WriteLine("WSDL: " + endpointAddress + "?wsdl");
    System.Console.WriteLine("Presione una tecla para cerrarlo.");
    System.Console.ReadKey();
    if (myServiceHost.State != CommunicationState.Closed)
      myServiceHost.Close();
  }
}

Nótese que se trata de una simple aplicación de consola que utiliza una instancia de la clase ServiceHost para crear un endpoint para un servicio de tipo HelloService.Hello en la dirección (Address) http://localhost:8080/Hello. Esto último gracias a que la clase ServiceHost utiliza la información proporcionada tanto en el código como en el archivo de configuración —que veremos a continuación— para crear y publicar un ServiceEndpoint.

Por lo que el siguiente paso es crear el archivo de configuración (que se vinculará al ejecutable de HelloHost) con el siguiente contenido:

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.Hello" binding="basicHttpBinding" 
                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>

Observe que en éste caso la clase hello.Hello sirve tanto para definir la clase de implementación del servicio como su contrato, es por ello que aparece tanto en el atributo name del elemento service, como en el atributo contract del elemento endpoint. En un artículo posterior veremos que esto se puede realizar de forma separada utilizando una interfaz como contrato.

Lo siguiente a destacar es la línea:

<endpoint contract="hello.Hello" binding="basicHttpBinding" ... />

En la cual se especifica tanto la clase/interfaz que define al servicio (Contract), así como la forma de enlace (Binding) con el endpoint, cuya ubicación en la red (Address) se definió en el código de HelloHost. Estos tres elementos: Address, Binding & Contract, constituyen el ABC de los Endpoints de WCF. Para más información al respecto consultar: Endpoints: Address, Bindings and Contracts.

El enlace de tipo basicHttpBinding representa la forma de enlace que el servicio WCF utiliza para configurar y exponer su correspondiente endpoint de modo que el servicio pueda comunicarse como un Servicio Web (basado en ASMX) que cumple con el WS-I Basic Profile 1.1.

Los enlaces o bindings se utilizan para especificar el transporte, la codificación de mensajes y los detalles de protocolo requeridos para los clientes y servicios para comunicarse entre sí. WCF proporciona una variedad de Bindings, de entre los cuales cuatro admiten transporte HTTP por lo que se utilizan para Servicios Web: basicHttpBinding, wsHttpBinding, wsDualHttpBinding y wsFederationHttpBinding. Los tres últimos admiten además diferentes protocolos WS-* para Servicios Web seguros e interoperables.

Binding Interop Seguridad Duplex WS-AT /
WS-RM
basicHttpBinding Basic Ninguna,
HTTPS
n/a No
wsHttpBinding WS WS-Security,
HTTPS
n/a
wsDualHttpBinding WS WS-Security,
Ninguna
wsFederationHttpBinding Federation WS-Security,
Ninguna
No

WS-AT = WS-AtomicTransaction, WS-RM = WS-ReliableMessaging

Para más información consultar: Bindings proporcionados por el sistema, donde además se listan otros Bindings para .NET Remoting y MSMQ.

Los atributos del endpoint bindingNamespace y name sirven para especificar el «targetNamespace» y «name» del enlace para la definición a través de WSDL.

Nota: El resto de la configuración (de color oliva) sirve para habilitar la exposición de los Metadatos del servicio, de modo que se pueda tener acceso al WSDL generado bajo demanda. No es necesaria para poder consumir el servicio, por lo que si se facilita el WSDL por otra vía bien podría omitirse dicha configuración extra.

Una vez elaborado el archivo de configuración solo resta compilar y ejecutar HelloHost, el cual mostrará unas líneas indicando que el servicio se está ejecutando, así como la ubicación del documento WSDL del mismo para cualquier cliente que desee consumirlo.

>csc /t:exe HelloHost.cs /r:System.ServiceModel.dll,Hello.dll
>HelloHost.exe

El Servicio Web Hello se esta ejecutando...
WSDL: http://localhost:8080/hello?wsdl
Presione una tecla para cerrarlo.

Nota: Se requiere de una cuenta con privilegios de Administrador para ejecutar el host, o bien para modificar los derechos de la reserva para una parte del espacio de nombres HTTP. Para más información consulte: Configuración de los elementos de enlace de transporte HTTP y HTTPS para WCF.

Lo siguiente a realizar, es crear un cliente que lo consuma…

Creando un Cliente muy simple para un Servicio Web muy simple

Ahora crearemos —en un directorio/proyecto distinto— el cliente para nuestro Servicio Web el cual debe estar publicado y en ejecución.

Pero primero necesitamos generar los artefactos cliente. Para ello utilizaremos la herramienta svcutil (Herramienta de utilidad de metadatos de ServiceModel) incluida en el Windows SDK for .NET Framework 3.x, a la cual le pasamos como argumento la ubicación del documento WSDL:

>svcutil http://localhost:8080/hello?wsdl /out:Hello.cs /config:Client.exe.config /n:http://hello/,hello

Esto generará un archivo con la clases proxy las cuales encapsularán la invocación de las operaciones del Servicios Web. En nuestro caso se generará el archivo Hello.cs (con la clase HelloClient que utilizaremos directamente en la aplicación cliente) así como el archivo de configuración Client.exe.config, de modo que coincida con el nombre del ejecutable de la aplicación cliente.

Una vez generados los artefactos cliente (proxy) solo resta crear la aplicación cliente en sí, nuevamente una simple aplicación de consola:

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 {
      // Comentar para obtener el FaultException...
      arg = "Anónimo";
    }
    try {
      hello.HelloClient 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);
    }
  }
}

Nótese que para invocar la operación de nuestro Servicio Web primero creamos una instancia de la clase HelloClient (generada por svcutil) por medio de la cual después invocamos al método SayHello —y tras del cual se invoca la operación del Servicio Web, el cual a su vez invoca el método correspondiente de la clase Hello implementada al inicio del artículo…

Solo resta compilar tanto el proxy como la aplicación cliente:

>csc /t:library Hello.cs

>csc /t:exe Client.cs /r:Hello.dll

Después ejecute la aplicación cliente con algún argumento, su nombre por ejemplo, si no se especifica argumento alguno la salida será «Hola Anónimo»:

>Client Willy

Hola Willy!

La misma salida se desplegará en la consola del servicio.

Creando un Cliente Java muy simple…

Como ejercicio final crearemos un cliente Java (utilizando JAX-WS) para el Servicio Web, el cual es prácticamente el mismo cliente del artículo Servicios Web con Java SE 6.0.

Primero necesitamos generar los artefactos cliente. Para ello utilizaremos la herramienta wsimport incluida en Java SE 6.0, a la cual le pasamos como argumento la ubicación del documento WSDL:

>wsimport http://localhost:8080/hello?wsdl

Una vez generados los artefactos cliente solo resta crear el cliente en sí, nuevamente una simple aplicación de consola:

Listado de Client.java

public class Client {
  public static void main(String[] args) {
    String arg = null;
    String result = null;
    if (args.length > 0) {
      arg = args[0];
    }
    else {
      arg = "Anonimo";
    }
    try {
      hello.HelloService service = new hello.HelloService();
      hello.Hello port = service.getHelloPort();
      result = port.sayHello(arg);
    }
    catch (Exception ex) {
      result = ex.toString();
    }
    finally{
      System.out.println(result);
    }
  }
}

Tal como puede apreciarse, la razón para nombrar al servicio como HelloService y su endpoint como HelloPort fue para que coincidiera con el Servicio Web elaborado en Java en el artículo anteriorermte citado y así se pudiese reutilizar el código del cliente. No obstante de no haberlo hecho el código del cliente cambiaría en algo como:

hello.Hello_Service service = new hello.Hello_Service();
hello.Hello port = service.getBasicHttpBindingHello();

Finalmente compilamos y ejecutamos nuestro cliente con algún argumento:

>javac Client.java
>java Client Java

Hola Java!

La misma salida se desplegará en la consola del servicio.

Epílogo

Con lo que hemos realizado vemos lo fácil que es crear y publicar un Servicio Web así como su cliente con WCF.

No obstante todo esto tan sólo fue el inicio, la finalidad fue dar una introducción al desarrollo de Servicios Web utilizando WCF esperando le motive a continuar para después «emplearse en la Fábrica». Quedan entonces muchas cosas por tratar, como crear un servicio y cliente a partir de un WSDL, el alojamiento y despliegue en IIS, utilizar contratos tanto de servicios, como de mensajes y de datos, Interoperabilidad y WS-*, etc…

Espero que sirva de algo y hasta la próxima,
Willy Mejía.

P.D.
Como continuación del presente artículo he escrito >> WCF bajo Contrato: Una práctica introducción a los Contratos de WCF.

17 respuestas a Servicios Web con WCF

  1. Willy Mejía dijo:

    Las sugerencias para mejorar y/o ampliar el presente artículo, o para proponer algún otro relacionado con el tema, son bienvenidas…

  2. Rod dijo:

    Hola, muy interesante tu post, quisiera saber como haria para tener el servicio en una maquina virtuak( servidor) y en el cliente en mi pc, pero claro con un binding nettcpbinding
    Gracias

    • Willy Mejía dijo:

      ¿Que problema específicamente tienes…?

      En teoría no tendría que haber mayor diferencia desarrollarlo en local que remotamente (o bien una máquina virtual), debido a que de todas formas cada aplicación se ejecutará en si propio dominio. Ahora que en ésto de los servicios siempre hay problemas a la hora de implementarlos, y más cuando hay de por medio que abrir puertos y conceder permisos, lo cual llega a ser el caso con «Remoting» y similares como nettcpbinding…

  3. R0d0lF0x dijo:

    hola, bueno la verdad es q nunk he colgado un servicio… tal sea x eso q no logra conectar :s… q tendría que hacer para lograr conectar? porque entre ambas maquinas(virtual y la pc) hay copnectividad ya q les puedo hacer ping

    … gracias

  4. R0d0lF0x dijo:

    Bueno, en forma local funcionan sin ningun problema… o sea lo q no se es como hacer q la pc cliente consuma el servicio desde el servidor, porque yo solo lo cuelgo en el IIS del servidor, pero no se que mas hacer

    • Willy Mejía dijo:

      ¿netTcpBinding con IIS? Pues e ahí el problema, nettcp no es por vía web (HTTP).
      Si deseas desplegar el servicio usando el IIS, debes cambiar el binding por basicHttpBinding, o bien wsHttpBinding, para consumirlo vía HTTP.
      O bien, crea tu host para usarlo vía TCP, pero entonces prepárate para lidear con los puertos y permisos…

      Saludos.

  5. R0d0lF0x dijo:

    uhmm… bueno tienes razon, pero el escenario en el q pienso hacer algo parecido es en una LAN, y pues cree una consola que hostea el servicio pero igualmente no puedo consumirlo desde la pc cliente

    PD: este servicio tb funciona correctamente en la misma pc(consola host y la app cliente en la misma pc)

  6. R0d0lF0x dijo:

    Lo probe, creo q el problema es con la direccion, he aqui una nueva pregunta… este es mi endpoint de la app que ofrece el servicio:

    y el endpoint de la app cliente es:

    bueno esos son los endpoint’s con los que funciona correctamente en la misma pc, pero cuando lo paso a la maquina virtual (la app de servicio), en la app cliente no deberia de cambiar la direccion??… me refiero a que tal vez en algun lugar poner la direccion IP …

    gracias

    • Willy Mejía dijo:

      No pasaron las «direcciones» de tus endpoints, pero creo que sé a que te refieres… Efectivamente, el cliente debe apuntar a la dirección del host de despliegue. Por ello es mejor usar los archivos de configuración para esos menesteres, de esa forma puedes cambiar la dirección sin tener que volver a compilar (aunque para ello no debes especificarlo via «hard-code»…)

      Así pues, soponiendo que no tengas problemas con los puertos, solo tendrías que actualizar la dirección en el .config cliente:

      <endpoint name="" 
        
        address="net.tcp://[IP del Servidor]:[Puerto]/sample/service"
        binding="netTcpBinding"
        contract="Samples.IService">

      O en su defecto, volver a generar el proxy con la nueva dirección del servicio y re-compilar el cliente.

      Saludos.

      • Ernesto dijo:

        Hola Willy, tengo una publicación de un servicio WCF con un comportamiento extraño me dicen que es por la metadata que genera el proxy, el hecho es que publico una operacion que contiene un objeto Request y un objeto Response, pero a la hora de consumir el servicio me salen un conjunto de parametros de entrada que no he definido en ningun lado, alguna idea?, gracias!

      • Willy Mejia dijo:

        Pues sin mayores datos solo puedo especular lo mismo: que es por la metadata que genera el proxy, misma debida al modo como configuraste (consciente o inconscientemente) el servicio (comportamientos, canales, etc.). Así por ejemplo si especificaste seguridad por credenciales te pedirá las mismas, etc.

        De buenas a primeras trata de consumir el servicio sin hacer caso a los parámetros extras, si lo puedes consumir entonces déjalo así mientras investigas como «limpiarlo». De lo contrario envía tu consulta junto con los datos técnicos del servicio (incluyendo una liga al WSDL para examinarlo) a http://social.msdn.microsoft.com/Forums/es-ES/wcfes/threads donde podremos ayudarte mejor.

        Saludos.

  7. Pingback: Amador Zamora Núñez » Web Service WCF .NET

  8. ASS dijo:

    Hola Willy, soy nuevo en este mundo de los servicios web y estoy tratando de consumir unos servicios de terceros, tengo un componente de servidor que es el que los consume y que es llamado a través de remoting por otro componente de aplicación. Necesito poder cargar los endpoint desde un fichero de configuración que no sea el de inicio de la aplicación. Si no lo hago desde ese fichero de configuración de inicio de la aplicación, recibo el siguiente error:

    No se encontró el elemento de extremo con el nombre ‘XXXX’ y el contrato ‘YYYY’ en la sección de configuración de cliente de ServiceModel. La razón puede ser que no se encontró ningún archivo de configuración para la aplicación o que no se encontró ningún elemento de extremo con este nombre en el elemento de cliente.

    Un saludo

Deja un comentario