指定终结点地址

与 Windows Communication Foundation (WCF) 服务的所有通信都通过服务的终结点进行。 每个 ServiceEndpoint 都包含一个 Address、一个 Binding 和一个 Contract。 协定指定可用的操作。 绑定指定如何与服务进行通信,而地址指定查找服务的位置。 每个终结点都必须具有一个唯一的地址。 终结点的地址由 EndpointAddress 类表示,该类包含一个表示服务地址的统一资源定位符 (URI),一个表示服务的安全标识的 Identity 和一个可选的 Headers 集合。 可选标头提供用于标识终结点或与终结点交互的更多详细寻址信息。 例如,标头可指示如何处理传入消息,终结点应发送答复消息的位置,或在多个实例可用时应使用哪个服务实例处理来自特定用户的传入消息。

终结点地址的定义

在 WCF 中,EndpointAddress 按照 WS-Addressing 标准中的定义建立终结点引用 (EPR) 的模型。

大多数传输的地址 URI 包含四个部分。 例如,该 URI http://www.fabrikam.com:322/mathservice.svc/secureEndpoint 含以下四个部分:

  • 方案:http:

  • 计算机:www.fabrikam.com

  • (可选)端口:322

  • 路径:/mathservice.svc/secureEndpoint

作为 EPR 模型的一部分,每个终结点引用都可以包含一些添加额外标识信息的引用参数。 在 WCF 中,将这些引用参数建模为 AddressHeader 类的实例。

可以通过使用代码以强制方式或通过配置以声明方式指定服务的终结点地址。 在代码中定义终结点通常是不可行的,因为已部署服务的绑定和地址通常与在部署服务时所用的绑定和地址不同。 一般而言,使用配置定义服务终结点比使用代码更为可行。 通过将绑定和寻址信息放置在代码之外,可以在更改这些信息之后不必重新编译和重新部署应用程序。 如果在代码或配置中未指定任何终结点,则运行时在该服务实现的每个协定的每个基地址上添加一个默认终结点。

在 WCF 中,有两种指定服务终结点地址的方式。 可以为每个与服务关联的终结点指定一个绝对地址,也可以为服务的 ServiceHost 提供一个基址,然后再为每个与此服务关联的终结点指定一个地址(该地址是相对于此基址定义的)。 可以在配置或代码中使用这两种过程来为服务指定终结点地址。 如果不指定相对地址,则服务会使用基址。 也可以为一个服务指定多个基址,但是对于每个传输协议,每个服务只允许有一个基址。 如果有多个终结点,则会使用不同的绑定来配置每个终结点,它们的地址必须是唯一的。 使用相同绑定但使用不同协定的终结点可以使用相同的地址。

使用 IIS 承载时,您不用自己管理 ServiceHost 实例。 在 IIS 中承载时,基址始终为在服务的 .svc 文件中指定的地址。 因此,必须对 IIS 承载的服务终结点使用相对终结点地址。 提供完全限定的终结点地址会在服务的部署过程中导致错误。 有关详细信息,请参阅部署承载于 Internet Information Services 中的 WCF 服务

在配置中定义终结点地址

若要在配置文件中定义终结点,请使用 <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>

当调用 Open 方法时(即宿主应用程序尝试启动该服务时),系统查找 <service> 元素,该元素具有用于指定“UE.Samples.HelloService”的名称特性。 如果找到 <service> 元素,则系统加载指定的类,并使用配置文件中提供的终结点定义来创建终结点。 此机制允许您将绑定和寻址信息放置在代码之外,而用两行代码来加载和启动服务。 此方法的优点是在进行这些更改后不必重新编译或重新部署应用程序。

可选的标头在 <headers> 中声明。 下面是用于在配置文件中指定服务终结点的元素的示例,该配置文件分为两个标头:来自 http://tempuri1.org/ 的“黄金”客户端和来自 http://tempuri2.org/ 的“标准”客户端。 调用此服务的客户端必须在其配置文件中具有相应的 <headers>

<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>

也可以为个别消息而不是终结点上的所有消息(如前面所示)设置标头。 可以通过使用 OperationContextScope 在客户端应用程序中创建新的上下文以向传出消息添加自定义标头,完成此操作,如以下示例中所示。

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

元数据中的终结点地址

终结点地址在 Web 服务描述语言 (WSDL) 中表示为对应终结点的 EndpointReference 元素内的 WS-Addressing wsdl:port (EPR) 元素。 EPR 包含终结点的地址以及所有的地址属性。 请注意,wsdl:port 内的 EPR 可替换 soap:Address,如下面的示例所示。

在代码中定义终结点地址

在代码中可以使用 EndpointAddress 类创建终结点地址。 为终结点地址指定的 URI 可以是完全限定的路径,也可以是相对于服务的基址的路径。 下面的代码演示如何创建 EndpointAddress 类的实例,并将其添加到承载服务的 ServiceHost 实例中。

下面的示例演示如何在代码中指定完整的终结点地址。

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();
}

下面的示例演示将相对地址(“MyService”)添加到服务主机的基址中。

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();
}

注意

服务应用程序中 ServiceDescription 的属性不能在 OnOpening 上的 ServiceHostBase 方法之后进行修改。 如果在该点之后修改某些成员(如 Credentials 属性以及 AddServiceEndpointServiceHostBase 上的 ServiceHost 方法),则将引发异常。 允许修改其他成员,但结果不可确定。

同样,在调用 ServiceEndpoint 上的 OnOpening 之后不能在客户端上修改 ChannelFactory 值。 如果在该点之后修改 Credentials 属性,则该属性将引发异常。 可以对其他客户端说明值进行修改而不会出现错误,但结果不可确定。

无论是对于服务还是客户端,建议您在调用 Open 之前修改说明。

使用默认终结点

如果在代码或配置中未指定任何终结点,则运行时通过在该服务实现的每个服务协定的每个基地址上添加一个默认终结点,来提供默认终结点。 可以在代码或配置中指定基地址,默认终结点是在 Open 上调用 ServiceHost 时添加的。

如果显式提供了终结点,则仍可以添加默认终结点,方法是先在 AddDefaultEndpoints 上调用 ServiceHost,然后调用 Open。 有关默认终结点、绑定和行为的详细信息,请参阅简化配置WCF 服务的简化配置

另请参阅