How to add a custom HTTP header to every WCF call?

后端 未结 13 2293
不知归路
不知归路 2020-11-22 05:33

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 i

相关标签:
13条回答
  • 2020-11-22 06:21

    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>
    
    0 讨论(0)
  • 2020-11-22 06:24

    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.

    0 讨论(0)
  • 2020-11-22 06:26

    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");
    
    0 讨论(0)
  • 2020-11-22 06:29

    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);
    }
    
    0 讨论(0)
  • 2020-11-22 06:30

    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.

    0 讨论(0)
  • 2020-11-22 06:33

    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... 
    }
    
    0 讨论(0)
提交回复
热议问题