Search code examples
c#wcf

WCF host to add custom HTTP header to response


I have a standalone C# WCF service running as a Windows service. I have the requirement to add custom headers like X-Frame-Options to all responses. I have tried to add an instance of the following class to ServiceEndpoint.Behaviors

internal class ServerInterceptor : IDispatchMessageInspector, IEndpointBehavior
{
    object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        return null;
    }

    void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
    {
        reply.Properties.Add("X-Frame-Options", "deny");
    }

    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
    }

    void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { }

    void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
}

This doesn't add any HTTP header to the response although the class gets called as the debugger can step into the BeforeSendReply function. Furthermore if I replace reply.Properties with reply.Headers then the header is added, but not to the HTTP headers but to the SOAP headers.

How can I add a HTTP header like X-Frame-Options to the response?


Solution

  • I made an example, which is used to add extra CORS HTTP header, wish it is instrumental for you.
    Message Inspector.

            public class CustomHeaderMessageInspector : IDispatchMessageInspector
            {
                Dictionary<string, string> requiredHeaders;
                public CustomHeaderMessageInspector(Dictionary<string, string> headers)
                {
                    requiredHeaders = headers ?? new Dictionary<string, string>();
                }
                public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
                {
                    string displayText = $"Server has received the following message:\n{request}\n";
                    Console.WriteLine(displayText);
                    return null;
                }
    
                public void BeforeSendReply(ref Message reply, object correlationState)
                {
                    if (!reply.Properties.ContainsKey("httpResponse")) 
                    reply.Properties.Add("httpResponse", new HttpResponseMessageProperty());
    
                    var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
                    foreach (var item in requiredHeaders)
                    {
                        httpHeader.Headers.Add(item.Key, item.Value);
                    }
    
                    string displayText = $"Server has replied the following message:\n{reply}\n";
                    Console.WriteLine(displayText);
    
                }
        }
    

    Custom Contract Attribute.

    public class MyBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
        {
            public Type TargetContract => typeof(MyBehaviorAttribute);
    
            public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
            {
            }
    
            public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
            }
    
            public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
            {
                var requiredHeaders = new Dictionary<string, string>();
    
                requiredHeaders.Add("Access-Control-Allow-Origin", "*");
                requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
                requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
    
                dispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
            }
    
            public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
            {
    
            }
    }
    

    Apply the contract behavior.

    [ServiceContract(Namespace = "mydomain")]
    [MyBehavior]
    public interface IService
    {
        [OperationContract]
        [WebGet]
        string SayHello();
    }
    

    Result.
    enter image description here

    Feel free to let me know if there is anything I can help with.