現在、以下を使用してHTTPの不正なリクエストを発生させています。
raise tornado.web.HTTPError(400)
html出力を返します:
<html><title>400: Bad Request</title><body>400: Bad Request</body></html>
カスタムボディでHTTP応答コードのみを返すことは可能ですか?
シミュレートできます RequestHandler.send_error
メソッド:
class MyHandler(tornado.web.RequestHandler):
def get(self):
self.clear()
self.set_status(400)
self.finish("<html><body>My custom body</body></html>")
Tornadoは RequestHandler.write_error
を呼び出してエラーを出力するため、 VisioNのアプローチ の代わりにTornadoで提案されているようにオーバーライドします docs 。このアプローチの利点は、以前と同様にHTTPError
を上げることができることです。
RequestHandler.write_error
のソースは here です。以下に、kwargsで理由を指定した場合にステータスコードの設定と出力を変更するwrite_errorの簡単な変更の例を示します。
def write_error(self, status_code, **kwargs):
if self.settings.get("serve_traceback") and "exc_info" in kwargs:
# in debug mode, try to send a traceback
self.set_header('Content-Type', 'text/plain')
for line in traceback.format_exception(*kwargs["exc_info"]):
self.write(line)
self.finish()
else:
self.set_status(status_code)
if kwargs['reason']:
self.finish(kwargs['reason'])
else:
self.finish("<html><title>%(code)d: %(message)s</title>"
"<body>%(code)d: %(message)s</body></html>" % {
"code": status_code,
"message": self._reason,
})
標準インターフェイスを使用して、HTTPError
でカスタムメッセージを定義することをお勧めします。
raise tornado.web.HTTPError(status_code=code, log_message=custom_msg)
RequestHandler
のエラーを解析して、メッセージを確認できます。
class CustomHandler(tornado.web.RequestHandler):
def write_error(self, status_code, **kwargs):
err_cls, err, traceback = kwargs['exc_info']
if err.log_message and err.log_message.startswith(custom_msg):
self.write("<html><body><h1>Here be dragons</h1></body></html>")
def write_error(self, status_code, **kwargs):
#Function to display custom error page defined in the handler.
#Over written from base handler.
data = {}
data['code'] = status_code
data['message'] = httplib.responses[status_code]
# your other conditions here to create data dict
self.write(TEMPLATES.load('error.html').generate(data=data))
いつでもself.send_error()呼び出しが開始されますwrite_error()関数はリクエストハンドラによって呼び出されます。したがって、ここでカスタムエラーデータディクテーションを作成し、カスタムエラーページにレンダリングできます。
http.responses [status_code]は、ステータスコードに基づいて「ページが見つかりません」などのエラーコードテキストを返します。
また、ハンドラでget_error_htmlメソッドをオーバーライドできます。例えば:
import tornado.web
class CustomHandler(tornado.web.RequestHandler):
def get_error_html(self, status_code, **kwargs);
self.write("<html><body><h1>404!</h1></body></html>")
...
def get(self):
...
この交換 ここで提案されているアプローチのいくつかを明確にし、reason
キーワード(試してみようと考えていた)を割引きます。
Q:(by mrtn)
「raise tornado.web.HTTPError(400, reason='invalid request')
を使用してカスタムの理由をエラー応答に渡したいので、write_error (self, status_code, **kwargs)
メソッドをオーバーライドしてこれを実行したいと考えています。
「しかし、私はself._reason
内のwrite_error
にしかアクセスできないようです。これは私が望むものではありません。また、kwargs['reason']
を試しましたが、それは存在しません。」
A:(Tornadoリード開発者@bendarnellによる)
「エラーを公開した例外は、キーワード引数のwrite_error
トリプルとしてexc_info
で利用可能です。次のような理由でreasonフィールドにアクセスできます。
if "exc_info" in kwargs:
e = kwargs["exc_info"][1]
if isinstance(e, tornado.web.HTTPError):
reason = e.reason
「しかし、reason
フィールドは本質的に非推奨である(HTTP/2には存在しない)ので、ここでやろうとしていることをするのにおそらく最善の方法ではないことに注意してください(HTTPError
のlog_message
フィールドは少し優れていますが、理想)。HTTPError
を使用する代わりに、独自の例外を発生させてください; write_error
オーバーライドは、適切な種類の例外が見つかったときにself.set_status(400)
を使用できます。
JSONエラー応答の場合、次のテンプレートを使用します。
リクエストハンドラ:
import json
from tornado.web import RequestHandler
from src.lib.errors import HTTPBadRequest
class JsonHandler(RequestHandler):
def prepare(self):
content_type = ''
if "Content-Type" in self.request.headers:
content_type = self.request.headers['Content-Type']
if content_type == 'application/json':
try:
self.request.body = json.loads(self.request.body.decode('utf-8'))
except ValueError:
raise HTTPBadRequest
def write_error(self, *args, **kwargs):
err_cls, err, traceback = kwargs['exc_info']
self.set_status(err.status_code)
if err.description:
self.write_json(err.description)
self.finish()
def set_default_headers(self):
self.set_header('Content-Type', 'application/json')
def write_json(self, response):
self.write(json.dumps(response))
エラーハンドラ:
from typing import Any
from tornado import httputil
class BaseHTTPError(Exception):
def __init__(
self, status_code: int = 500, description=None, *args: Any, **kwargs: Any
) -> None:
if description is None:
description = {}
self.status_code = status_code
self.description = description
self.args = args
self.kwargs = kwargs
def __str__(self) -> str:
message = "HTTP %d: %s" % (
self.status_code,
httputil.responses.get(self.status_code, "Unknown"),
)
return message
class HTTPBadRequest(BaseHTTPError):
def __init__(self, *args, **kwargs):
super().__init__(status_code=400, description={"error": "Bad Request"}, *args, **kwargs)