Как добавить настраиваемый HTTP-заголовок для каждого вызова WCF?

У меня есть служба WCF, размещенная в службе Windows. Клиенты, использующие эту службу, должны передавать идентификатор каждый раз, когда они вызывают методы службы (поскольку этот идентификатор важен для того, что должен делать вызываемый метод). Я подумал, что это хорошая идея, чтобы как-то поместить этот идентификатор в информацию заголовка WCF.

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

UPDATE: Клиенты, которые используют службу WCF, являются приложениями Windows и приложением Windows Mobile (с использованием Compact Framework).

Ответ 1

Преимущество этого заключается в том, что оно применяется к каждому вызову.

Создайте класс, который реализует IClientMessageInspector. В методе BeforeSendRequest добавьте свой настраиваемый заголовок в исходящее сообщение. Это может выглядеть примерно так:

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request,  System.ServiceModel.IClientChannel channel)
{
    HttpRequestMessageProperty httpRequestMessage;
    object httpRequestMessageObject;
    if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject))
    {
        httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
        if (string.IsNullOrEmpty(httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER]))
        {
            httpRequestMessage.Headers[USER_AGENT_HTTP_HEADER] = this.m_userAgent;
        }
    }
    else
    {
        httpRequestMessage = new HttpRequestMessageProperty();
        httpRequestMessage.Headers.Add(USER_AGENT_HTTP_HEADER, this.m_userAgent);
        request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
    }
    return null;
}

Затем создайте поведение конечной точки, которое применяет инспектор сообщений к среде выполнения клиента. Вы можете применить поведение через атрибут или через конфигурацию с помощью элемента расширения поведения.

Вот отличный пример о том, как добавить заголовок пользовательского агента HTTP ко всем сообщениям запроса. Я использую это в нескольких моих клиентах. Вы также можете сделать то же самое на стороне обслуживания, выполнив IDispatchMessageInspector.

Это то, что вы имели в виду?

Обновление: Я нашел этот список функций WCF, которые поддерживаются компактной картой. Я считаю, что инспекторы сообщений классифицируются как "Расширение канала", которые, согласно этому сообщению, поддерживаются компактной инфраструктурой.

Ответ 2

Вы добавляете его к вызову, используя:

using (OperationContextScope scope = new OperationContextScope((IContextChannel)channel))
{
    MessageHeader<string> header = new MessageHeader<string>("secret message");
    var untyped = header.GetUntypedHeader("Identity", "http://www.my-website.com");
    OperationContext.Current.OutgoingMessageHeaders.Add(untyped);

    // now make the WCF call within this using block
}

И затем, на стороне сервера вы его захватите, используя:

MessageHeaders headers = OperationContext.Current.IncomingMessageHeaders;
string identity = headers.GetHeader<string>("Identity", "http://www.my-website.com");

Ответ 3

Если вы просто хотите добавить один и тот же заголовок ко всем запросам службы, вы можете сделать это без какой-либо кодировки!
Просто добавьте заголовки node с необходимыми заголовками под конечной точкой node в конфигурационном файле клиента

<client>  
  <endpoint address="http://localhost/..." >  
    <headers>  
      <HeaderName>Value</HeaderName>  
    </headers>   
 </endpoint>  

Ответ 4

Вот еще одно полезное решение для ручного добавления пользовательских заголовков HTTP в ваш клиентский запрос WCF с использованием ChannelFactory в качестве прокси. Это нужно сделать для каждого запроса, но в качестве простой демонстрации достаточно, если вам просто нужно unit test ваш прокси-сервер для подготовки к платформам .NET.

// create channel factory / proxy ...
using (OperationContextScope scope = new OperationContextScope(proxy))
{
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = new HttpRequestMessageProperty()
    {
        Headers = 
        { 
            { "MyCustomHeader", Environment.UserName },
            { HttpRequestHeader.UserAgent, "My Custom Agent"}
        }
    };    
    // perform proxy operations... 
}

Ответ 5

Это похоже на ответ NimsDotNet, но показывает, как это сделать программно.

Просто добавьте заголовок к привязке

var cl = new MyServiceClient();

var eab = new EndpointAddressBuilder(cl.Endpoint.Address);

eab.Headers.Add( 
      AddressHeader.CreateAddressHeader("ClientIdentification",  // Header Name
                                         string.Empty,           // Namespace
                                         "JabberwockyClient"));  // Header Value

cl.Endpoint.Address = eab.ToEndpointAddress();

Ответ 6

var endpoint = new EndpointAddress(new Uri(RemoteAddress),
               new[] { AddressHeader.CreateAddressHeader(
                       "APIKey", 
                       "",
                       "bda11d91-7ade-4da1-855d-24adfe39d174") 
                     });

Ответ 7

Контекст привязки в .NET 3.5 может быть именно тем, что вы ищете. Есть три варианта: BasicHttpContextBinding, NetTcpContextBinding и WSHttpContextBinding. Контекстный протокол в основном передает пары ключ-значение в заголовке сообщения. Ознакомьтесь с статья "Управление государством с долговременными службами" в журнале MSDN.

Ответ 8

Если я правильно понимаю ваше требование, простой ответ таков: вы не можете.

Это потому, что клиент службы WCF может быть сгенерирован любой третьей стороной, которая использует вашу службу.

IF вы контролируете клиентов своей службы, вы можете создать базовый клиентский класс, который добавляет желаемый заголовок и наследует поведение на рабочих классах.

Ответ 9

Вы можете указать пользовательские заголовки в MessageContract.

Вы также можете использовать <endpoint> headers, которые хранятся в файле конфигурации и будут скопированы в заголовке всех сообщений, отправленных клиентом/службой. Это полезно, чтобы легко добавить некоторый статический заголовок.

Ответ 10

Это работает для меня

TestService.ReconstitutionClient _serv = new TestService.TestClient();

using (OperationContextScope contextScope = new OperationContextScope(_serv.InnerChannel))
{
   HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();

   requestMessage.Headers["apiKey"] = ConfigurationManager.AppSettings["apikey"]; 
   OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = 
      requestMessage;
   _serv.Method(Testarg);
}

Ответ 11

Это то, что сработало для меня, адаптировано из Добавление заголовков HTTP в вызовы WCF

// Message inspector used to add the User-Agent HTTP Header to the WCF calls for Server
public class AddUserAgentClientMessageInspector : IClientMessageInspector
{
    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
    {
        HttpRequestMessageProperty property = new HttpRequestMessageProperty();

        var userAgent = "MyUserAgent/1.0.0.0";

        if (request.Properties.Count == 0 || request.Properties[HttpRequestMessageProperty.Name] == null)
        {
            var property = new HttpRequestMessageProperty();
            property.Headers["User-Agent"] = userAgent;
            request.Properties.Add(HttpRequestMessageProperty.Name, property);
        }
        else
        {
            ((HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]).Headers["User-Agent"] = userAgent;
        }
        return null;
    }

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
    }
}

// Endpoint behavior used to add the User-Agent HTTP Header to WCF calls for Server
public class AddUserAgentEndpointBehavior : IEndpointBehavior
{
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new AddUserAgentClientMessageInspector());
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }
}

После объявления этих классов вы можете добавить новое поведение к клиенту WCF следующим образом:

client.Endpoint.Behaviors.Add(new AddUserAgentEndpointBehavior());

Ответ 12

Немного поздно, но Juval Lowy обращается к этому точному сценарию в своей книге и связанной с ней ServiceModelEx.

В основном он определяет специализации ClientBase и ChannelFactory, которые позволяют указывать значения заголовков, безопасных для типов. Я рекомендую загрузить исходный код и посмотреть классы HeaderClientBase и HeaderChannelFactory.

Джон