Canada is a bilingual country and Canadian websites usually support both English and French languages. Lately, I experienced a problem in my implementation of a language switch feature (if the website is currently in English, switch to french, and vice versa). I thought it is a great example of showing the difference between returning http 301 (redirect permanent) and http 302 (redirect) status codes in an ASP.Net MVC application. The following code snippet shows the action methods which switches the language by changing the "Language" cookie value and returns to referrer url:
public ActionResult SwitchLanguage() {
string redirectUrl = string.Empty;
redirectUrl = Request.UrlReferrer != null ? Request.UrlReferrer.ToString() : "~/";
HttpCookie languageCookie = GetCurrentLanguage();
languageCookie.Value =
languageCookie.Value == Language.English.ToString("G") ?
Language.French.ToString("G") :
Language.English.ToString("G");
HttpContext.Response.Cookies.Set(languageCookie);
return RedirectPermanent(redirectUrl);
}
Now if we run this code in Google Chrome (happens to be the first browser I was testing this application on), the language changes every time we click the "Switch Language" link. But if we try in Firefox or IE we can see the problem. The very first time we switch the language it changes the language. But it fails the consecutive switches. The problem is, once the first switch triggered, server sends a 301 message indicating this resource has been permanently moved to a new location. Future requests will use the destination route and will no longer execute SwitchLanguage action method. A close look at the rfc2616 specification reveals the fundamental bug this code has when it is returning to the referrer url:
301 Moved Permanently: The requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs.
and
302 Found: The requested resource resides temporarily under a different URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests.
Obviously, the following change will fix the bug.
return Redirect(redirectUrl);
You can find the full solution here.