MENU

Django Ninjaチュートリアル17(APIドキュメント、エラーハンドリング)

Django Ninjaチュートリアル17(APIドキュメント、エラーハンドリング)

前回までの記事はこちらです。

前回までの記事が読まれている前提で書いていきますので、まだ読んでいない方はこちらから読み進めて追いついてください。

それでは始めていきましょう。

目次

APIドキュメント

OpenAPI

Django NinjaでAPIを設定して、Djangoのサーバーを起動(python manage.py runserver )したら、http://127.0.0.1:8000/api/docs にアクセスします。
自動でインタラクティブなAPIドキュメントが生成されています(これはOpenAPI/Swagger UIによって提供されているものです)。

これまで、様々APIの例を見てきた中で何度も作成した自動生成ドキュメントは、OpenAPIによるものです。

CDN と静的ファイル

Django ninjaは、Djangoのプロジェクトディレクトリにあるsettings.pyINSTALLED_APPSに記載する必要がありません。
この場合、インタラクティブUIはCDNによってホストされます。

自分のサーバーからドキュメント(Js/css) をホストするにはninjaINSTALLED_APPSに入れるだけです。
この場合、標準のdjango staticfilesメカニズムがホストします。

Redocへの切り替え

先ほど見たように、基本的にDjango NinjaではデフォルトでOpenAPIによるドキュメントを生成しますが、以下の設定をすると、OpenAPIではなく、Redocを表示することができます。

NINJA_DOCS_VIEW = 'redoc'

これまでに設定したAPIの例を見てみます。
http://127.0.0.1:8000/api/docsにアクセスすると、通常は下記のようなOpenAPIが生成されます(apiの部分は各自で設定したNinjaAPIインスタンスに変更してください)。

生成されたOpenAPI

上記のようなRedocの生成をすると、http://127.0.0.1:8000/api/docsにアクセスしても表示が以下のようになります。

設定変更後に生成されるRedoc

先ほどの設定をせずに、http://127.0.0.1:8000/api/redocにアクセスしても同じ結果は得られますが、自分の好みに合わせてデフォルトで表示されるドキュメントを変更することができます。

OpenAPIを非表示にする

これまでは当たり前のようにOpenAPIで生成されたドキュメントを見ながらチュートリアルを進めてきましたが、以下のように記述することで、ドキュメントの自動生成をしないようにすることもできます。
必要に応じて設定をしてみてください。

api = NinjaAPI(
  docs_url = None
)

ドキュメントの保護

ドキュメントの参照に認証を要求し、保護する(または他のユースケースのためにデコレートする)には、次のようにdocs_decorator引数を使用します。

from django.contrib.admin.views.decorators import staff_member_required

api = NinjaAPI(
  docs_decorator = staff_member_required
)

これにより、認証が完了していないとログインするように促されます。

以上が、Django Ninjaで生成されるREST APIの自動生成ドキュメントに関する基本的な操作でした。
OpenAPIによる自動生成ドキュメントは、非常に使い勝手が良いため、これを使用しないということは少ないかと思いますが、Redocが合う人もいれば、そうでない人など、様々ですので、必要に応じて使い分けてください。

エラーハンドリング

次に見ていくのは、Django Ninjaにおけるエラーハンドリングについてです。
Django Ninjaでは、エラーや処理された例外が発生した際に、これらのレスポンスを処理するため、カスタムされた例外処理を発生させることが許容されています。

Custom exception handlers

ある時点で利用できないように設計された外部サービスに依存するAPIを作成しているとします。
例外時にデフォルトの500エラーを投げる代わりに、エラーを処理して、クライアントに友好的な応答を返すことができます。

それを実行するには、以下のものを設定する必要があります。

  1. いくつかの例外を作成する(または既存のものを使用する)
  2. api.exception_handlerデコレーターを使用する

それでは例を見てみます(今回はDjango Ninjaの公式ドキュメントの例を使用します)。

from ninja import NinjaAPI, Form

api = NinjaAPI()

# エラー生成用の関数(中身はなし)
class ServiceUnavailableError(Exception):
    pass


# api.exception_handlerによるデコレータの作成
@api.exception_handler(ServiceUnavailableError) # 事前に生成していた関数を引き継ぐ
def service_unavailable(request, exc):
  # エラーが生じたら、メッセージとステータスコードを返す
  return api.create_response(
    request,
    {
      "message": "Please retry later"},
      status = 503,
    )

# サンプルとしてTrue, Falseをランダムで生成するだけのAPIを定義→結果を投げて例外処理を確認
@api.get("/service")
def some_operation(request):
  # True, Falseをランダムに生成
  if random.choice([True, False]):
    # Falseの場合、事前に設定したエラー関数を実行→上記のapi.exception_handlerに戻りエラーを返す
    raise ServiceUnavailableError() 
  return {"message": "Hello"}

例外ハンドラー関数は2つの引数を取ります。
そして、関数はhttpレスポンスを返す必要があります。

  1. request:Django http リクエスト
  2. exc:実際の例外

デフォルトの例外ハンドラーをオーバーライドする

デフォルトでは、Django Ninjaは次の例外処理を初期化しました。

  • ninja.errors.AuthenticationError:認証データが無効な場合に発生
  • ninja.errors.ValidationError:リクエストデータが検証されない場合に発生
  • ninja.errors.HttpError:コードの任意の場所からステータスコードでhttpエラーを投げるために使用
  • django.http.Http404:Djangoのデフォルトの404エラーの例外get_object_or_404を使用し、feを返すことができる。
  • Exception:アプリケーションによって処理されないその他の例外

デフォルトの動作は、

  1. Djangoのsettings.pyでDEBUG=True の場合、プレーンテキストでトレースバックを返す(コンソールまたはSwagger UIでデバッグするときに役立ちます)
  2. その他の場合、デフォルトのDjangoの例外処理メカニズムが使用される(エラー ログ、ADMINSへの電子メールなど)

デフォルトの例外処理をオーバーライド

バリデーションエラーのデフォルトの出力を変更する必要がある場合は、ValidationErrorという例外ハンドラーをオーバーライドします。

from django.shortcuts import HttpResponse
from events.models import Client
from ninja import NinjaAPI, Form
from ninja.security import django_auth, HttpBasicAuth, HttpBearer, APIKeyQuery, APIKeyHeader, APIKeyCookie
from ninja.errors import ValidationError # add

〜略〜

app_v7 = NinjaAPI(
  title = "Django-Ninja Sample App7",
  version = "1.0.7",
  csrf = True,
  auth = GlobalAuth()
)

〜略〜

# add
@app_v7.exception_handler(ValidationError)
def validation_errors(request, exc):
  return HttpResponse("Invalid input", status = 422)

例外を伴うHttpレスポンスを設定する

代替的なカスタム例外処理とそれに伴うハンドラーとして、目的のコードでHttpレスポンスを返すため、例外のHttpレスポンスを設定することができます。

from django.shortcuts import HttpResponse
from events.models import Client
from ninja import NinjaAPI, Form
from ninja.security import django_auth, HttpBasicAuth, HttpBearer, APIKeyQuery, APIKeyHeader, APIKeyCookie
from ninja.errors import ValidationError, HttpError

〜略〜

class GlobalAuth(HttpBearer):
  def authenticate(self, request, token):
    if token == "supersecret":
      return token

〜略〜

app_v7 = NinjaAPI(
  title = "Django-Ninja Sample App7",
  version = "1.0.7",
  csrf = True,
  auth = GlobalAuth()
)

〜略〜

@app_v7.get("/some/response")
def some_operation(request):
  if True:
    raise HttpError(503, "Service Unvailable. Please retry later.")

設定すると以下のようにレスポンスが設定できます。

認証が済んでいない場合は通常どおりUnauthorizeが表示される
認証が済んでいる場合、指定したHttpエラーが返ってくる

最後に

今回はDjango Ninjaの自動生成ドキュメントの切り替え方法や表示設定を見てきました。
また、Django Ninjaにおけるエラーハンドリングを確認し、例外処理を他のAPIと同様に記述することで例外処理を実行することができることがわかりました。

簡単な内容ではありますが、数行で設定が完了することは開発においてはスピード感を持って取り組めるのではないかと思います。

次回は、Django NinjaにおけるURLの逆引きについてみていきます。
公式ドキュメントはこちらです。

よろしければ一緒にDjango Ninjaを勉強しませんか?

それでは次回もよろしくお願いします。

Django Ninjaチュートリアル17(APIドキュメント、エラーハンドリング)

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

はじめまして、ふじです。
Python、Django、FastAPI、React.js、Next.jsを学習している、ずっと文系のプログラミング独学者です。

目次