Как добавить пользовательский заголовок HTTP к каждому вызову WCF?


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

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

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

12 145
wcf

12 ответов:

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

создать класс, который реализует 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 user-agent ко всем сообщениям запроса. Я использую это в некоторых из моих клиентов. Вы также можете сделать то же самое на стороне услуги по реализации IDispatchMessageInspector.

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

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

вы добавляете его в вызов с помощью:

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");

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

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

вот еще одно полезное решение для ручного добавления пользовательских заголовков HTTP в клиентский запрос WCF с помощью ChannelFactory в качестве прокси. Это должно быть сделано для каждого запроса, но достаточно как простая демонстрация, если вам просто нужно проверить свой прокси-сервер в процессе подготовки non-.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... 
}

это похоже на ответ 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();
var endpoint = new EndpointAddress(new Uri(RemoteAddress),
                                               new[]
                                                   {
                                                       AddressHeader.CreateAddressHeader("APIKey", "",
                                                                                         "bda11d91-7ade-4da1-855d-24adfe39d174")
                                                   });

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

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

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

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

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

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

это то, что сработало для меня, адаптировано из добавление заголовков 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());

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

TestService.ReconstitutionClient _serv = новый 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);
                }

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

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

Джон