четверг, 3 июля 2008 г.

Redirect Browser to Custom URL with HTTP POST Method

Recently I've investigated how to redirect browser to custom URL using HTTP POST method.

This problem is not new, many people looked for the solution. The one I've come up with my colleague, Yury Povhanych, is a bit different from others. But inly in details, not in idea.

In order to be more specific let me describe the case in more detail. Now our company is building application for Facebook. Well, there are two ways to build applications for Facebook:
  • use FBML and Facebook-specific JavaScript
  • implement IFrame-based application
For a number of reasons we've gone the second way and our application is being displayed as internal frame.

Now, we want to get use of Facebook's friend selection system, i.e. use similar UI to select friends and send them message or request. There are specific controls which help to achieve this goal, but they are FBML-based, so they are of no real value in our case. Basically, Facebook developed a solution for this situation. They have a page called Multi Friend Selector. This page is hosted on Facebook. In order to use the page, it should be fed with parameters. Some of these parameters are messages to be displayed on the selector itself, others represent application API key and call signature. All these staff may be the part of URL, but this is BAD for at least two reasons:
  • URL may become long, thus it may be handled badly by some browsers
  • URL is bloated with data that should not be seen so easily by the user
In order to resolve that POST request must be sent instead of GET. Parameters in POST request become the part of the request body, not the URL.

First thing I wanted to know is whether I can achieve such redirection by using only server-side capabilities. In Java EE stack I've found two methods which deal with redirection: request.forward and response.sendRedirect() but both of them has nothing to do with POST. First one is used to forward the request within the same servlet container while sendRedirect can be used to perform GET based redirection to any URL, both internal and external.

Well, there is no way to do it in pure server-side. It is because of redirection nature, which is implemented in the following way. When server wants to say browser "User is boring, send him to xxx site", it means that HTTP response status code must be set to 302 (which means "Moved Temporarily") and URL of "xxx site" must be specified under "Location" header entry in that response. "Location" is just a string. It doesn't have some POST-specific format. Pure URL, nothing more. Browser should open "xxx site" and will use GET method in order to achieve that.

Solution for the problem is easy: use DOM forms, on the client side. Forms may be given a method to be used for request sending, including POST. Forms may be created dynamically, like any other DOM element. So, what you need to do, is:
  1. Create form
  2. Set URL to send request to in action parameter
  3. Set HTTP method in method parameter
  4. Optionally specify target
  5. Insert hidden input elements inside the form, give them the name and values which correspond to parameters to be sent via POST
  6. Make form the part of DOM by appending it to one of the DOM nodes
  7. Submit the form
  8. Destroy the form by
I've implemented this functionality as the part of my open-source framework, called Digr2iD. Here is the code that does the trick, dynamically (comments are removed):

Digr2iD.Controller.PostRedirector = {

go: function(url, parameters, target) {
if (!url) {
return;
}

var form = document.createElement("form");
form.id = "org_flushpongle_digr2id_js_controller_postredirector_form";
form.name = form.id;
form.action = url;
form.method = "post";

if (!target) {
target = "_self";
}
form.target = target;

document.body.appendChild(form);

for (var parameterName in parameters) {
var input = document.createElement("input");
input.name = parameterName;
input.value = parameters[parameterName];
input.type = "hidden";
form.appendChild(input);
}

form.submit();

document.body.removeChild(form);
}

};

Комментариев нет: