8/08/2009

Using the load method of JQuery to load the selected data from the response

I recently had a requirement where in I had to display documents from a Knowledge management repository in an unordered list items view. There was already a standard server side component which was displaying the documents but not in the format required and I had no control over this component to change it (This is the case for most of us who are working on customizations for some standard product). There were two options for me to implement this requirement.

  • To implement a new server side component which retrieves the data in the format required (or)
  • Use the same server side component, retrieve the data using the Javascript and asynchronous technique and display the partial data (in this case, bunch of links holding a specific class) and decorate these links with an unordered list.

I instantly chose the second option. There were quite a good number of reasons for choosing this option, I would not like to get into those details in this article though. I was implementing the JQuery in my applications for quite some time I found the very good use of "load()" method to display the selected elements from the response of an asynchronous call to the server. And it turned out to be as simple as we talk. Here I am drafting the steps for implementing this.

  1. Create a div in the HTML to hold the dynamically retrieved data from the server. And we can also define the server side URL which needs to be retrieved dynamically.

    <div id="kmContent" href="some server side URL" > </div>

  2. Write the JQuery document ready function to retrieve the contents from the URL specified in the div. In the load method, the first parameter is the URL and the element selector separated by a space and this is the reason why we need to make sure that the URL does not have any space in it. The space should be escaped. But it's better to use the escape function of Javascript to escape the URL for safer side. And the second parameter is the function reference, which will be called by JQuery when the loading is completed.

    $(function() {

    var url = escape($("#kmContent").attr("href"));

    $("#kmContent").load(url +" a.someSpecificClass");

    });


    This piece of script, sends the request to the specified URL and retrieves the response and selects only "a" elements with the specified CSS class "someSpecifiedClass" and inserts that into "kmContent" div.

  3. The next and ofcourse the last step is to define a callback function, which will be called when the loading is complete. This callback function is responsible for decorating the links with unordered list.

    $("#kmContent").load(url +" a.someSpecificClass", function() {

    $('#kmContent a').wrapAll('<ul></ul>').wrap("<li></li>").addClass("someFormattingClass");

    });



    And I placed this HTML with the script as part of my content management system and there you go. JQuery does the job perfectly well.

    In this age of agile development, the frameworks like JQuery are the best fit without any doubt.

2/03/2009

WebDAV MOVE method fails with 502 Bad Gateway Error with HTTPS

I was recently writing a Javascript piece of code to move a bunch files from one location to another in the same WebDAV repository. It worked all well in the lower systems but when I tried the same thing in the Production environment I got the "502 Bad Gateway" exception. I thought probably I was doing some mistake and I tried to use the WebDAV Javascript Ajax API provided in http://www.webdavsystem.com/ajax. But it also suffered the same issue.

Here is the piece of Javascript code to do the simple move.


function doResourceMove(from, to) {
var postParams = '<?xml version="1.0"?gt;<propertybehavior xmlns="DAV:"><keepalive>*</keepalive></propertybehavior>';
var xhr = getXHR(); //gets the HTTP Request object
xhr.open("MOVE",from, false);
xhr.setRequestHeader("Destination",to);
xhr.setRequestHeader("Overwrite","F");
xhr.setRequestHeader("Content-Type",'text/xml; charset="utf-8"');
xhr.send(postParams);

var status = xhr.status;
if(status == 201 status == 204 )
return true;
else {
throw status; //can be 201, 204, 403, 409, 412, 423, 502
}
}

After trying WebDAV Ajax API provided by webdavsystems, it was time for me to roll my sleeves up and analyze the issue.

The main issue was that the production system runs on https and there was a reverse proxy setup for this system. So, all the "https" requests were converted to "http" at this proxy level and forwarded to the main system. This was the main culprit.

Here is an example of HTTP request for the MOVE resource request for a WebDAV resource. For brevity I removed all unnecessary details.

MOVE contentLocation //request line, some https location, URI of webDAV resource
Destination:destinationLocation // this is the HTTP request header, should be absolute URI according to specifications.

Overwrite: "F" // this is also a HTTP request header

So, when the reverse proxy sees the request line, it knows that it has to convert this to HTTP request but the header Destionation also contains an HTTPS request which would be ignored by the proxy. So, when the request reached the server, we are moving the resource from an URI which begins with a http to a URI which begins with an https. Server treats this request as a request to move a WebDAV resource from one server location to another server location.(Refer RFC: http://greenbytes.de/tech/webdav/rfc2518.html#METHOD_MOVE). This was the source of the main problem.

Solution: When I changed the destination location to use the relative URL, it just worked fine. It may not be the solution for all WebDAV based systems but for me, it just did the trick.

Example:Let's assume that the resource you want to move is located at the URL location https://www.abc.com/webdavimpl/folder1/folder2 and you want to move this resource to location https://www.abc.com/webdavimpl/folder1/folder3/folder2. Following are the important HTTP request headers to follow.

MOVE https://www.abc.com/webdavimpl/folder1/folder2

Destination: /webdavimpl/folder1/folder3/folder2

Overwrite: "F".