Задание адреса конечной точки

Все взаимодействие со службой Windows Communication Foundation (WCF) осуществляется через конечные точки. Каждая конечная точка службы ServiceEndpoint содержит адрес Address, привязку Binding и контракт Contract. В контракте задается, какие операции доступны. Привязка определяет, как осуществлять взаимодействие со службой, а адрес показывает, где можно найти службу. Каждая конечная точка должна иметь уникальный адрес. Адрес конечной точки представляется классом EndpointAddress, содержащим универсальный код ресурса (URI), который, в свою очередь, обозначает адрес службы, Identity, представляющий удостоверение безопасности службы и коллекцию необязательных заголовков Headers. Необязательные заголовки содержат более подробную информацию для идентификации конечной точки и взаимодействия с ней. Например, в заголовках может содержаться информация о том, как следует обрабатывать входящее сообщение, куда конечная точка должна отправить ответное сообщение или какой экземпляр службы необходимо использовать для обработки входящего сообщения от конкретного пользователя, если доступно несколько экземпляров.

Определение адреса конечной точки

В WCF модель EndpointAddress ссылки на конечную точку (EPR), как определено в стандарте WS-Адресации.

Универсальный код ресурса (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, следует использовать относительные адреса. Указание полного адреса конечной точки может привести к ошибкам при развертывании службы. Дополнительные сведения см. в статье "Развертывание службы WCF с службы IIS размещением".

Определение адреса конечной точки в конфигурации

Чтобы определить конечную точку в файле конфигурации, используйте элемент конечной <точки> .

<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 При вызове метода (то есть при попытке запуска службы размещенное приложение) система ищет <элемент службы> с атрибутом имени, указывающим "UE". Samples.HelloService. <Если элемент службы> найден, система загружает указанный класс и создает конечные точки с помощью определений конечных точек, указанных в файле конфигурации. Благодаря такому механизму можно загружать и запускать службу с двумя строками кода, при этом не указывая привязку и адрес в коде. Преимуществом этого подхода является отсутствие необходимости в повторной компиляции или повторном развертывании приложения при внесении этих изменений.

Необязательные заголовки объявляются в заголовках>.< Ниже приведен пример элементов, используемых для указания конечных точек для службы в файле конфигурации, который отличается от двух заголовков: "Gold" от клиентов http://tempuri1.org/http://tempuri2.org/"Стандартный". Клиент, вызывающий эту службу, должен иметь соответствующие <заголовки> в файле конфигурации.

<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

Адрес конечной точки в метаданных

Адрес конечной точки представляется на языке описания веб-служб (WSDL) в виде элемента ссылки на конечную точку EPR WS-Addressing EndpointReference в элементе wsdl:port соответствующей конечной точки. Ссылка на конечную точку (EPR) содержит адрес конечной точки и любые свойства адреса. Обратите внимание, что EPR в элементе wsdl:port заменяет 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 и методы AddServiceEndpoint классов ServiceHostBase и ServiceHost, создают исключение, если их изменить после вызова этого метода. Другие члены можно изменять без появления исключения, но результат при этом будет неопределенным.

Аналогично, на стороне клиента нельзя изменять значения ServiceEndpoint после вызова метода OnOpening для объекта ChannelFactory. Если после вызова этого метода изменить свойство Credentials, создается исключение. Изменение других значений описания клиента происходит без ошибок, но результат изменения в этом случае является неопределенным.

Как для службы, так и для клиента, рекомендуется изменять описание до вызова метода Open.

Использование конечных точек по умолчанию

Если конечные точки не заданы в коде или в конфигурации, то среда выполнения предоставляет конечные точки по умолчанию, добавляя одну конечную точку по умолчанию для каждого базового адреса в каждом контракте службы, реализованном в службе. Базовый адрес можно указывать в коде или в конфигурации, а конечные точки по умолчанию добавляются, когда вызывается метод Open в объекте ServiceHost.

Если конечные точки предоставляются явно, то конечные точки по умолчанию можно добавить, вызвав метод AddDefaultEndpoints класса ServiceHost перед вызовом Open. Дополнительные сведения о конечных точках по умолчанию, привязках и режимах работы см. в разделах Упрощенная конфигурация и Упрощенная конфигурация служб WCF.

См. также