6

I've created one phonegap app where I'm calling WCF service which resides in nopCommerce plugin.

I'm getting following error while sending api request:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://testwebsite.com/Plugins/NopRestApi/RemoteService/WebService.svc/GetData. (Reason: CORS preflight channel did not succeed).

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://testwebsite.com/Plugins/NopRestApi/RemoteService/WebService.svc/GetData. (Reason: CORS request failed).

API call using Ajax

$.ajax({
  crossDomain: true,
  type: "POST",
  contentType: "application/json",
  async: false,
  url: "http://testwebsite.com/Plugins/NopRestApi/RemoteService/WebService.svc/GetData",
  data: "{storeId:" + storeId + ", languageId:" + languageId + ", customerId:" + customerId + "}",            
  //data: { storeId: storeId, languageId: languageId, customerId: customerId },
  dataType: 'json',
  //jsonp: false,
  success: function (data) {
      alert(data.d);                
  },
  error: function (xhr, textStatus, error) {
      alert("Error! " + error);                
  }
});

I've added following in my Web.config file by some research on google.

<httpProtocol>
  <customHeaders>
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Headers" value="Content-Type" />
  </customHeaders>
</httpProtocol>

I've tried many ways even disabled browser cross origin request check by following How to temporarily disable XSS protection in modern browsers for testing? link but still same issue persist.

Does anyone have similar issue and resolved?

Thanks!

Community
  • 1
  • 1
immayankmodi
  • 8,210
  • 9
  • 38
  • 55
  • I got similar experience, browser with preflight, Cors not working. browser without preflight, Cors works! – Kenneth Li May 29 '15 at 14:56
  • @KennethLi thanks. Which browser, are you talking about? One more thing, It'll work in phonegap for Android, iOS etc? – immayankmodi May 29 '15 at 15:22
  • I already solved the problem and it now works in all known pc browsers, all known mobile browsers, all version of android and all version of iPhone and iPad. The problem is, I forgot how I did it, looking for it in my codes now. – Kenneth Li May 29 '15 at 15:41
  • All known means IE, safari, chrome, firefox – Kenneth Li May 29 '15 at 15:42
  • I got it, it's a OPTIONS call during a preflight, so I add a default response in the global.asax for all OPTIONS call. Then the cors works in ALL browsers instead of some browsers. By the way I don't know how to add codes in comment so I add it in answer, pls take a look. – Kenneth Li May 29 '15 at 15:46
  • OK, is there no other way to do so? – immayankmodi May 29 '15 at 15:49

3 Answers3

4

In global.asax:

Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
    ' Fires at the beginning of each request
    If Request.HttpMethod = "OPTIONS" Then
        Response.Flush()
    End If

End Sub

And the allow method in web.config show add OPTIONS too.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
    <add key="aspnet:MaxJsonDeserializerMembers" value="1000000" />
  </appSettings>

  <connectionStrings/>
  <system.web>
    <compilation debug="true" strict="false" explicit="true" targetFramework="4.5.1"/>
    <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID">
      <namespaces>
        <clear/>
        <add namespace="System"/>
        <add namespace="System.Collections"/>
        <add namespace="System.Collections.Specialized"/>
        <add namespace="System.Configuration"/>
        <add namespace="System.Text"/>
        <add namespace="System.Text.RegularExpressions"/>
        <add namespace="System.Web"/>
        <add namespace="System.Web.Caching"/>
        <add namespace="System.Web.SessionState"/>
        <add namespace="System.Web.Security"/>
        <add namespace="System.Web.Profile"/>
        <add namespace="System.Web.UI"/>
        <add namespace="System.Web.UI.WebControls"/>
        <add namespace="System.Web.UI.WebControls.WebParts"/>
        <add namespace="System.Web.UI.HtmlControls"/>
      </namespaces>
    </pages>
    <authentication mode="Windows"/>
    <webServices>
      <protocols>
        <add name="HttpGet"/>
        <add name="HttpPost"/>
      </protocols>
    </webServices>
  </system.web>
  <system.webServer>
    <staticContent>
      <mimeMap fileExtension=".apk" mimeType="application/octet-stream"/>
    </staticContent>
  <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
      <remove name="OPTIONSVerbHandler"/>
      <remove name="TRACEVerbHandler"/>
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
    </handlers>
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Methods" value="POST,OPTIONS,GET"/>
        <add name="Access-Control-Allow-Headers" value="Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept"/>
        <add name="Access-Control-Allow-Origin" value="*"/>
      </customHeaders>
    </httpProtocol>
  </system.webServer>
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
                <assemblyIdentity name="System.Web.Cors" publicKeyToken="31BF3856AD364E35" culture="neutral"/>
                <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
            </dependentAssembly>
        </assemblyBinding>
    </runtime>
  <system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization maxJsonLength="4000000" />
      </webServices>
    </scripting>
  </system.web.extensions>

</configuration>

Sorry that the global.asax is in VB cuz I'm a VB guy, I think you can convert it to C#.

Per your request, update as follow:

function funCheckWS() {
    var tblLogin = {};
    tblLogin.strLoginName = "UserName";
    tblLogin.strPassword = "123456";
    jQuery.support.cors = true;
    $.ajax({
        type: 'POST',
        url: 'http://www.example.com/wsMain.asmx/funCheckWS',
        data: "{tblLogin:" + JSON.stringify(tblLogin) + "}",
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        crossDomain: true,
        // async: false,
        async: true,
        success: function (r) {
            // Browser support WS
            gloBolWS = true;
            // alert("funCheck: " + r.d.strPassword);
        },
        error: function (xhr, ajaxOptions, thrownError) {
            // Browser does not support WS
            //alert(xhr.status);
            //alert(thrownError);
        }
    });
}

Below is my web services in VB (the clsSQL is a Class written in VB that contains functions of all my backend business logics and can access the sql server):

Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.ComponentModel
Imports System.Web.Script.Serialization
Imports System.Web.Script.Services
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web

<System.Web.Script.Services.ScriptService()> _
<System.Web.Services.WebService(Namespace:="http://tempuri.org/")> _
<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<ToolboxItem(False)> _
Public Class wsMain
    Inherits System.Web.Services.WebService

    Private clsSQL As New wxdlSQL.clsSQL()

    Private tblLogin As tabLogin
    <WebMethod()> _
    <ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
    Public Function funCheckWS(tblLogin As tabLogin) As tabLogin
        tblLogin.strLoginName += "Hello World"
        Return tblLogin
    End Function
End Class

Below is how I define data type for the parameters:

Public Class tabLogin
    Private _loginname As String
    Public Property strLoginName As String
        Get
            Return _loginname
        End Get
        Set(ByVal value As String)
            _loginname = value
        End Set
    End Property
    Private _password As String
    Public Property strPassword As String
        Get
            Return _password
        End Get
        Set(ByVal value As String)
            _password = value
        End Set
    End Property
End Class
Kenneth Li
  • 1,632
  • 1
  • 14
  • 19
  • Yeah, I can convert it into c#. I need to test it! – immayankmodi May 29 '15 at 15:55
  • Before these 3 lines in BeginRequest were added, the CORS works in some browsers without preflight but failed in some other browsers with preflight but I forgot which is which. After these 3 lines are added, CORS works in all browsers I've installed. (Of course, I didn't test all versions of those browsers, but I installed the latest) – Kenneth Li May 29 '15 at 16:00
  • I've added this but still having same issue. Can you post your Ajax call for api call POST type and which version of jQuery you'd used?? – immayankmodi May 30 '15 at 04:08
  • I'm using mobile phone to reply, don't have the pc with me, but I remember the jquery is 2.2.0 – Kenneth Li May 30 '15 at 04:30
  • by the way: the async = false just killing me! when the connection is slow, my app. just hangs there! now I need to spend days to change all my sync to async! do you have similar problem? – Kenneth Li Jun 01 '15 at 04:19
  • Thanks. My issue only was I was not able to access that directly from web browsers even after disabling origin settings. However main think is I can access from phonegap app by enabling `` in app's config file. No need to add settings in `global.asax` file, only you need to add allow origin settings in Web.config file as you are currently using. For ajax call, no need to send `crossDomain` as well as `asyc` parameters while posting data to server. Make sure you are sending JSON as request otherwise it'll take longer response time. – immayankmodi Jun 02 '15 at 06:24
3

You need to allow cross domain request from your WCF service.

Response.AppendHeader("Access-Control-Allow-Origin", "*");

add this in your function but add this before your data return from function.

How to implement “Access-Control-Allow-Origin” header in asp.net might be helpful.

Community
  • 1
  • 1
Vinit Patel
  • 2,408
  • 5
  • 28
  • 53
  • 1
    Thanks for the reply. Actually I already added that but the thing is I'm not able to make api request from browsers even after disable cross-domain/origin setting from about:config. I think now it's no longer supported. However main think is I can access from phonegap app by enabling `` in phonegap app config file. – immayankmodi Jun 02 '15 at 06:16
  • Is this " phonegap app by enabling in phonegap app config file " works? if yes then reply with exctly solution.. – Vinit Patel Jun 02 '15 at 06:22
1

In your Global.asax, add the following code:

protected void Application_AuthenticateRequest(object sender, EventArgs e){
    Response.AddHeader("Access-Control-Allow-Origin", "*");
    if (Context.Request.HttpMethod.Equals("OPTIONS")){
        Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
        Response.AddHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
        Context.ApplicationInstance.CompleteRequest();
     }
 }

That should work

Brian Burns
  • 20,575
  • 8
  • 83
  • 77
Esha Wali
  • 19
  • 2