utamaro’s blog

誰かの役に立つ情報を発信するブログ

DjangoでPageNotFound時にjsonを返す方法

Djangoapiを作っている際に、自分で定義していないpathをリクエストしたところ404ページが表示されました。

apiを使うため、これをjsonで返すように変更したいと考えて、実現する方法を調べてみました。

参考にしたページ一覧

Error handling
https://docs.djangoproject.com/en/3.0/topics/http/urls/#error-handling

handler404
https://docs.djangoproject.com/en/3.0/ref/urls/#handler404

Customizing error views
https://docs.djangoproject.com/en/3.0/topics/http/views/#customizing-error-views

実現方法

まずはproject/urls.pyを以下のようにします。

from django.http import JsonResponse
from django.urls import path, include


def handle_404(request, exception):
    return JsonResponse({"status": "page not found."})

def handle_403(request, exception):
    return JsonResponse({"status": "forbidden."})

def handle_500(request):
    return JsonResponse({"status": "server error."})


urlpatterns = [
    path('api/', include('api.urls')),
]

handler403 = 'project.urls.handle_403'
handler404 = 'project.urls.handle_404'
handler500 = 'project.urls.handle_500'

この記述の中のhandlerXXXが重要な部分です。

この記述はurls.pyに書くようにドキュメントにかかれています。 また、参考にできるコードはdjango.views.defaults.page_not_foundから確認できます。

import用のパスをproject.urls.handle_403のようにしていますが、これは以下のように関数を代入しても良いです。

handler403 = handle_403
handler404 = handle_404
handler500 = handle_500

handle_404関数についてはurls.pyに書く必要はなく、別のファイルに書いても大丈夫です。

例えば、restframework用の例外ハンドラと同じ場所に書くのが良いと思います。

検証

検証する際にDEBUG = TRUEではできないのでFALSEにします。

curlを利用してリクエストします。

curl http://localhost:8000/api/hoge/piyo

結果

{"status": "page not found."}

このときのexception変数のインスタンスResolver404でした。 404でハンドリングしているため、Http404で例外が出ていると予想していたのですが間違っていました。

というかdocstringに以下のように書かれていたのでわかっていませんでした。

The message from the exception which triggered the 404 (if one was supplied), or the exception class name

なんのインスタンスなのか書いておいてほしいです。

Resolver404だったので、api内部でraise Http404としてもハンドラでキャッチされませんでした。

私の場合はdjangorestframework用の例外ハンドラを用意していたので、そちらでキャッチされました。