1

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 Fiddler inspection^

Chrome dev tools request and response Chrome dev tools request and response^

ThePartyTurtle
  • 2,276
  • 2
  • 18
  • 32
  • 1
    Im not a jquery expert but that content-type: application/json will trigger a preflight response, I do know that. Are you getting an error?? I didn't see any errors in your post. – Puerto Mar 07 '17 at 20:23
  • Oh I should add my error, thanks for pointing that out. And I think you're right about that, although I know I need to specify JSON content type somehow, since that's how I plan to pass the array of strings to my web method. I've also made some progress since originally posting, I'll update my post now. Thanks for taking a look! – ThePartyTurtle Mar 07 '17 at 21:01
  • 1
    Do you have fiddler?? I would be looking at the traffic in fiddler. If your headers are changing than that most likely means you are not sending a custom header and will end up with a default header which could change the values you are sending through (although I don't know if this effects the method or not). If this is happening you need to confirm your cors configuration in your applications thoroughly. Also don't post your error messages in html format, just post the error messages please. – Puerto Mar 07 '17 at 22:03
  • 1
    post a fiddle inspector of the post you are attempting to send maybe. – Puerto Mar 07 '17 at 22:04
  • Hmm so you think the headers I'm attempting to add to the ajax call are getting clobbered somewhere? I have been using postman as well as observing requests and responses in Chrome dev tools, but I will try out Fiddler. Does fiddler reveal more than post man or Chrome dev tools would? Also, I just added a little more information to my question about the service's behavior when called from Postman that may be helpful. – ThePartyTurtle Mar 07 '17 at 22:48
  • 1
    postman will not have cors issues. it spoofs an app in another domain I believe. fiddler is the true story of what's happening when ur app calls ur Webservice. u see the in-between. show me a raw fiddle request or response that should help diagnose. – Puerto Mar 08 '17 at 00:41
  • Updating with fiddler inspection along with a dev tools inspection. – ThePartyTurtle Mar 08 '17 at 19:03
  • I can't see screen shots at work, they are blocked. Sorry. Might be able to view it later when I get home. – Puerto Mar 08 '17 at 20:33
  • did they at least look like what you were expecting? – Puerto Mar 08 '17 at 20:34
  • Yea more or less. I'm seeing the OPTIONS call that comes before the POST call, but no POST or hitting of my endpoint while debugging. The call returns a 405 method not allowed error. I think it's because I haven't added the response header correctly, so the client never get's an acknowledgement that CORS is ok. Problem is, I really don't know how to add the header in my WCF service and I've looked all over the place. – ThePartyTurtle Mar 08 '17 at 20:57
  • So far, I've tried adding to CorsEnabled attribute to my WebInvoke method as shown above, I've also tried this method of adding a custom header: http://stackoverflow.com/questions/38129187/options-method-not-allowed-get-works-but-not-post , and I'm currently trying to fiddle with the app.config but I really just don't know a lot about it. And there is a decent chance I've done something incorrectly. – ThePartyTurtle Mar 08 '17 at 21:03
  • Problem seems to be that the server is not accepting the OPTION verb used in the CORS preflight request. I'd venture to guess that this is not directly related to CORS on the server side, but instead some sort of configuration issue relating to which http verbs are accepted by the server. Have you seen [this](http://stackoverflow.com/a/14909140/1429080)? – user1429080 Mar 09 '17 at 10:08
  • I looked at the pic's last night. The are preflight requests only I believe. – Puerto Mar 09 '17 at 16:23
  • Are you not seeing a preflight error in your dev tools console? – Puerto Mar 09 '17 at 16:35

1 Answers1

2

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.

Community
  • 1
  • 1
Puerto
  • 1,072
  • 3
  • 11
  • 32
  • Yea you are hitting the nail on the hammer. My problem then, is that I am having a really difficult time having my service ok the preflight and allow the following POST. Thanks so much for the time and explanation! – ThePartyTurtle Mar 09 '17 at 17:51
  • 1
    @ThePartyTurtle see the update to the solution I posted. Hope that solves it. – Puerto Mar 09 '17 at 18:52
  • no, unfortunately I could not get that method to solve my issues :^/. And admittedly I'm having to put this on the back burner for the time being. Luckily, like I mentioned there are no CORS issues in the production environment. When I come revisit this issue I will give your method, the one mentioned in that post, another fresh try with a fresh mind. Thanks again for you help! – ThePartyTurtle Mar 10 '17 at 19:04