我有一个 Flask 应用程序,它使用 Flask-JWT-Extended 扩展来处理用户身份验证,并使用 Flask-RESTx 来进行 API 定义。
这是获取当前用户的途径:
@user_api.route("/", endpoint="get_user")
class GetUser(Resource):
"""Handles HTTP requests to URL: /api/v1/user."""
@user_api.doc(security="Bearer")
@user_api.response(int(HTTPStatus.OK), "Token is currently valid.", user_model)
@user_api.response(int(HTTPStatus.BAD_REQUEST), "Validation error.")
@user_api.response(int(HTTPStatus.UNAUTHORIZED), "Token is invalid or expired.")
@user_api.marshal_with(user_model)
@jwt_required()
def get(self) -> User:
"""Validate access token and return user info."""
return current_user
在另一个地方,我为
jwt_required()
装饰器引发 flask_jwt_extended.NoAuthorizationError
异常的情况定义了一个自定义错误处理程序:
@jwt.unauthorized_loader
def unauthorized_callback(message: str) -> ResponseReturnValue:
"""Get a response when a token is not present."""
response, code = default_unauthorized_callback(message)
response.headers["WWW-Authenticate"] = 'Bearer realm="[email protected]"'
return response, code
当我发送没有授权标头的
GET /api/v1/user
请求时,我希望得到此响应:
{
"message": "Missing Authorization Header"
}
但我明白:
{
"message": "Internal Server Error"
}
以及日志中的回溯:
2023-08-06 17:04:28,007 [ERROR]: Exception on /api/v1/user/ [GET]
Traceback (most recent call last):
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask/app.py", line 1484, in full_dispatch_request
rv = self.dispatch_request()
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask/app.py", line 1469, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask_restx/api.py", line 404, in wrapper
resp = resource(*args, **kwargs)
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask/views.py", line 109, in view
return current_app.ensure_sync(self.dispatch_request)(**kwargs)
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask_restx/resource.py", line 46, in dispatch_request
resp = meth(*args, **kwargs)
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask_restx/marshalling.py", line 244, in wrapper
resp = f(*args, **kwargs)
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask_jwt_extended/view_decorators.py", line 171, in decorator
verify_jwt_in_request(
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask_jwt_extended/view_decorators.py", line 98, in verify_jwt_in_request
jwt_data, jwt_header, jwt_location = _decode_jwt_from_request(
File "/home/kirill/programming/monet/.venv/lib/python3.10/site-packages/flask_jwt_extended/view_decorators.py", line 362, in _decode_jwt_from_request
raise NoAuthorizationError(errors[0])
flask_jwt_extended.exceptions.NoAuthorizationError: Missing Authorization Header
2023-08-06 17:04:28,014 [INFO]: 127.0.0.1 - - [06/Aug/2023 17:04:28] "GET /api/v1/user/ HTTP/1.1" 500 -
问题变得更奇怪,因为此行为的测试正在通过:
def test_get_user_no_header(client: FlaskClient, db: SQLAlchemy) -> None:
"""Test get user API request with no header provided.
Checks:
- Response has a valid structure.
"""
register_user(client)
login_user(client)
response = client.get(url_for("api.get_user"))
assert response.status_code == HTTPStatus.UNAUTHORIZED
assert "message" in response.json and response.json["message"] == "Missing Authorization Header"
assert "WWW-Authenticate" in response.headers
assert response.headers["WWW-Authenticate"] == WWW_AUTH_NO_TOKEN
当我使用
--debug
参数运行应用程序时,它会返回正确的响应。
但只有当我照常运行应用程序时才会出现错误。
我尝试在配置中添加
TRAP_HTTP_EXCEPTIONS = True
,但没有成功。
事实证明 Flask-JWT-Extended 和 Flask-RESTx(Flask-RESTPlus 的分支)不能很好地协同工作...
Flask-JWT-Extended 维护者在 GitHub 上 Flask-JWT-Extended 问题中的评论有帮助。
简单来说:需要将
PROPAGATE_EXCEPTIONS
设置为True