简体   繁体   English

jQuery $.ajax 调用 WCF 服务返回 400 错误请求

[英]jQuery $.ajax call to WCF service returns 400 Bad Request

(Update at end) (最后更新)

I'm working on an idea using unfamiliar technology.我正在使用不熟悉的技术研究一个想法。 I've written a few WCF services, but I've never done any advanced configuration.我已经写了一些 WCF 服务,但我从未做过任何高级配置。 This is my first dive into jQuery.这是我第一次深入了解 jQuery。 The premise is I create a WCF service to get branch information, to be retrieved by jQuery.前提是我创建了一个 WCF 服务来获取分支信息,由 jQuery 检索。

My first search yielded this page: http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx#2 which I'm using as the base of my code.我的第一次搜索产生了这个页面: http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx#2 ,我将其用作代码的基础。 I initially started off as a cross-site setup, which I got rid of to see if I could just get the thing working.我最初是作为一个跨站点设置开始的,我摆脱了它,看看我是否能让这个东西正常工作。 I've searched stack overflow and none of the posts resolve my 400 Bad Request issue.我搜索了堆栈溢出,但没有一篇文章解决了我的 400 Bad Request 问题。

Code from my web.config:我的 web.config 中的代码:

<system.serviceModel>
<behaviors>
  <serviceBehaviors>
    <behavior name="GeoDataBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
    <behavior name="">
      <serviceMetadata httpGetEnabled="true" />
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
    <behavior name="GDEPBehavior">
      <webHttp />
    </behavior>
  </endpointBehaviors>
</behaviors>
<bindings>
  <webHttpBinding>
    <binding name="GDBinding" crossDomainScriptAccessEnabled="true"/>
  </webHttpBinding>
</bindings>
<services>
  <service behaviorConfiguration="GeoDataBehavior" name="GeoDataService">
    <endpoint address="" 
              binding="webHttpBinding" contract="IGeoDataService"
               behaviorConfiguration="GDEPBehavior"/>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>
</services>

Code from my interface:我的界面中的代码:

[ServiceContract]
public interface IGeoDataService
{
    [OperationContract]
    [WebInvoke(Method = "POST",
        BodyStyle = WebMessageBodyStyle.Wrapped,
        ResponseFormat = WebMessageFormat.Json)]
    List<BranchData> GetBranches();
}


// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class BranchData
{
    [DataMember]
    public string BranchNumber { get; set; }

    [DataMember]
    public string BranchName { get; set; }

    [DataMember]
    public string StreetAddress { get; set; }

    [DataMember]
    public string City { get; set; }

    [DataMember]
    public string Zip { get; set; }

    [DataMember]
    public string State { get; set; }

    [DataMember]
    public string Phone { get; set; }

    [DataMember]
    public string County { get; set; }
}

jQuery script: jQuery 脚本:

 <script type="text/javascript" language="javascript" src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.6.1.js">
</script>
<script type="text/javascript" language="javascript">
    /* help from http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx
    */
    var varType;
    var varUrl;
    var varData;
    var varContentType;
    var varDataType;
    var varProcessData;

    function CallService() {
        // Thank you Bing: http://blueonionsoftware.com/blog.aspx?p=03aff202-4198-4606-b9d6-686fd13697ee
        jQuery.support.cors = true;


        $.ajax({
            type: varType,
            url: varUrl,
            data: null,
            crossDomain: true,
            contentType: varContentType,
            dataType: varDataType,
            processdata: varProcessData,
            success: function (msg) {
                ServiceSucceeded(msg);
            },
            error: ServiceFailed
        });

        /*
        $.getJSON(varUrl, null, function (msg) {
            ServiceSucceeded(msg);
        });
        */
    }

    function GetBranchDataJson() {
        varType = "POST";
        varUrl = "GeoDataService.svc/GetBranches";
        varData = "";
        varContentType = "application/json; charset=utf-8";
        varDataType = "json";
        varProcessData = true;
        CallService();
    }

    function ServiceSucceeded(result) {
        var ddlResult = document.getElementById("ddlResult");
        for (var j = ddlResult.options.length - 1; j >= 0; j--) { ddlResult.remove(j); }

        for (var i = 0; i < result.length; i++) {
            var opt = document.createElement("option");
            opt.text = result[i].BranchName;
            ddlResult.options.add(opt);
        }
    }

    function ServiceFailed(jqXHR, errorType, errorThrown) {
        alert('error!\n' + jqXHR + '\n' + errorType + '\n' + errorThrown);
    }

</script>
<input name="WTF" type="button" onclick="GetBranchDataJson()" />

You'll note I'm using jQuery 1.6.1, not 1.3 from the tutorial.您会注意到我使用的是 jQuery 1.6.1,而不是教程中的 1.3。 The tutorial runs fine on my box and does everything as expected.该教程在我的盒子上运行良好,并且按预期完成了所有工作。 Unfortunately, my code does not.不幸的是,我的代码没有。 I appreciate any help y'all can provide.我很感激你们能提供的任何帮助。

Oh, and here's a copy of the request from Fiddler:哦,这是 Fiddler 的请求副本:

POST http://localhost:16062/GeoDataService.svc/GetBranches HTTP/1.1
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/json; charset=utf-8
Referer: http://localhost:16062/Default.aspx
Accept-Language: en-us
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Host: localhost:16062
Content-Length: 0
Connection: Keep-Alive
Pragma: no-cache

Update: Ok, I passed "{}" as the Data query (apparently this is the right way to pass nothing to a method that does not take parameters), and I now get Unsupported Media Type.更新:好的,我将“{}”作为数据查询传递(显然这是向不带参数的方法传递任何内容的正确方法),现在我得到了不受支持的媒体类型。 And the trace exception is: System.ServiceModel.ProtocolException: Content Type application/json;并且跟踪异常是: System.ServiceModel.ProtocolException: Content Type application/json; charset=utf-8 was sent to a service expecting text/xml; charset=utf-8 被发送到需要 text/xml 的服务; charset=utf-8.字符集=utf-8。

The call by itself doesn't seem to have any problems - you should try to enable tracing to see why WCF is considering the incoming request to be bad.调用本身似乎没有任何问题 - 您应该尝试启用跟踪以查看为什么 WCF 认为传入请求是错误的。 I tried a similar code as the one you have (see below) and it worked just fine.我尝试了一个与您所拥有的代码类似的代码(见下文),它工作得很好。 Also, since the request is coming from the same domain (localhost:16062) as the service, you don't have any cross-domain problems.此外,由于请求来自与服务相同的域 (localhost:16062),因此您不会遇到任何跨域问题。

Update: solution based on the comment thread on the question更新:基于问题评论线程的解决方案

The "name" attribute of the <service> element in the web.config must match the fully-qualified name (ie, the namespace + the name) of the service class (ie, the same value used in the.svc file). web.config 中 <service> 元素的“name”属性必须与服务 class 的完全限定名称(即命名空间 + 名称)匹配(即与 .svc 文件中使用的值相同)。 Otherwise you'll get a default endpoint added for your service which may or may not be what you want - by default you get a BasicHttpBinding endpoint, which is not what you wanted in your case.否则,您将获得为您的服务添加的默认端点,这可能是您想要的,也可能不是您想要的 - 默认情况下,您会获得一个 BasicHttpBinding 端点,这不是您想要的。

This problem is un unfortunate side effect of a feature added in .NET Framework 4.0: Simplified Configuration .这个问题是 .NET Framework 4.0: Simplified Configuration中添加的功能的不幸副作用。 Until .NET 3.5, every service needed to have an entry on web.config to configure it, and the config files for even the simplest applications (ie, hello world) were big.在 .NET 3.5 之前,每个服务都需要在 web.config 上有一个条目来配置它,即使是最简单的应用程序(即 hello world)的配置文件也很大。 So what happened is that, since 4.0, if WCF doesn't find a service element with a name which matches the fully-qualified name of the service, it will happily think that you want to use the default configuration.所以发生的事情是,从 4.0 开始,如果 WCF 没有找到名称与服务的完全限定名称匹配的服务元素,它会很高兴地认为您要使用默认配置。 That's why it happens to "work" with the WcfTestClient at first.这就是为什么它一开始碰巧与 WcfTestClient 一起“工作”的原因。

public class StackOverflow_6526659
{
    [ServiceContract]
    public interface IGeoDataService
    {
        [OperationContract]
        [WebInvoke(Method = "POST",
            BodyStyle = WebMessageBodyStyle.Wrapped,
            ResponseFormat = WebMessageFormat.Json)]
        List<BranchData> GetBranches();
    }

    public class Service : IGeoDataService
    {
        public List<BranchData> GetBranches()
        {
            return new List<BranchData>();
        }
    }

    // Use a data contract as illustrated in the sample below to add composite types to service operations.
    [DataContract]
    public class BranchData
    {
        [DataMember]
        public string BranchNumber { get; set; }

        [DataMember]
        public string BranchName { get; set; }

        [DataMember]
        public string StreetAddress { get; set; }

        [DataMember]
        public string City { get; set; }

        [DataMember]
        public string Zip { get; set; }

        [DataMember]
        public string State { get; set; }

        [DataMember]
        public string Phone { get; set; }

        [DataMember]
        public string County { get; set; }
    }

    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        WebHttpBinding binding = new WebHttpBinding { CrossDomainScriptAccessEnabled = true };
        WebHttpBehavior behavior = new WebHttpBehavior();
        host.AddServiceEndpoint(typeof(IGeoDataService), binding, "").Behaviors.Add(behavior);
        host.Open();
        Console.WriteLine("Host opened");

        HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "/GetBranches");
        req.Method = "POST";
        req.GetRequestStream().Close();
        HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
        Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
        foreach (var header in resp.Headers.AllKeys)
        {
            Console.WriteLine("{0}: {1}", header, resp.Headers[header]);
        }
        if (resp.ContentLength > 0)
        {
            Console.WriteLine(new StreamReader(resp.GetResponseStream()).ReadToEnd());
        }

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM