WCF REST Service Template 40(CS) Cross domain error
Asked Answered
C

3

4

Hi I have a WCF Rest service using ( WCF REST Service Template 40(CS)). I have managed to get it to return Json response. It is working fine when the project is run. But when I try to make a Ajaxcall to the service from the browser, I am getting Error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://192.168.0.70:8001/Service/test. This can be fixed by moving the resource to the same domain or enabling CORS.

And The Ajax call:

$.ajax({
        type: "POST",
        url: "http://192.168.0.70:8001/Service/test",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        async: true,
        cache: false,
        success: function (msg) {
        var Tabels = msg.d;
        alert('success');
        }
    });

Here is the web.config:

    <?xml version="1.0"?>
    <configuration>
      <connectionStrings>

        <add name="ConString" connectionString="SERVER=localhost;DATABASE=fabapp;UID=root;PASSWORD=;"/>
      </connectionStrings>
      <system.web>
        <compilation debug="true" targetFramework="4.0" />
      </system.web>

      <system.webServer>
<httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
      </customHeaders>
    </httpProtocol>
        <modules runAllManagedModulesForAllRequests="true">
          <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        </modules>
      </system.webServer>
      <system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
        <standardEndpoints>
          <webHttpEndpoint>
            <!-- 
                Configure the WCF REST service base address via the global.asax.cs file and the default endpoint 
                via the attributes on the <standardEndpoint> element below
            -->
            <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="false" defaultOutgoingResponseFormat="Json"/>
          </webHttpEndpoint>
        </standardEndpoints>
      </system.serviceModel>

    </configuration>

I tried adding crossDomainScriptAccessEnabled="true" but when I do that the service does not work on the localhost also. I get this:

Cross domain javascript callback is not supported in authenticated services.

Anything I need to change in web.config file?

Culler answered 17/9, 2014 at 11:41 Comment(0)
S
3

Practically speaking , i had faced this issue, i have gone one step further to check WebAPI, and same effort was required, when i analysed. So i had to fix this CORS with WCF. I will try to explain in short. Here we go. When you access WCF request with CrossOrigin, like from JS code existing in different domain, and from JS , you try to do PUT or POST request, 1st browser sends an OPTION request 405 HTTP Status, to see if this domain is in allowed list, then if your WCF respond to OPTIONS request, sends required response with header value, then browser will again do a POST or PUT request ( which ever browser issued earlier), and it will work as expected.

NOTE: you can not send ("Access-Control-Allow-Origin", "*"), because, there is a security feature , that mandates required domain name to be listed in Access-Control-Allow-Origin instead of *.

For more info -

http://social.msdn.microsoft.com/Forums/ro-RO/5613de55-2573-49ca-a389-abacb39e4f8c/wcf-rest-service-post-cross-domain-not-working?forum=wcf

https://stackoverflow.com/questions/26163802/wcf-cors-request-from-jquery-not-working

From practical experience, i have tried * in that header, it was not working. If you don't believe me, go ahead and try .

Finally the code is following. You need to put this in Global.asax.

protected void Application_BeginRequest(object sender, EventArgs e)
{
    String domainname = HttpContext.Current.Request.Headers["Origin"].ToString();
    if (IsAllowedDomain(domainname))
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", domainname);
    String allowedmethods =  "POST, PUT, DELETE, GET";
    String headers = HttpContext.Current.Request.Headers["Access-Control-Request-Headers"].ToString();
    String accesscontrolmaxage =  "1728000";
    String contenttypeforoptionsrequest = "application/json";


    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        //These headers are handling the "pre-flight" OPTIONS call sent by the browser
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", allowedmethods);
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", headers);
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", accesscontrolmaxage);
        HttpContext.Current.Response.AddHeader("ContentType", contenttypeforoptionsrequest);
        HttpContext.Current.Response.End();
    }

}
private bool IsAllowedDomain(String Domain)
{
    if (string.IsNullOrEmpty(Domain)) return false;
    string[] alloweddomains = "http://192.168.0.70:8001"; // you can place comma separated domains here.
    foreach (string alloweddomain in alloweddomains)
    {
        if (Domain.ToLower() == alloweddomain.ToLower())
            return true;
    }
    return false;
}
Subjection answered 9/10, 2014 at 15:16 Comment(16)
* is working, for some reason you just had some issues with it (possibly because of the localhost domain). Is this answer yours or you cited the other answer by @Samir? His answer is 2 hours earlier and yours seems like a copy-paste of his plus some comments.Haze
@WiktorZychla, yes my solution is working, i mean i had used this 6 months back , and point was HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); was not working for mine, so i gone ahead to fix that, and that will send header with required domains, instead of *.Subjection
@ArindamNayak Ur solution isn't working. I am getting error Object reference not set to an instance of an object. at this line: String domainname = HttpContext.Current.Request.Headers["Origin"].ToString();Culler
I see, you should not get error, to just give a try use domainname = "http://192.168.0.70:8001/", see if that helpsSubjection
Now I am getting same error at String headers = HttpContext.Current.Request.Headers["Access-Control-Request-Headers"].ToString();Culler
just use "Content-Type, Accept" there, not sure why you are getting HttpContext.Current.Request.Headers as null! or just look up in browser network console , see value for Access-Control-Request-Headers and use that value here!Subjection
Thanks, looks like it is working. I will test it more and let you knowCuller
How do I get current domain name instead of just hardcoding "http://192.168.0.70:8001/"Culler
There are many ways, i have used above( which didn't work for you), one more way - Request.ServerVariables["HTTP_HOST"] , more help #2181405Subjection
Let us continue this discussion in chat.Culler
Now i got it, can you tell me from which domain you are executing JS, if you have used JS in "example.com" , then use following HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "example.com");Subjection
This may be a stupid question, but why is "GET" not defined in the allowedmethods? I get the Cross domain javascript callback is not supported in authenticated services error when using JSON GET requests via AJAX. And even when I add GET to that list I still get the same error (also when trying to access the service directly via the browser address bar).Augustina
@Flo, I have updated answer to include GET it is working fine, i havn't added that to not to confuse OP. You need to check alloweddomains, it should be equal to domain, where JS call happens, say you have hosted in example1.com and make call to example2.com, then alloweddomains should have http://www.example1.com.Subjection
@ArindamNayak I tried your exact code, but still I get the same error. My services work over http, over https and when I'm not logged in, but the moment I am, the error occurs. Do I also need to alter my web services perhaps?Augustina
@Flo, if you have enabled trace and post those in a separate question, then i can certainly help you out.Subjection
@ArindamNayak, thanks! I've put the new question here: #26824765Augustina
V
2

put following in global.asax file of your wcf application

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("Cache-Control", "no-cache");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
        HttpContext.Current.Response.End();
    }
}
Voluntary answered 9/10, 2014 at 13:11 Comment(1)
I had already tried with the solution you provide, it isn't working for me.Culler
K
0

You might want to take a look at the post in this link:

http://www.codeproject.com/Articles/223572/Calling-Cross-Domain-WCF-service-using-Jquery-Java

Ajax allows to get data in the background without interfering with the display. Ajax call done by using XMLHttpRequest object allows the client side JavaScript code to make HTTP connections. But Ajax call does not allow to get data from cross-domain because of restrictions imposed by the browser. Security error gets issued when requesting data from another domain. One way to avoid security errors is to control remote server where data resides and every request goes to the same domain. That gives rise to the question, what's the fun if data comes from own server only? What to do if data is required to get from the other server?

There is one way to come out from this limitation - to insert a dynamic script element in the Web page, one whose source is pointing to the service URL in the other domain and gets the data in the script itself. When the script loads, it executes. It works because the same-origin policy doesn't prevent dynamic script insertions and treats the scripts as if they were loaded from the domain that provided the Web page. But if this script tries to load a document from yet another domain, it will fail. Fortunately, you can improve this technique by adding JavaScript Object Notation (JSON) to the mix.

JSONP or "JSON with padding" is a complement to the base JSON data format, a pattern of usage that allows a page to request data from a server in a different domain. As a solution to this problem, JSONP is an alternative to a more recent method called Cross-Origin Resource Sharing.

Under the same origin policy, a web page served from server1.example.com cannot normally connect to or communicate with a server other than server1.example.com. An exception is the HTML element. Taking advantage of the open policy for elements, some pages use them to retrieve JavaScript code that operates on dynamically-generated JSON-formatted data from other origins. This usage pattern is known as JSONP. Requests for JSONP retrieve not JSON, but arbitrary JavaScript code. They are evaluated by the JavaScript interpreter, not parsed by a JSON parser.

Personally, I have encountered this problem before, but then what I did is I changed the architecture. I usually would just call the WCF from my web server. So

AJAX Request from Browser -> Web Server -> WCF Service

I think for security purposes, this restriction is built to prevent many security vulnerabilities. However, if you really want to achieve what you want and the previous answers don't meet your requirement, you might want to take a look at this post as alternative. It seems promising. Please keep me updated.

Hope it helps. Thanks.

Karolyn answered 16/10, 2014 at 2:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.