1 :如果我们Post时没有任何数据传给服务端,请指定Data:{} 如:
这是因为在IIS中post请求时Content —Length是必须提供的,即使没用任何Post Data.Content-Length也应该设为0,但这样的话Jquery不会自动设置Header,除非请求中包含post Data。而Asp.net Ajax的json传输,又要求post方式,因此我们不能改变他的请求方式。简便的解决法案就是在请求中给定一个空的json 对象。以符合IIS的要求,此时Content-Length为2。这时在服务端我可以完全忽略这个为空的参数,并处理相应的请求。
2:当post data 不为空时。我们应该避免在beforeSend事件里设置RequestHeader。
如一点所述的范例post data 为空时,既然jquery不能自动设置Header,我们能否手工帮他设置呢?答案时是
肯定的。这时我们是在beforeSend事件中设置的。如代码所示(请注意:必须设置为application/json否则webservice
时不会返回json。这也是出于安全的考虑)。
Code
但时此时问题又来了,在IE中XmlHttpRequst的setRequestHeader不时直接完全的设置RequstHeader。而是
在已有的基础上附加setRequestHeader参数所指定的以形成新的header。这样的话,在jquery会在包含Post Data请求
的header中自动设置content-type为默认的application/x-www-form-urlencoded ,而在beforeSend又会被重新追
加一个application/json; charset=utf-8。此时Content-Type变成了 application/x-www-form-urlencoded,
application/json; charset=utf-8(在ff会重新设置)。很显然这个content-type不为Asp.net Ajax所接受。webservice
不会返回任何json。因此包含post data时建议使用下列方式保证返回的为json
Code
3:注意区分json对象和json形式的字符串。
看看下面代码:
Code
咋一看没什么问题,想当然的认为data会乖乖的post给服务端。但实际上这是不正确的。请仔细看此时data时json
表示的对象。此时Json表示的对象会被Jquery序列化。如上述例子data将是fname=dave&lname=ward。再看如下:
Code
这时data才是我们期望的。其形式:{'fname':'dave', 'lname':'ward'}。小小语义的改变就可以造成如此大的不同。
因此,我们在编程过程中应该特别注意。以免浪费时间。
注:有关细节可以参看3 mistakes to avoid when using jQuery with ASP.NET AJAX ,有更详细的说明。我只是
重新用中文简要的重述一下其中讲到的问题。
英文原文:http://encosia.com/2008/06/05/3-mistakes-to-avoid-when-using-jquery-with-aspnet-ajax/
3 mistakes to avoid when using jQuery with ASP.NET AJAX
By Dave Ward on June 5th, 2008Over the past few weeks, I think I have definitely embodied Jeff Atwood’s claim that we’re all amateurs, learning together. Despite my best efforts to thoroughly test before posting, a few problems slipped through in my recent posts about using jQuery to consume ASP.NET JSON serialized web services and using jQuery to call ASP.NET AJAX page methods.
On the bright side, your great feedback in both posts’ comments has reinforced the fact that some of the best content on my blog is the part that you write.
In this post, I’m going to detail three of the problems that were discovered as a result of those previous two posts:
- An extra requirement when making a read-only request to IIS6+.
- An oddity in Internet Explorer 7’s XmlHttpRequest class.
- A common mistake when passing JSON parameters through jQuery.
Finally, I’ll suggest what I now believe to be a best practice usage, taking all of these issues into account.
Security requirements when POSTing to IIS
This error message was the most common problem that was reported:
Length Required
The underlying issue is that most installations of IIS6+ require a content-length be provided with all POST requests, even if there is no content (POST data).
The content-length for a request with no data should be 0, but jQuery doesn’t set that header automatically unless there is a data parameter. Since ASP.NET AJAX’s JSON serialized services require a POST request, this becomes a stumbling block for read-only requests.
The easiest way I’ve found to work around this is to use an empty JSON object as a parameter on read-only requests. For example, if you were calling a page method:
$.ajax({ type: "POST", url: "PageMethod.aspx/PageMethodName", data: "{}", contentType: "application/json; charset=utf-8", dataType: "json" });This will cause jQuery to correctly set a content-length of 2, while your web service or page method will happily ignore the empty parameter and treat the request as read-only.
You might notice the content-type is set differently in this example than in my previous posts. More on that next:
A problem with Internet Explorer and XmlHttpRequest
Previously, to call ASP.NET AJAX services with jQuery, I suggested this usage:
$.ajax({ type: "POST", url: "WebService.asmx/WebMethodName", beforeSend: function(xhr) { xhr.setRequestHeader("Content-type", "application/json; charset=utf-8"); }, dataType: "json" });The reason for the beforeSend delegate is due a quirk in jQuery which causes it to send a specified content-type only if there is also a data parameter specified.
For security reasons, ASP.NET AJAX will not provide a JSON serialized response unless the proper content-type is provided. By using the beforeSend delegate as shown, the content-type will be manually set on the XmlHttpRequest object, before the request.
However, the new XmlHttpRequest class in Internet Explorer 7 doesn’t implement setRequestHeader very intuitively. Instead of setting the specified header, it appends the value.
This becomes a problem when you make a request that includes a data parameter. When POST data is provided, jQuery will automatically set the content-type to its default of application/x-www-form-urlencoded. In the beforeSend delegate, the required application/json; charset=utf-8 will be appended after jQuery’s default.
End result? Because this amalgamation of content-types isn’t what ASP.NET AJAX expects, the web service will not return JSON serialized content and jQuery will be unable to parse it.
Solution? If you’re sending data in your request, use jQuery’s contentType parameter to set the content-type, so that the default is never added:
$.ajax({ type: "POST", url: "WebService.asmx/WebMethodName", data: "{'fname':'dave', 'lname':'ward'}", contentType: "application/json; charset=utf-8", dataType: "json" });JSON, objects, and strings: oh my!
In requests made to them, ASP.NET AJAX script services and page methods understand and expect parameters serialized as JSON strings. These parameters are parsed out of the POST data and used as arguments to the method you’ve called.
However, if you directly provide a JSON object as the data parameter for an $.ajax call, jQuery will attempt to serialize the object instead of passing it on to your web service.
Take this sample request, for example:
$.ajax({ type: "POST", url: "WebService.asmx/WebMethodName", data: {'fname':'dave', 'lname':'ward'}, contentType: "application/json; charset=utf-8", dataType: "json" });Because that data parameter is a valid JSON object, calling the web service this way won’t throw any errors. Unfortunately, it won’t have the desired result either. Instead of passing that JSON object through to the web service, jQuery will automatically serialize and send it as:
fname=dave&lname=ward
To which, the server will respond with:
Invalid JSON primitive: fname.
This is clearly not what we want to happen. The solution is to make sure that you’re passing jQuery a string for the data parameter, like this:
$.ajax({ type: "POST", url: "WebService.asmx/WebMethodName", data: "{'fname':'dave', 'lname':'ward'}", contentType: "application/json; charset=utf-8", dataType: "json" });It’s a small change in syntax, but makes all the difference. Now, jQuery will leave our JSON object alone and pass the entire string to ASP.NET for parsing on the server side.
Best practice? I hope so this time!
Taking these three caveats into account, I think this is the best practice for calling read-only ASP.NET AJAX web services via jQuery:
$.ajax({ type: "POST", url: "ServiceName.asmx/WebMethodName", data: "{}", contentType: "application/json; charset=utf-8", dataType: "json", success: function(msg) { // Do interesting things here. } });When consuming web services that require parameters, the usage is similar. Just make sure that you’re passing a JSON object in a string:
$.ajax({ type: "POST", url: "ServiceName.asmx/WebMethodName", data: "{'fname':'dave','lname':'ward'}", contentType: "application/json; charset=utf-8", dataType: "json", success: function(msg) { // Do magic here. } });In either of these cases, substitute PageName.aspx for ServiceName.asmx and PageMethodName for WebMethodName if you want to call a page method instead of a web service.
Conclusion
I hope this post will help you to avoid these common problems when using jQuery with ASP.NET AJAX. I think there’s a great synergy between the two that is certainly worth jumping through these few hoops.
I’d especially like to thank Martin and Ryan for their invaluable feedback, leading to two of the three solutions posted above.
If you’ve run into related problems yourself, I’m eager to hear about them. Please don’t hesitate to comment if you think I’m still missing something important.