Today I struggled with more redirect related issues as I continued rewriting our Facebook app with Wicket - I'll note my observations here (keep in mind that I am a first-timer with Wicket).
Caveats!
- The newWebResponse() implementation below ignores the getRequestCycleSettings().getBufferResponse() value, which the original implementation takes into consideration.
- If you embed iframes to your fbml pages with the Fb:iframe tag that point to the app's callback url, you will want to "enable" the normal redirects for those requests.
Problem explained
When I submit one of my forms, my access log reveals that Wicket follows the "redirect after post" pattern.
Code Blocknoformat |
---|
"POST /appcontext/somepage?wicket:interface=:0:somepanel:someform::IFormSubmitListener:: HTTP/1.1" 200 "POST /appcontext/somepage?wicket:interface=:1:::: HTTP/1.1" 200 |
In the context of a Facebook FBML application, the second access log entry never appears, and the url in the browser remains as follows:
Code Blocknoformat |
---|
http://apps.facebook.com/myapp/somepage?wicket:interface=:0:somepanel:someform::IFormSubmitListener:: |
...
This is by Facebook's design and - the redirect issued by Wicket after the form post is simply ignored discarded and never makes it to the browser. Access log looks like this:
...
the browser is never made aware of it.
Solution explained
Facebook provides a custom tag to accomplish a redirect within the Facebook canvas - Fb:redirect (http://wiki.developers.facebook.com/index.php/Fb:redirect).
The trick is to make Wicket use the Fb:redirect tag instead of doing the "normal" redirect.
org.apache.wicket.protocol.http.WebResponse
If you check out the source for org.apache.wicket.protocol.http.WebResponse, you'll find a redirect() method that contains the following line:
Code Block |
---|
httpServletResponse.sendRedirect(url);
|
This is what we wish to avoid, and instead output the Fb:redirect tag into the response body:.
FBWebResponse
Let's override the redirect() method:
Code Block |
---|
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import org.apache.wicket.protocol.http.WebResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FBWebResponse extends WebResponse
{
private static final Logger log = LoggerFactory.getLogger(FBWebResponse.class);
private final HttpServletResponse httpServletResponse;
public FBWebResponse(HttpServletResponse response)
{
super(response);
this.httpServletResponse = response;
}
@Override
public void redirect(String url)
{
if (!redirect)
{
if (httpServletResponse != null)
{
// encode to make sure no caller forgot this
url = httpServletResponse.encodeRedirectURL(url);
try
{
if (httpServletResponse.isCommitted())
{
log.error("Unable to redirect to: " + url +
", HTTP Response has already been committed.");
}
if (log.isDebugEnabled())
{
log.debug("Redirecting to " + url);
}
if (isAjax())
{
/*
* By reaching this point, make sure the HTTP response status code is set to
* 200, otherwise wicket-ajax.js will not process the Ajax-Location header
*/
httpServletResponse.addHeader("Ajax-Location", url);
// safari chokes on empty response. but perhaps this is
// not the best place?
httpServletResponse.getWriter().write(" ");
}
else
{
// httpServletResponse.sendRedirect(url);
httpServletResponse.getWriter().write("<fb:redirect url=\"" + url + "\" />");
}
redirect = true;
}
catch (IOException e)
{
log.warn("redirect to " + url + " failed: " + e.getMessage());
}
}
}
else
{
log.info("Already redirecting to an url current one ignored: " + url);
}
}
}
|
The redirect() method was a copied from WebResponse, and the following change was applied:
Code Block |
---|
// httpServletResponse.sendRedirect(url);
httpServletResponse.getWriter().write("<fb:redirect url=\"" + url + "\" />");
|
Make use of FBWebResponse in your application
In your application class, override the newWebResponse() method:
Code Block |
---|
@Override
protected WebResponse newWebResponse(HttpServletResponse servletResponse)
{
return new FBWebResponse(servletResponse);
}
|
You should be set!
A big thank you goes to jwcarman on ##wicket for helpful pointers.