167
votes

I have a WCF service that is hosted in a Windows Service. Clients that using this service must pass an identifier every time they're calling service methods (because that identifier is important for what the called method should do). I thought it is a good idea to somehow put this identifier to the WCF header information.

If it is a good idea, how can I add the identifier automatically to the header information. In other words, whenever the user calls the WCF method, the identifier must be automatically added to the header.

UPDATE: Clients that are using the WCF service are both Windows applications and Windows Mobile application (using Compact Framework).

14
Did you end up getting this to work on the Compact Framework?Vaccano

14 Answers

189
votes

The advantage to this is that it is applied to every call.

Create a class that implements IClientMessageInspector. In the BeforeSendRequest method, add your custom header to the outgoing message. It might look something like this:

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

Then create an endpoint behavior that applies the message inspector to the client runtime. You can apply the behavior via an attribute or via configuration using a behavior extension element.

Here is a great example of how to add an HTTP user-agent header to all request messages. I am using this in a few of my clients. You can also do the same on the service side by implementing the IDispatchMessageInspector.

Is this what you had in mind?

Update: I found this list of WCF features that are supported by the compact framework. I believe message inspectors classified as 'Channel Extensibility' which, according to this post, are supported by the compact framework.

81
votes

You add it to the call using:

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
}

And then, server-side you grab it using:

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

If you just want to add the same header to all the requests to the service, you can do it with out any coding!
Just add the headers node with required headers under the endpoint node in your client config file

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

Here is another helpful solution for manually adding custom HTTP Headers to your client WCF request using the ChannelFactory as a proxy. This would have to be done for each request, but suffices as a simple demo if you just need to unit test your proxy in preparation for non-.NET platforms.

// 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... 
}
12
votes

This is similar to NimsDotNet answer but shows how to do it programmatically.

Simply add the header to the binding

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();
5
votes
var endpoint = new EndpointAddress(new Uri(RemoteAddress),
               new[] { AddressHeader.CreateAddressHeader(
                       "APIKey", 
                       "",
                       "bda11d91-7ade-4da1-855d-24adfe39d174") 
                     });
5
votes

This works for me

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);
}
4
votes

This is what worked for me, adapted from Adding HTTP Headers to WCF Calls

// 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)
    {
    }
}

After declaring these classes you can add the new behavior to your WCF client like this:

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

If you want to add custom HTTP headers to every WCF call in an object oriented way, look no further.

Just as in Mark Good's and paulwhit's answer, we need to subclass IClientMessageInspector to inject the custom HTTP headers into the WCF request. However, lets make the inspector more generic by accepting an dictionary containing the headers we want to add:

public class HttpHeaderMessageInspector : IClientMessageInspector
{
    private Dictionary<string, string> Headers;

    public HttpHeaderMessageInspector(Dictionary<string, string> headers)
    {
        Headers = headers;
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        // ensure the request header collection exists
        if (request.Properties.Count == 0 || request.Properties[HttpRequestMessageProperty.Name] == null)
        {
            request.Properties.Add(HttpRequestMessageProperty.Name, new HttpRequestMessageProperty());
        }

        // get the request header collection from the request
        var HeadersCollection = ((HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name]).Headers;

        // add our headers
        foreach (var header in Headers) HeadersCollection[header.Key] = header.Value;

        return null;
    }

    // ... other unused interface methods removed for brevity ...
}

Just as in Mark Good's and paulwhit's answer, we need to subclass IEndpointBehavior to inject our HttpHeaderMessageInspector into our WCF client.

public class AddHttpHeaderMessageEndpointBehavior : IEndpointBehavior
{
    private IClientMessageInspector HttpHeaderMessageInspector;

    public AddHttpHeaderMessageEndpointBehavior(Dictionary<string, string> headers)
    {
        HttpHeaderMessageInspector = new HttpHeaderMessageInspector(headers);
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        clientRuntime.ClientMessageInspectors.Add(HttpHeaderMessageInspector);
    }

    // ... other unused interface methods removed for brevity ...
}

The last part needed to finish our object oriented approach, is to create a subclass of our WCF auto-generated client (I used Microsoft's WCF Web Service Reference Guide to generate a WCF client).

In my case, I need to attach an API key to the x-api-key HTML header.

The subclass does the following:

  • calls the constructor of the base class with the required parameters (in my case a EndpointConfiguration enum was generated to pass into the constructor - maybe your implementation won't have this)
  • Defines the headers that should be attached to every requests
  • Attaches AddHttpHeaderMessageEndpointBehavior to the client's Endpoint behaviors
public class Client : MySoapClient
{
    public Client(string apiKey) : base(EndpointConfiguration.SomeConfiguration)
    {
        var headers = new Dictionary<string, string>
        {
            ["x-api-key"] = apiKey
        };

        var behaviour = new AddHttpHeaderMessageEndpointBehavior(headers);
        Endpoint.EndpointBehaviors.Add(behaviour);
    }
}

Finally, use your client!

var apiKey = 'XXXXXXXXXXXXXXXXXXXXXXXXX';
var client = new Client (apiKey);
var result = client.SomeRequest()

The resulting HTTP request should contain your HTTP headers and look something like this:

POST http://localhost:8888/api/soap HTTP/1.1
Cache-Control: no-cache, max-age=0
Connection: Keep-Alive
Content-Type: text/xml; charset=utf-8
Accept-Encoding: gzip, deflate
x-api-key: XXXXXXXXXXXXXXXXXXXXXXXXX
SOAPAction: "http://localhost:8888/api/ISoapService/SomeRequest"
Content-Length: 144
Host: localhost:8888

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <SomeRequestxmlns="http://localhost:8888/api/"/>
  </s:Body>
</s:Envelope>
2
votes

Context bindings in .NET 3.5 might be just what you're looking for. There are three out of the box: BasicHttpContextBinding, NetTcpContextBinding, and WSHttpContextBinding. Context protocol basically passes key-value pairs in the message header. Check out Managing State With Durable Services article on MSDN magazine.

2
votes

If I understand your requirement correctly, the simple answer is: you can't.

That's because the client of the WCF service may be generated by any third party that uses your service.

IF you have control of the clients of your service, you can create a base client class that add the desired header and inherit the behavior on the worker classes.

2
votes

You can specify custom headers in the MessageContract.

You can also use < endpoint> headers that are stored in the configuration file and will be copied allong in the header of all the messages sent by the client/service. This is usefull to add some static header easily.

0
votes

Found another approach here:

SoapServiceClient client = new SoapServiceClient();
using(new OperationContextScope(client.InnerChannel)) 
{
    HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
    requestMessage.Headers["MyHttpHeader"] = "MyHttpHeaderValue";
    OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;    
    var result = client.MyClientMethod();
}
-1
votes

A bit late to the party but Juval Lowy addresses this exact scenario in his book and the associated ServiceModelEx library.

Basically he defines ClientBase and ChannelFactory specialisations that allow specifying type-safe header values. I suggesst downloading the source and looking at the HeaderClientBase and HeaderChannelFactory classes.

John