1

I have 2 sites.

I also have a webservice.

You can see this in action when I load countrynames in the cascading dropdown on: http://www.mydomain.com/trouwlocaties/zoeken-uitgebreid

However, the same webservice throws an error on: http://otherdomain.com/weddingvenues/search-advanced As you can see the dropdown shows 'method error -1' and in my Chrome console I see: 500 (Internal Server Error), where the client tries to GET the .asmx service, where on toptrouwen it uses POST (which is as I believe what's supposed to happen and also more secure).

This is the GetCountries webservice:

<System.Web.Script.Services.ScriptService()> _
<System.Web.Services.WebService(Namespace:="http://tempuri.org/")> _
<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
<ToolboxItem(False)> _
Public Class geolocation
'<System.Web.Script.Services.ScriptService()> _
'<WebService(Namespace:="http://tempuri.org/")> _
'<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
'<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _


Inherits System.Web.Services.WebService

<WebMethod()> _
Public Function GetCountries(ByVal knownCategoryValues As String, ByVal category As String) As CascadingDropDownNameValue()
    Dim values As New List(Of CascadingDropDownNameValue)

    Dim myConnection As SqlConnection = GetConnection()
    Dim cmd As New SqlCommand(String.Format("SELECT id,name as title FROM country order by title asc", Threading.Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName), myConnection)
    Try
        myConnection.Open()
        Dim reader As SqlDataReader = cmd.ExecuteReader
        Dim CountryName As String
        Dim CountryID As Integer
        While reader.Read
            CountryName = reader("title").ToString
            Int32.TryParse(reader("id"), CountryID)
            values.Add(New CascadingDropDownNameValue(CountryName, CountryID.ToString))
        End While
    Catch ex As Exception

    Finally
        myConnection.Close()
    End Try

    Return values.ToArray
End Function

End Class   

First I tried adding this to my web.config:

<system.web>
<webServices>
  <protocols>
    <remove name="Documentation"/>
    <add name="HttpGet"/>
    <add name="HttpPost"/>
  </protocols>
</webServices>
</system.web>

After doing that, I receiving this in my Chrome console:

Uncaught SyntaxError: Unexpected token < 

Where apparently the result was not interpreted as XML, but my guess is JSON. After some Google searches I believed this had to do with the MIME type, but I never found out how to change that to XML for this service.

So I continued searching and found something else, I was reading these posts: http://social.msdn.microsoft.com/forums/en-us/asmxandxml/thread/F80BDA62-C87A-4BDA-8CB1-F2CFAD1C8891 Uncaught SyntaxError: Unexpected token < -- in jQuery ajax

Where apparently it might be a 'cross-domain issue'.

So I ended up with creating these files:

clientaccesspolicy.xml

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
<policy>
  <allow-from http-request-headers="*">
    <domain uri="*"/>
  </allow-from>
  <grant-to>
    <resource path="/" include-subpaths="true"/>
  </grant-to>
</policy>
  </cross-domain-access>
</access-policy>

crossdomain.xml

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<?xml version="1.0" ?> 
<cross-domain-policy>
    <allow-access-from domain="*" /> 
    <allow-access-from domain="*.otherdomain.com" secure="false" /> 
</cross-domain-policy>

<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
            <binding name="GetCountries" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"> 
                <security mode="None" />
            </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://www.mydomain.com/geolocation.asmx"
            binding="basicHttpBinding" name="GeoLocation" />
        </client>
    </system.serviceModel>
</configuration> 

In the first example link that user also added attributes bindingConfiguration="DashboardServiceSoap" and contract="DashboardService.DashboardServiceSoap", but I have no idea what I would have to fill in there for my case.

I'm still stuck, I don't know what is the right track and how to configure my setup.

UPDATE 21-06-2013

Updated my web.config with:

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

I also tried the following 4 configurations:

<System.Web.Script.Services.ScriptService()> _
<System.Web.Services.WebService(Namespace:="http://tempuri.org/")> _
<System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
<ToolboxItem(False)> _
Public Class geolocation

    Inherits System.Web.Services.WebService

Scenario 1 and 2 With this method definition:

<WebMethod()> _
Public Function GetCountries(ByVal knownCategoryValues As String, ByVal category As String) As CascadingDropDownNameValue() 

Scenario 1: WITH protocols section in web.config

<webServices>
  <protocols>
    <remove name="Documentation"/>
    <add name="HttpGet"/>
    <add name="HttpPost"/>
  </protocols>
</webServices>  

Works correct on .nl domain Throws method error -1 on .com domain. Chrome Console shows: Uncaught SyntaxError: Unexpected token < GetCountries:1

Scenario 2: WITHOUT protocols section in web.config

Works correct on .nl domain Throws method error -1 on .com domain. Chrome Console shows: GET http://www.otherdomain.com/geolocation.asmx/GetCountries?knownCategoryValues=%22%22&category=%22Country%22&callback=Sys._jsonp0 500 (Internal Server Error) ScriptResource.axd:7773

Scenario 3 and 4 with this method definition:

<WebMethod()> _
<ScriptMethod(UseHttpGet:=True, ResponseFormat:=System.ServiceModel.Web.WebMessageFormat.Json)> _
Public Function GetCountries(ByVal knownCategoryValues As String, ByVal category As String) As CascadingDropDownNameValue() 

Scenario 3: WITH protocols section in web.config

<webServices>
  <protocols>
    <remove name="Documentation"/>
    <add name="HttpGet"/>
    <add name="HttpPost"/>
  </protocols>
</webServices>  

Throws method error 500 on .nl domain. Chrome Console shows: POST http://www.mydomain.com/geolocation.asmx/GetCountries 500 (Internal Server Error) catcher.js:197 Throws method error -1 on .com domain in dropdown. Chrome Console shows: Uncaught SyntaxError: Unexpected token < GetCountries:1

Scenario 4: WITHOUT protocols section in web.config

Throws method error 500 on .nl domain. Chrome Console shows: Failed to load resource: the server responded with a status of 500 (Internal Server Error) Throws method error -1 on .com domain in dropdown. Chrome Console shows: GET http://www.otherdomain.com/geolocation.asmx/GetCountries?knownCategoryValues=%22%22&category=%22Country%22&callback=Sys._jsonp0 500 (Internal Server Error)

Also I'm not explicity calling the .asmx from script, I let the cascading dropdown do that work for me. Like so:

<asp:DropDownList ID="ddlCountries" CssClass="textbox" AutoPostBack="true" runat="server"></asp:DropDownList>
<cc1:cascadingdropdown ID="cddCountries" runat="server" Category="Country" Enabled="True" LoadingText="<%$Resources:Glossary,loading %>" PromptText="<%$Resources:Glossary,country_choose %>" 
ServiceMethod="GetCountries" TargetControlID="ddlCountries">
</cc1:cascadingdropdown>

code-behind

cddCountries.ServicePath = "http://www.mydomain.com/geolocation.asmx"

I don't know if the fact that I'm using these pre-defined elements have anything to do with my issue, and I could better call the .asmx service via script myself and fill the dropdowns. If so: I have no idea how to do so.

Community
  • 1
  • 1
Adam
  • 6,041
  • 36
  • 120
  • 208

1 Answers1

1

You are correct that this is a cross-origin problem. There are several ways of handling this:

  1. You can turn your web service into JSONP as long as the data that needs to go to the web service is not terribly large. The data coming from the service can be as large as you like though. By not terribly large, it must be about 2k characters or less - you can calculate the amount of data that can be sent in one JSONP request by knowing that it is sent as part of the get request from the src attribute of the script tag.

    Here's a good SO answer on JSONP with which you may already be familiar:

    What is JSONP all about?

    UPDATE

    Here's an example of doing JSONP in VB.NET:

    http://www.sattsoft.com/tutorials/contents/1/14/cross-domain-call-using-ajax-jquery-jsonp-and-vb-net-web-service.html

  2. You can create a sub-domain of www.wunderweddings.com, call it perhaps "api.www.wunderweddings.com" and use DNS to point that subdomain to the right place using either an A or CNAME record. Then you would embed a tiny (invisible) iframe into your client-side page that would point to this new api host (be sure to specify the src as "//api.www.underweddings.com" so as to match http/s of the containing page), and use javascript within the iframe to promote its document.domain to www.wunderweddings.com which you may do through script injection but its easier to just have that page on the server provide the script to do it, then you can communicate freely between the iframe which points to your api and the page containing the iframe which is at www.wunderweddings.com. So code inside the iframe would go access the web service for you, get the data, promote its document.domain, and notify the containing page.

  3. If you know postMessage is always available on your clients (probably not though) you can do the above without changing the document.domain.

  4. Points 2 and 3 above probably sound like a hassle! Especially if you intend to expand the web services you offer and/or the number of domains accessing that service. If so, I would very highly recommend utlizing EasyXDM, it is a wonderful and quite powerful library for doing client-side cross-domain RPC:

    http://easyxdm.net/wp/

    EasyXDM provides fallbacks from postMessage if it isn't available such as communication through the hash or the name attribute, among a few other things.

  5. You can fix the crossdomain.xml. Now here's where I'm a little rusty but I'll give you my best guess:

UPDATE

You want your crossdomain.xml to like this:

<?xml version="1.0" ?> 
<cross-domain-policy>
    <allow-access-from domain="*" /> 
    <allow-access-from domain="*.wunderweddings.com" /> 
</cross-domain-policy>

The first child of the "<cross-domain-policy>", that is "<allow-access-from domain="" />" will make it completely unrestricted whereas "<allow-access-from domain=".wunderweddings.com" />" will make it so only wunderweddings.com and subdomains will be allowed to do a crossdomain call by the browser. You don't need both "allow-access-from" tags, but at least one of them.

I'm not sure why that configuration stuff is in there, it shouldn't be. I completely failed noticing that the first time around, that is almost certainly your problem. Also make sure that crossdomain.xml is being served from the other server, the one with the web service.

So just to clarify, that crossdomain.xml should not have that extra XML at the bottom, the <onfiguration>...</configuration> tags and everything inside of it, all of that is leaking in from someplace and shouldn't be inside crossdomain.xml


FINAL UPDATE

For those reading this answer who have a similar problem, Floran discovered the problem with the invalid character:

this had to be added to the top of the page:

<asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server"> 
<Services> 
<asp:ServiceReference Path="geolocation.asmx" /> 
</Services> 
</asp:ScriptManagerProxy>

Community
  • 1
  • 1
Matt Mullens
  • 2,266
  • 15
  • 14
  • Suggestion 1, 4 and 5 seem interesting: Suggestion 1: how do I convert my webservice to JSONP? I see a lot of example where a piece of AJAX code is added to a page, but I don't have that AJAX call currently since I believe it's all handled by the default .NET control cascading dropdown...should I override/replace that? I find it weird that my requirement is not possible out-of-the-box. Suggestion 4: Seems a bit too much work for now. Suggestion 5: I tried this, but does not fix the issue, still get the "Unexpected token <" error. – Adam Jun 20 '13 at 14:01
  • First thing, I'd try to track down that error. Get the page back to the state prior to the error if you have source control for example. Check inside the vbhtml file (I assume it is vb as well) to make sure you don't have mismatched quotes, inside script tags or included javascript. The reason there isn't an out-of-the-box solution is due to the browser security policies. These have been around for quite awhile, but CORS (Cross-Origin Resource Sharing) should help, though support is not complete or missing in some older browsers (http://en.wikipedia.org/wiki/Cross-origin_resource_sharing). – Matt Mullens Jun 20 '13 at 14:50
  • Then when its in that state try #5 as a quick fix. Using crossdomain.xml should actually work just fine, I'll update the answer in a few minutes with a working crossdomain.xml example. To convert your web service to JSONP you can check these resources: http://www.sattsoft.com/tutorials/contents/1/14/cross-domain-call-using-ajax-jquery-jsonp-and-vb-net-web-service.html and http://stackoverflow.com/questions/17107161/access-asmx-webservice-cross-domain-and-load-results-into-cascading-dropdown/17213675?noredirect=1#comment24938737_17213675 – Matt Mullens Jun 20 '13 at 14:53
  • Ok, thank you, I'll wait for that. ps. Your 2nd link in your last comment points to the current question... – Adam Jun 20 '13 at 15:02
  • Aha, I think I know the issue - just updated the answer with the change to your crossdomain.xml. Oops! And now I've lost the link that I meant to have, when I find it again I'll link it. Can't edit that comment any longer unfortunately. – Matt Mullens Jun 20 '13 at 15:08
  • One thing, make sure all your cache is cleared etc. It looks like it can be cached and you want to make sure you start with a fresh slate with respect to the browser when changes are made. – Matt Mullens Jun 20 '13 at 16:02
  • For some reason that configuration stuff in the crossdomain.xml file got in there again... I'll do some investigation, perhaps it is leaking in there from your web.config somehow? Seems totally strange, but there must be an explanation. I am taking a look... curl -O http://www.toptrouwen.nl/crossdomain.xml - hope you don't mind! – Matt Mullens Jun 20 '13 at 16:08
  • Also, here's a link to the specification for crossdomain.xml. It shows the DTD bottom of page 5 and top of page 6, so there's definitely something going on with that extra XML getting added... http://www.adobe.com/devnet-docs/acrobatetk/tools/AppSec/CrossDomain_PolicyFile_Specification.pdf – Matt Mullens Jun 20 '13 at 16:11
  • Ah, I bet there is a transform taking place that is misconfigured and somehow that web.config stuff is leaking in from there. This link doesn't state the same issue you have, but using transforms they are taking information from places such as the web.config and placing it into the crossdomain.xml, so I wonder if that is how it is getting in there: http://stackoverflow.com/questions/9745632/transforming-crossdomain-xml-on-publish-in-asp-net-web-application?lq=1 – Matt Mullens Jun 20 '13 at 16:17
  • Last comment here, don't want to flood your inbox, but here's another link that describes the settings in web.config and relationship with crossdomain.xml in ASP.NET, it may help straighten some things out: http://encosia.com/using-cors-to-access-asp-net-services-across-domains/ – Matt Mullens Jun 20 '13 at 16:24
  • I don't mind the flooding, as long as this gets resolved :) I checked your links, did some tests and updated my post with the results. Still doesn't work :( – Adam Jun 21 '13 at 09:56
  • When I retrieve the crossdomain.xml I still see a bunch of XML in it that doesn't belong there - I still see the ... ; None of that should be in the crossdomain.xml, I still am puzzled how it is getting in there. Perhaps try to completely overwrite that crossdomain.xml, get rid of it on the server that you are working on and get rid of it anywhere you can find it, then place a brand new one with only the stuff XML in the example I show above, then watch that file closely and see when it changes. – Matt Mullens Jun 21 '13 at 12:38
  • whoops...forgot to upload the latest and greatest file. It's there now (please check), but still the same errors occur. – Adam Jun 21 '13 at 13:14
  • Ok, the crossdomain.xml is good, you could probably choose one or the other allow-access-from tags, probably don't need both but that should work just fine. So when I go to the advanced search on wunderweddings.com, I see that it is requesting "http://www.wunderweddings.com/geolocation.asmx/GetCountries?knownCategoryValues=%22%22&category=%22Country%22&callback=Sys._jsonp0" I was under the impression that the GetCountries service was at toptrouwen.nl, it looks like it is attempting to access the service on the wrong server, is that correct? – Matt Mullens Jun 21 '13 at 13:25
  • I notice stackoverflow has a chat option, would you like to try and use that? – Matt Mullens Jun 21 '13 at 13:31
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/32156/discussion-between-floran-and-hoonto) – Adam Jun 21 '13 at 13:32
  • You are not going to believe this...it stopped working again :S – Adam Jun 21 '13 at 15:30
  • That is so odd, different error or same one? I notice the crossdomain.xml is still in a good state, so that's not the problem. – Matt Mullens Jun 21 '13 at 15:32