Especificación de una dirección de punto de conexión

Toda comunicación con un servicio de Windows Communication Foundation (WCF) se produce a través de sus puntos de conexión. Cada ServiceEndpoint contiene un Address, un Bindingy un Contract. El contrato especifica qué operaciones están disponibles. El enlace especifica cómo comunicarse con el servicio y la dirección especifica dónde encontrar el servicio. Cada punto de conexión debe tener una dirección única. La clase EndpointAddress representa la dirección de extremo, que contiene un Identificador uniforme de recursos (URI) que representa la dirección del servicio, una Identity, que representa la identidad de seguridad del servicio, y una colección de Headersopcional. Los encabezados opcionales proporcionan información más detallada de direccionamiento para identificar o interactuar con el punto de conexión. Por ejemplo, los encabezados pueden indicar cómo procesar un mensaje entrante, dónde debería enviar el punto de conexión un mensaje de respuesta o qué instancia de un servicio se va a usar para procesar un mensaje entrante de un usuario determinado cuando hay varias instancias disponibles.

Definición de una dirección del punto de conexión

En WCF, el elemento EndpointAddress modela una referencia de punto de conexión (EPR) como se define en el estándar WS-Addressing.

El URI de la dirección de la mayoría de transportes tiene cuatro partes. Por ejemplo, el URI http://www.fabrikam.com:322/mathservice.svc/secureEndpoint tiene las cuatro partes siguientes:

  • Esquema: http:

  • Máquina: www.fabrikam.com

  • (Opcional) Puerto: 322

  • Ruta de acceso: /mathservice.svc/secureEndpoint

Parte del modelo EPR consiste en que cada referencia de extremo puede llevar algunos parámetros de referencia que agregan información de identificación adicional. En WCF, estos parámetros de referencia se modelan como instancias de la clase AddressHeader.

La dirección del punto de conexión de un servicio puede especificarse de manera imperativa mediante código, o de manera declarativa mediante configuración. Normalmente, no resulta muy práctico definir los puntos de conexión en el código ya que los enlaces y las direcciones de un servicio implementado son, por lo general, diferentes de los utilizados durante el desarrollo del servicio. Generalmente, es más práctico definir extremos de servicio mediante la configuración en lugar del código. Mantener la información del enlace y el direccionamiento fuera del código les permite cambiar sin tener que recompilar e implementar la aplicación. Si no se especifica ningún extremo en el código o en la configuración, el tiempo de ejecución agrega un extremo predeterminado en cada dirección base de cada contrato de servicio implementado por el servicio.

Hay dos maneras de especificar las direcciones del punto de conexión de un servicio en WCF. Puede especificar una dirección absoluta para cada punto de conexión asociado al servicio o puede proporcionar una dirección base para ServiceHost de un servicio y, a continuación, especificar una dirección para cada punto de conexión asociado a este servicio que se define relativo a esta dirección base. Puede utilizar cada uno de estos procedimientos para especificar las direcciones de extremo de un servicio mediante configuración o código. Si no especifica una dirección relativa, el servicio utiliza la dirección base. También puede tener varias direcciones base para un servicio, pero en cada servicio se permite sólo una dirección base para cada transporte. Si tiene varios puntos de conexión, cada uno de los cuales está configurado con un enlace diferente, sus direcciones deben ser únicas. Los extremos que utilizan el mismo enlace pero contratos diferentes pueden utilizar la misma dirección.

Al hospedarse con IIS, no administra por sí mismo las instancias ServiceHost. La dirección base siempre es la dirección especificada en el archivo .svc para el servicio al hospedarse en IIS. De modo que siempre debe utilizar direcciones de punto de conexión relativas para los puntos de conexión de servicio hospedados en IIS. Proporcionar una dirección de punto de conexión completa puede conducir a errores en la implementación del servicio. Para obtener más información, consulte Implementación de un servicio WFC hospedado en Internet Information Services.

Definición de direcciones de puntos de conexión mediante configuración

Para definir un punto de conexión en un archivo de configuración, use el elemento <endpoint>.

<configuration>
  <system.serviceModel>
    <services>
      <service name="UE.Samples.HelloService"
               behaviorConfiguration="HelloServiceBehavior">
        <endpoint address="/Address1"
                  binding="basicHttpBinding" 
                  contract="UE.Samples.IHello"/>

        <endpoint address="mex"
                  binding="mexHttpBinding"
                  contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="HelloServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Cuando se llama al método Open (es decir, cuando la aplicación de hospedaje intenta iniciar el servicio), el sistema busca un elemento <service> con un atributo de nombre que especifique "UE.Samples.HelloService". Si encuentra el elemento <service>, el sistema carga la clase especificada y crea los puntos de conexión utilizando las definiciones de punto de conexión proporcionadas en el archivo de configuración. Este mecanismo le permite cargar e iniciar un servicio con dos líneas de código, mientras mantiene la información de enlace y dirección fuera de su código. La ventaja de este enfoque es que estas modificaciones se pueden realizar sin tener que recompilar o implementar la aplicación.

Los encabezados opcionales se declaran en un elemento <headers>. El siguiente es un ejemplo de los elementos utilizados para especificar los puntos de conexión de un servicio en un archivo de configuración que distingue entre dos encabezados: clientes "Gold" de http://tempuri1.org/ y clientes "Standard" de http://tempuri2.org/. El cliente que llama a este servicio debe tener el elemento <headers> correspondiente en su archivo de configuración.

<configuration>
  <system.serviceModel>
    <services>
      <service name="UE.Samples.HelloService"
               behaviorConfiguration="HelloServiceBehavior">
        <endpoint address="/Address1"
                  binding="basicHttpBinding" 
                  contract="UE.Samples.IHello">
          <headers>
            <Member xmlns="http://tempuri1.org/">Gold</Member>
          </headers>
        </endpoint>
        <endpoint address="/Address2"
          binding="basicHttpBinding" 
          contract="UE.Samples.IHello">
          <headers>
            <Member xmlns="http://tempuri2.org/">Silver</Member>
          </headers>
        </endpoint>

        <endpoint address="mex"
                  binding="mexHttpBinding"
                  contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="HelloServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Los encabezados también se pueden definir como en mensajes individuales en lugar de todos los mensajes en un extremo (como se ha mostrado previamente). Ello se consigue utilizando OperationContextScope para crear un nuevo contexto en una aplicación de cliente para agregar un encabezado personalizado al mensaje saliente, tal y como se muestra en el ejemplo siguiente.

SampleServiceClient wcfClient = new SampleServiceClient(new InstanceContext(this));
try
{
  using (OperationContextScope scope = new OperationContextScope(wcfClient.InnerChannel))
  {
    MessageHeader header
      = MessageHeader.CreateHeader(
      "Service-Bound-CustomHeader",
      "http://Microsoft.WCF.Documentation",
      "Custom Happy Value."
      );
    OperationContext.Current.OutgoingMessageHeaders.Add(header);

    // Making calls.
    Console.WriteLine("Enter the greeting to send: ");
    string greeting = Console.ReadLine();

    //Console.ReadLine();
    header = MessageHeader.CreateHeader(
        "Service-Bound-OneWayHeader",
        "http://Microsoft.WCF.Documentation",
        "Different Happy Value."
      );
    OperationContext.Current.OutgoingMessageHeaders.Add(header);

    // One-way
    wcfClient.Push(greeting);
    this.wait.WaitOne();

    // Done with service.
    wcfClient.Close();
    Console.WriteLine("Done!");
    Console.ReadLine();
  }
}
catch (TimeoutException timeProblem)
{
  Console.WriteLine("The service operation timed out. " + timeProblem.Message);
  Console.ReadLine();
  wcfClient.Abort();
}
catch (CommunicationException commProblem)
{
  Console.WriteLine("There was a communication problem. " + commProblem.Message);
  Console.ReadLine();
  wcfClient.Abort();
}
Dim wcfClient As New SampleServiceClient(New InstanceContext(Me))
Try
    Using scope As New OperationContextScope(wcfClient.InnerChannel)
        Dim header As MessageHeader = MessageHeader.CreateHeader("Service-Bound-CustomHeader", _
                            "http://Microsoft.WCF.Documentation", "Custom Happy Value.")
        OperationContext.Current.OutgoingMessageHeaders.Add(header)

        ' Making calls.
        Console.WriteLine("Enter the greeting to send: ")
        Dim greeting As String = Console.ReadLine()

        'Console.ReadLine();
        header = MessageHeader.CreateHeader("Service-Bound-OneWayHeader", _
                                            "http://Microsoft.WCF.Documentation", "Different Happy Value.")
        OperationContext.Current.OutgoingMessageHeaders.Add(header)

        ' One-way
        wcfClient.Push(greeting)
        Me.wait.WaitOne()

        ' Done with service. 
        wcfClient.Close()
        Console.WriteLine("Done!")
        Console.ReadLine()
    End Using
Catch timeProblem As TimeoutException
    Console.WriteLine("The service operation timed out. " & timeProblem.Message)
    Console.ReadLine()
    wcfClient.Abort()
Catch commProblem As CommunicationException
    Console.WriteLine("There was a communication problem. " & commProblem.Message)
    Console.ReadLine()
    wcfClient.Abort()
End Try

Dirección del punto de conexión en metadatos

Una dirección de extremo se representa en el Lenguaje de descripción de servicios Web (WSDL) como un elemento EndpointReference (EPR) de WS-Addressing dentro del elemento wsdl:port del extremo correspondiente. El EPR contiene la dirección del extremo, así como todas las propiedades de la dirección. Observe que la EPR dentro de wsdl:port reemplaza a soap:Address, como se observa en el siguiente ejemplo.

Definición de direcciones del punto de conexión mediante código

Una dirección de punto de conexión se puede crear mediante código con la clase EndpointAddress. El URI especificado para la dirección del extremo puede ser una ruta de acceso completa o una ruta de acceso relativa a la dirección base del servicio. En el siguiente ejemplo observaremos cómo crear una nueva instancia de la clase EndpointAddress y agregarla a la instancia ServiceHost que hospeda el servicio.

En el siguiente ejemplo se muestra cómo se especifica una dirección de punto de conexión completa mediante código.

Uri baseAddress = new Uri("http://localhost:8000/HelloService");
string address = "http://localhost:8000/HelloService/MyService";

using (ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddress))
{
    serviceHost.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(), address);
    serviceHost.Open();
    Console.WriteLine("Press <enter> to terminate service");
    Console.ReadLine();
    serviceHost.Close();
}

El ejemplo siguiente muestra cómo agregar una dirección relativa ("MyService") a la dirección base del host de servicio.

Uri baseAddress = new Uri("http://localhost:8000/HelloService");

using (ServiceHost serviceHost = new ServiceHost(typeof(HelloService), baseAddress))
{
    serviceHost.AddServiceEndpoint(typeof(IHello), new BasicHttpBinding(), "MyService");
    serviceHost.Open();
    Console.WriteLine("Press <enter> to terminate service");
    Console.ReadLine();
    serviceHost.Close();
}

Nota

Las propiedades de ServiceDescription en la aplicación de servicio no se deben modificar después del método OnOpening en ServiceHostBase. Algunos miembros, como la propiedad Credentials y los métodos AddServiceEndpoint en ServiceHostBase y ServiceHost, inician una excepción si se modifican después de ese punto. Otros permiten modificarlos, pero el resultado no está definido.

De igual forma, en el cliente no se deben modificar los valores ServiceEndpoint después de la llamada a OnOpening en ChannelFactory. La propiedad Credentials inicia una excepción si se modifica pasado ese punto. Los otros valores de descripción del cliente pueden modificarse sin el error, pero el resultado no está definido.

Tanto si es para el servicio como para el cliente, se recomienda modificar la descripción antes de llamar a Open.

Usar puntos de conexión predeterminados

Si no se especifica ningún extremo en el código ni en la configuración, el tiempo de ejecución proporciona extremos predeterminados, agregando uno en cada dirección base de cada contrato de servicio implementado por el servicio. La dirección base se puede especificar en el código o en la configuración, y los extremos predeterminados se agregan al llamar al método Open en el objeto ServiceHost.

Si se proporcionan puntos de conexión de forma explícita, es posible agregar puntos de conexión predeterminados llamando a AddDefaultEndpoints en el objeto ServiceHost antes de llamar a Open. Para obtener más información sobre los puntos de conexión, enlaces y comportamientos predeterminados, vea Configuración simplificada y Configuración simplificada de los servicios de WCF.

Consulte también