简体   繁体   中英

Enable CORS POST from jQuery ajax to WCF Service NOT Hosted in IIS

I am needing to make a cross domain POST request to a WCF service hosted by an executable, NOT from IIS. The purpose is to pass in an array of id strings from a web client, which the WCF service will use to find and return logs pertaining to those ids.

I believe I am close and that the main piece I'm missing is a piece of configuration in the app.config file which adds a custom header. I will note that elsewhere in my solution there is a web.config for another service that describes the behavior I'm looking for, which includes:

<httpProtocol>
  <customHeaders>
    <!-- http://stackoverflow.com/questions/12458444/enabling-cross-origin-resource-sharing-on-iis7 -->
    <add name="Access-Control-Allow-Headers" value="X-Requested-With,Content-Type,Accept" />
    <add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
    <add name="Access-Control-Allow-Credentials" value="true" />
    <add name="Timing-Allow-Origin" value="*" />
  </customHeaders>
</httpProtocol>

Now my issue with the above code is that it resides in a web.config file for an IIS hosted service. I need to be able to describe that custom header in an app.config for my service which I am hosting in an executable (I have provided the method in question below).

I have been trying to solve this for days, and thus far, aside from the configuration confusion I'm looking for help with, it's my understanding that this syntax is alright for calling the service:

        var settings: JQueryAjaxSettings = {};

        settings.async = true;
        settings.crossDomain = true;
        settings.url = this.getLogEntriesByIdResource;
        settings.method = "POST";
        settings.type = "POST";
        settings.dataType = 'JSON';
        settings.headers = {
            "content-type": "application/json",
            "cache-control": "no-cache"
        }
        settings.data = JSON.stringify(ids);

        jQuery.ajax(settings).done(function (response) {
            console.log(response);
        });

and that this is a good pass at a WCF endpoint which the above code calls:

    [OperationContract, CorsEnabled]
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare,
        RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "GetLogEntriesById")]
    [Description("Request entities or entity updates for a particular time and place")]
    public List<LogEntryInfo> GetLogEntriesById(string[] ids)
    {
        List<LogEntryInfo> results = new List<LogEntryInfo>();
        List<ILogEntry> rels = Gateway.MongoDbInstance.ReturnLogEntries(ids.ToList());
        rels.ForEach(e => results.Add(new LogEntryInfo(e)));
        return results;
    }

Additionally, here is the code which sets up my webServiceHost:

    public LogEntryRestService(string baseAddress, string endpoint)
    {
        _baseAddress = baseAddress;
        _endpoint = endpoint;

        _webServiceHost = new WebServiceHost(this, new Uri(baseAddress));

        var webBinding = new WebHttpBinding(WebHttpSecurityMode.None) { CrossDomainScriptAccessEnabled = true };
        ServiceEndpoint ep = _webServiceHost.AddServiceEndpoint(typeof(LogEntryRestService), webBinding, endpoint);

        WebHttpBehavior webBehavior = new WebHttpBehavior();
        webBehavior.HelpEnabled = true;

        ep.EndpointBehaviors.Add(webBehavior);
        ep.EndpointBehaviors.Add(new EnableCorsEndpointBehavior());

        Program.LogMessage(string.Format("Initializing LogEntry REST endpoint BaseAddress: {0} EndpointName: {1}",
                _webServiceHost.BaseAddresses[0], endpoint));

        _webServiceHost.Open();
    }

I'll also point out that standard calls from Postman with JSON body specified to the endpoint are working perfectly. I am aware that Postman (a Chrome extension) doesn't adhere to CORS the same way a web page does. My calling ajax code is the same suggested calling code that Postman provides for jQuery ajax, so I assume that means I'm seeing a CORS issue.

I am a little exhausted from all of this configuration and CORS appeasement and I really appreciate the fresh eyes and the help!

UPDATE

So I tried deploying my current code (what I've shared above) to an actual server instance, as opposed to debugging on my local box, and found that everything is working fine. I am however, still receiving errors locally that I would like to resolve though. That error is described by this response when my locally debugged client is calling my locally debugged service:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Service</title>
    <style>BODY { color: #000000; background-color: white; font-family: Verdana; margin-left: 0px; margin-top: 0px; } #content { margin-left: 30px; font-size: .70em; padding-bottom: 2em; } A:link { color: #336699; font-weight: bold; text-decoration: underline; } A:visited { color: #6699cc; font-weight: bold; text-decoration: underline; } A:active { color: #336699; font-weight: bold; text-decoration: underline; } .heading1 { background-color: #003366; border-bottom: #336699 6px solid; color: #ffffff; font-family: Tahoma; font-size: 26px; font-weight: normal;margin: 0em 0em 10px -20px; padding-bottom: 8px; padding-left: 30px;padding-top: 16px;} pre { font-size:small; background-color: #e5e5cc; padding: 5px; font-family: Courier New; margin-top: 0px; border: 1px #f0f0e0 solid; white-space: pre-wrap; white-space: -pre-wrap; word-wrap: break-word; } table { border-collapse: collapse; border-spacing: 0px; font-family: Verdana;} table th { border-right: 2px white solid; border-bottom: 2px white solid; font-weight: bold; background-color: #cecf9c;} table td { border-right: 2px white solid; border-bottom: 2px white solid; background-color: #e5e5cc;}</style>
  </head>
  <body>
    <div id="content">
      <p class="heading1">Service</p>
      <p xmlns="">Method not allowed. Please see the <a rel="help-page" href="http://localhost/LogEntryService/help">service help page</a> for constructing valid requests to the service.</p>
    </div>
  </body>
</html>                                

I'll also note that the Request Method is shown as "OPTIONS" rather that POST, which is what it is supposed to be, and is the behavior that I see when running all this code on the dedicated server.

Any reason why a POST method would default to an OPTIONS method? I assume I am back to square one in that to solve this I need to enable CORS on my non-IIS hosted web service. Thanks again for any help or insight!

UPDATE

I've included below a screen cap of both a fiddler inspection of the request and the Chrome dev tools network view of the request.

提琴手检查 Fiddler inspection^

Chrome开发者工具的请求和响应 Chrome dev tools request and response^

This should help you break down where your problem is. The screen shots I saw (mainly the fiddle) that was a preflight. Those are Options. But if you pass preflight your Post should get sent off so there should be another entry in fiddle after that. Here is an example of a good Post in Fiddle.

Preflight (raw):

OPTIONS http://localhost:5000/api/values/dui/ HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://evil.com/
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Access-Control-Request-Headers: access-control-allow-origin, content-type
Accept: */*
Referer: http://localhost:3000/validator
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8

Immediately followed by the Post

Request (Raw):

POST http://localhost:5000/api/values/dui/ HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Content-Length: 17
access-control-allow-origin: *
accept: application/json
Origin: http://evil.com/
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
content-type: application/json
Referer: http://localhost:3000/validator
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

{"name":"test"}

Response from my API (raw):

HTTP/1.1 200 OK
Date: Thu, 09 Mar 2017 16:42:27 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 57

{"name":"Hey Test, Im The Dude man.","link":"dude.png"}

I don't think you are passing preflight. Im not sure where that response in Chrome is coming from but you need to ensure you are setup to pass preflight. So The code in your application must have the right cors configuration as well as the server (in IIS using web.config). I ran into a similar issue with a dotnet core webApi and React UI getting Cors setup correctly (view here ). You can see how I dealt with it here. Hope this helps. But I would focus on preflight, make sure that is passing. If so move on to the Post itself. If you aren't seeing your content type in the Post you are setting up (application/json) for example, your custom header is getting clobbered somewhere. Sorry I can't pinpoint exactly where you need to set it but Options is preflight is normal. I wouldn't focus on that.

*******Update for Solution Added**********

I believe you need something like this to solve your problem. Basically you need to add the header in the WebService itself.

"Add global.asax file and add following code to Application_BeginRequest. Following is the code snippet."

protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin",    "http://localhost");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE");

    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
    HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
    HttpContext.Current.Response.End();
}
}

Full reference is here . The article is fairly recent. This should resolve your issue.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM