问题
I have scratched my head for 7 hours trying to figure this out. I have searched all over the web but no luck. I have an Angular App that is making requests to a WCF command-line hosted service application. I managed to get by CORS by using these two classes:
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, 
    System.ServiceModel.IClientChannel channel, 
    System.ServiceModel.InstanceContext instanceContext)
    {
        return null;
    }
    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
        foreach (var item in requiredHeaders)
        {
            httpHeader.Headers.Add(item.Key, item.Value);
        }
    }
}
And:
public class EnableCorsBehavior : BehaviorExtensionElement, IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    { }
    public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
    { }
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    {
        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");
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
    }
    public void Validate(ServiceEndpoint endpoint) { }
    public override Type BehaviorType
    {
        get { return typeof(EnableCorsBehavior); }
    }
    protected override object CreateBehavior()
    {
        return new EnableCorsBehavior();
    }
}
Adding this custom extension to the app.config file solved my CORS problem. My current problem is whenever I make a POST request, I get the error:
Request Method:OPTIONS
Status Code:405 Method Not Allowed
I am quite new to C# and I can't seem to find where to place the code that will allow me to get past this. I have an idea that it should be placed somewhere in the BeforeSendReply() method. Please help me! I will really really appreciate it!
Regards!
回答1:
I figured out the solution to this and i hope this helps everyone who comes across this same issue. In the CustomHeaderMessageInspector class that I posted in the question, I edited the following code in the AfterReceiveRequest method as follows:
// return null;
var httpRequest = (HttpRequestMessageProperty)request
    .Properties[HttpRequestMessageProperty.Name];
return new
{
    origin = httpRequest.Headers["Origin"],
    handlePreflight = httpRequest.Method.Equals("OPTIONS",
    StringComparison.InvariantCultureIgnoreCase)
};
What I hoped that code did is monitor any request with the OPTIONS method and "tag" it with a preflight state. Then I modified the code in the BeforeSendReply to look as follows:
var state = (dynamic)correlationState;
if (state.handlePreflight)
{
    reply = Message.CreateMessage(MessageVersion.None, "PreflightReturn");
    var httpResponse = new HttpResponseMessageProperty();
    reply.Properties.Add(HttpResponseMessageProperty.Name, httpResponse);
    httpResponse.SuppressEntityBody = true;
    httpResponse.StatusCode = HttpStatusCode.OK;
}
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
    httpHeader.Headers.Add(item.Key, item.Value);
}
What that does (i hope) is get any request tagged with OPTIONS and handle it by returning a 200 status code. This got it finally working and I hope it helps someone!
回答2:
In addition to realnsleo answer:
I had problems to use (dynamic)correlationState because my project has to be in Framework 3.5
I tried to simplify some lines, too:
private class CORSHeaderInjectingMessageInspector : IDispatchMessageInspector
{
    private static IDictionary<string, string> _headersToInject = new Dictionary<string, string>
    {
        { "Access-Control-Allow-Origin", "*" },
        { "Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS" },
        { "Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Origin,Accept" },
        { "Access-Control-Request-Headers", "POST" }
    };
    public object AfterReceiveRequest( ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        var httpRequest = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];       
        return httpRequest.Method.Equals("OPTIONS", StringComparison.InvariantCulture);   
    }
    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        if ((bool) correlationState)
        {
            var httpResponse = (HttpResponseMessageProperty)reply.Properties[HttpResponseMessageProperty.Name];
            httpResponse.SuppressEntityBody = true;
            httpResponse.StatusCode = HttpStatusCode.OK;
        }
        var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
        foreach (var item in _headersToInject)
        {
            httpHeader.Headers.Add(item.Key, item.Value);
        }
    }
    来源:https://stackoverflow.com/questions/48490239/enabling-options-method-in-cors-during-rest-request-from-ajax-on-wcf-service