【问题标题】:jQuery $.ajax call to WCF service returns 400 Bad RequestjQuery $.ajax 调用 WCF 服务返回 400 Bad Request
【发布时间】:2011-09-25 11:32:49
【问题描述】:

(最后更新)

我正在使用不熟悉的技术来研究一个想法。我编写了一些 WCF 服务,但我从未做过任何高级配置。这是我第一次深入了解 jQuery。前提是我创建了一个WCF服务来获取分支信息,供jQuery检索。

我的第一次搜索产生了这个页面:http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx#2,我将其用作代码的基础。我最初是作为一个跨站点设置开始的,我摆脱了它,看看我是否能让这个东西正常工作。我搜索了堆栈溢出,但没有一篇文章解决了我的 400 Bad Request 问题。

来自我的 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>

我的界面中的代码:

[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 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()" />

您会注意到我使用的是 jQuery 1.6.1,而不是教程中的 1.3。该教程在我的盒子上运行良好,并按预期完成所有操作。不幸的是,我的代码没有。感谢大家提供的任何帮助。

哦,这是 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

更新: 好的,我将“{}”作为数据查询传递(显然这是向不带参数的方法传递任何内容的正确方法),现在我得到了 Unsupported Media类型。并且跟踪异常是: System.ServiceModel.ProtocolException: Content Type application/json; charset=utf-8 被发送到需要 text/xml 的服务;字符集=utf-8。

【问题讨论】:

  • 所以只是为了测试,我在我的 GetBranches 方法中添加了一个参数,bool Test。我更新了 $.ajax 调用以传入数据:'{"Test": "true"}',现在我在跟踪中收到错误:Content Type application/json; charset=utf-8 被发送到需要 text/xml 的服务;字符集=utf-8。我添加了 ReceiveFormat=Json,但仍然出现错误。 web.config 中是否有我需要更改的内容?另外,是否有正确的方法将 null 传递给没有参数的方法(我原来的 GetBranches)?
  • 能否检查web.config中的服务名称与GeoDataService.svc文件中的服务名称是否一致?它们必须完全相同(即带有命名空间的类名)
  • 感谢您的想法,卡洛斯。我的命名空间是 GeoDataServices,我的服务名称是 GeoDataService。当我将 web.config 中的服务名称更改为 GeoDataServices.IGeoDataService 时,它​​会引发相同的错误(当我将其更改为 GeoDataServices.GeoDataService 时,它​​告诉我使用跟踪中的接口)。因此,任一配置都返回相同的新 ProtocolException。
  • 你需要改变service名字,不仅仅是接口,像
  • 哇,卡洛斯,你就是那个男人。如果您想将其发布为答案,我会标记它。非常感谢您的帮助!

标签: wcf jquery


【解决方案1】:

调用本身似乎没有任何问题 - 您应该尝试enable tracing 了解为什么 WCF 认为传入的请求是错误的。我尝试了与您所拥有的代码类似的代码(见下文),它工作得很好。此外,由于请求来自与服务相同的域(localhost:16062),因此您不会遇到任何跨域问题。

更新:基于问题评论线程的解决方案

web.config 元素的“name”属性必须与服务类(即.svc 文件中使用的相同值)。否则,您将获得为您的服务添加的 默认端点,这可能是也可能不是您想要的 - 默认情况下,您会获得一个 BasicHttpBinding 端点,这不是您想要的。

此问题是 .NET Framework 4.0 中添加的功能的不幸副作用:Simplified Configuration。在 .NET 3.5 之前,每个服务都需要在 web.config 上有一个条目来配置它,即使是最简单的应用程序(即 hello world)的配置文件也很大。所以发生的事情是,从 4.0 开始,如果 WCF 没有找到名称与服务的完全限定名称匹配的服务元素,它会很高兴地认为您要使用默认配置。这就是为什么它恰好与 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();
    }
}

【讨论】:

  • 感谢您的指点,卡洛斯。使用跟踪日志,出现以下错误: System.Xml.XmlException:无法读取消息正文,因为它是空的。现在,我对“原始”Web 服务调用没有太多经验(通常我会从 .NET 转到 .NET),所以我不知道这是否过于笼统的例外无法提供帮助。
  • 感谢卡洛斯的澄清。不知道关于 WCF 4.0 和 3.5 的情况。再次感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2012-05-17
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多