Search code examples
c#wcfwindows-servicesrest

WCF Windows Service installed by ProjectInstaller not working on POST request


I have a WCF C# Rest API application which main desire is be accessible via HTTP to execute scanning documents via device. I've configured CORS and used ProjectInstaller to generate package which should be used to install as windows service. When I run this application in development mode without ProjectInstaller features, everything works fine. The issue becomes when I'm trying to execute scan action - then CORS options request failed. It is very curious, especially since the GET method for check scanner availibity doesn't have any CORS issues with execution. I was tried the multiple variances to solve this issue, applied CORS implementation from Google search results and it was worked, but only on development mode. Finally I used WebHttpBehaviorExtensions, but issue is still occurring.

To clearify - ProjectInstaller is embedded in ConsoleApplication which is linked with WCF Application.

My app.config is here.

My Contract looks like:

    [ServiceContract]
public interface IScanService
{
    [OperationContract]
    [WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped, 
        RequestFormat = WebMessageFormat.Json, UriTemplate = "ScanDocument")]
    Task ScanDocument(int userId, Guid caseId, string fileName);

    [OperationContract]
    [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
        RequestFormat = WebMessageFormat.Json, UriTemplate = "IsAlive")]
    bool IsAlive();
}

What should I do to allow send POST request with enabled CORS?

Thank you in advance for any response.


Solution

  • In your configuration file, I see that you use crossDomainScriptAccessEnabled to solve cross-domain. If you use it to solve cross-domain, the WCF service only supports Jsonp access, and all only support GET requests.

    If your service is hosted in IIS, you can add Global.asax in the project and add the following code in Global.asax:

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
    
        if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    
        {
           HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "*");
    
          HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "*");
    
          HttpContext.Current.Response.End();
        }
    
    }
    

    When WCF is hosted in IIS, IIS will treat it as a web service, so the configuration in Global.asax will take effect.

    If you use self-hosting,you can implement IDispatchMessageInspector to add response headers before the service responds.

    public class ServerMessageLogger : IDispatchMessageInspector
        {
            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
               return null;
            }
    
            public void BeforeSendReply(ref Message reply, object correlationState)
            {
                WebOperationContext ctx = WebOperationContext.Current;
                ctx.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
            }
        }
    

    Add ServerMessageLogger to service behavior:

        [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple = false)]
        public class CustContractBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
        {
            public Type TargetContract => throw new NotImplementedException();
    
            public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
            {
                return;
            }
    
            public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
               return;
            }
    
            public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
            {
                dispatchRuntime.MessageInspectors.Add(new ServerMessageLogger());
            }
    
            public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
            {
                return;
            }
        }
    

    Finally, we need to apply this behavior to the service to support cross-domain:

    enter image description here