Engineer in Tokyo

Django redirect_to はnon-ascii URLに対応してない

Django は一般的なリダイレクトするビューを django.views.generic.simple.redirect_to に用意していますけど、unicodeのキーワードがあれば、動かないのが最近見つけた。

Djangoプロジェクトのurls.pyでこういう風にURLの設定を書けます。

from django.conf.urls.defaults import *

urlpatterns = patterns('django.views.generic.simple',
    url(r'^jp/(?P<tag_name>.+)\;', 'redirect_to', {'url': '/jp/tag/%(tag_name)s'}),
)

そうした場合、redirect_to は tag_nameに入ったデータをリダイレクト先にURLに入れてくれます。が、そのデータはasciiでない場合、redirect_to の中にtag_nameのデータに入れたURLを urlquoteに特に渡すなど、特に何もしないで、HttpResponseRedirectに渡す。redirect_toはDjango 1.1でこの通り

def redirect_to(request, url, permanent=True, **kwargs):
    """
    Redirect to a given URL.

    The given url may contain dict-style string formatting, which will be
    interpolated against the params in the URL.  For example, to redirect from
    ``/foo/<id>/`` to ``/bar/<id>/``, you could use the following URLconf::

        urlpatterns = patterns('',
            ('^foo/(?P<id>\d+)/$', 'django.views.generic.simple.redirect_to', {'url' : '/bar/%(id)s/'}),
        )

    If the given url is ``None``, a HttpResponseGone (410) will be issued.

    If the ``permanent`` argument is False, then the response will have a 302
    HTTP status code. Otherwise, the status code will be 301.
    """
    if url is not None:
        klass = permanent and HttpResponsePermanentRedirect or HttpResponseRedirect
        return klass(url % kwargs)
    else:
        return HttpResponseGone()

最終的に、この問題が起こる時に、このエラーが出る。

UnicodeEncodeError: 'ascii' codec can't encode characters in position 8-11: ordinal not in
  range(128), HTTP response headers must be in US-ASCII format

実際は下バグが既にあって、HttpResponseRedirectの中で、asciiでないURLをちゃんとエンコードするはず。

#11522 Crash on redirect to a relative URL if request.path is unicode