سؤال

I think I've read every StackOverflow post on XDomainRequest and another few dozen on AJAX and WCF, but I'm still having trouble getting an XDomainRequest AJAX call to work. I've implemented CORS ("Access-Control-Allow-Origin") on my WCF service and my code works fine with xmlHttpRequest in Chrome and Firefox, but I'm making calls cross-domain and so for IE I need to use the XDomainRequest object. My xdr works fine when I GET or POST to a method that has no args, and I can even use the GET verb successfully to a method with args using a querystring, but when I try to POST to a method with args my xdr throws an error, even though I put a breakpoint in the BeginRequest method and I see that the Response from the server is "200 OK". I'd like to think I've tried every combination of config file settings but I have to be missing something. Any help in pointing me in the right direction is greatly appreciated.

Here are the pertinent parts of my code:

WCF - Global.asax

protected void Application_BeginRequest(object sender, EventArgs e)
    {
        //for CORS
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
    }

WCF - IService1.cs

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [WebInvoke(Method = "POST")]
    string GetData();

    [OperationContract]
    [WebInvoke(Method = "POST")]
    string GetData2(string param);
}

WCF - Service1.svc

public class Service1 : IService1
{
    public string GetData()
    {
        return "Hello";
    }

    public string GetData2(string param)
    {
        return string.Format("Hello - {0}", param);

    }
}

WCF - Web.config

<?xml version="1.0"?>

<system.web>
    <compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    <services>
        <service behaviorConfiguration="WcfService1.Service1Behavior" name="WcfService1.Service1">
            <endpoint address="AjaxEndpoint" behaviorConfiguration="AjaxBehavior" contract="WcfService1.IService1" bindingConfiguration="AjaxBinding"/> 
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="WcfService1.Service1Behavior">
                <!--<serviceMetadata httpGetEnabled="true" />-->
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="AjaxBehavior">
                <enableWebScript/>
            </behavior>
        </endpointBehaviors>
    </behaviors>
    <bindings>
        <webHttpBinding>
            <binding name="AjaxBinding"/>
        </webHttpBinding>
    </bindings>
</system.serviceModel>
<system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

Client AJAX call

var WcfURL = "http://localhost/WcfService1/Service1.svc/AjaxEndpoint"
if (window.XDomainRequest) {
//IE - use cross-domain request
xdr = new XDomainRequest();
xdr.onprogress = function () { alert("onprogress: " + xdr.responseText) };
xdr.onload = function () { updateText(xdr.responseText) }; 
xdr.onerror = function () { alert("xdr error") };
xdr.timeout = 7500;
xdr.ontimeout = function () { alert("xdr timeout") };

var data = "passedInParam";
//var method = "/GetData";  //this works
var method = "/GetData2";  //this throws an error
xdr.open("POST", WcfURL + method);

xdr.send(data);

}

هل كانت مفيدة؟

المحلول

I've found the solution. Thanks to Fiddler, I was able to view more of the response I was getting back from the service. The error was

The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml', 'Json'. This can be because a WebContentTypeMapper has not been configured on the binding.

Armed with this information I started looking into the WebContentTypeMapper. I found this article useful, and after adding the WebContentTypeMapper method I was able to see that the contentType of my request coming from the XDomainRequest was of type "application/json" (as expected) when I did not include an argument in the XDomainRequest.send() method, but changed to type "application/octet-stream" when an argument was passed in. (ie. xdr.send(data)) I don't know why it changes to octet-stream, but in doing so it would cause the service method to throw a 500 error (with the message from above) and therefore cause the xdr request to error. But the WebContentTypeMapper is aptly named and using it to change the contentType is easy enough. After correcting it to type Json my xdr is working well.

Here's the method:

public class CustomContentTypeMapper : WebContentTypeMapper
{
    public override WebContentFormat GetMessageFormatForContentType(string contentType)
    {
        if (contentType == "application/octet-stream")
        {
            return WebContentFormat.Json;
        }
        else
        {
            return WebContentFormat.Default;
        }
    }
}

And here are the portions of the config file that were updated:

<endpoint address="AjaxEndpoint" behaviorConfiguration="AjaxBehaviour" contract="WcfService1.IService1" binding="customBinding" bindingConfiguration="CustomMapper"/>
...
<bindings>
        <customBinding>
            <binding name="CustomMapper">
                <webMessageEncoding webContentTypeMapperType="WcfService1.CustomContentTypeMapper, WcfService1" />
                <httpTransport manualAddressing="true" />
            </binding>
        </customBinding>
    </bindings>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top