jQuery $.ajax 调用 WCF 服务返回 400 Bad Request
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6526659/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery $.ajax call to WCF service returns 400 Bad Request
提问by Garrison Neely
(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. This is my first dive into jQuery. The premise is I create a WCF service to get branch information, to be retrieved by jQuery.
我正在使用不熟悉的技术研究一个想法。我写了一些 WCF 服务,但我从来没有做过任何高级配置。这是我第一次深入 jQuery。前提是我创建了一个 WCF 服务来获取分支信息,由 jQuery 检索。
My first search yielded this page: http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx#2which I'm using as the base of my code. 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.
我的第一次搜索产生了这个页面:http: //www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx#2,我将其用作代码的基础。我最初是作为跨站点设置开始的,我摆脱了它,看看我是否能让它工作。我搜索了堆栈溢出,但没有一篇文章解决了我的 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. 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.
您会注意到我使用的是 jQuery 1.6.1,而不是教程中的 1.3。该教程在我的盒子上运行良好,并按预期执行所有操作。不幸的是,我的代码没有。我很感激你们能提供的任何帮助。
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; charset=utf-8 was sent to a service expecting text/xml; charset=utf-8.
更新:好的,我通过“{}”作为数据查询(显然这是不向不带参数的方法传递任何内容的正确方法),现在我得到了不支持的媒体类型。跟踪异常是: System.ServiceModel.ProtocolException: Content Type application/json; charset=utf-8 被发送到需要 text/xml 的服务;字符集=utf-8。
回答by carlosfigueira
The call by itself doesn't seem to have any problems - you should try to enable tracingto see why WCF is considering the incoming request to be bad. 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.
调用本身似乎没有任何问题 - 您应该尝试启用跟踪以查看 WCF 为什么认为传入请求是错误的。我尝试了与您拥有的代码类似的代码(见下文),并且效果很好。此外,由于请求来自与服务相同的域 (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(i.e., the namespace + the name) of the service class (i.e., the same value used in the .svc file). Otherwise you'll get a default endpointadded 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.
web.config 中 <service> 元素的“name”属性必须与服务类的完全限定名称(即命名空间 + 名称)匹配(即与 .svc 文件中使用的值相同)。否则,您将为您的服务添加一个默认端点,这可能是您想要的,也可能不是您想要的 - 默认情况下,您会获得一个 BasicHttpBinding 端点,这在您的情况下不是您想要的。
This problem is un unfortunate side effect of a feature added in .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 (i.e., hello world) were big. 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. That's why it happens to "work" with the WcfTestClient at first.
此问题是 .NET Framework 4.0 中添加的一项功能的不幸副作用:简化配置。在 .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();
}
}