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.
Security requirements when POSTing to IIS
This error message was the most common problem that was reported:Length RequiredThe 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" });
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" });
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!
ASP.NET AJAX script services and page methods understand and expect input parameters to be 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 URL encode 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" });
fname=dave&lname=wardTo 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" });
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. } });
$.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. } });
No comments:
Post a Comment