Skip to content

Commit 5173ff7

Browse files
authored
Update the global unified response code (#223)
* Update the global unified response * Update the uniform return model default * Update the environment variable type
1 parent 46d6fbc commit 5173ff7

File tree

7 files changed

+210
-79
lines changed

7 files changed

+210
-79
lines changed

backend/app/api/v1/mixed/tests.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async def task_demo_add():
2424
task_demo, 'interval', seconds=1, id='task_demo', replace_existing=True, start_date=datetime.datetime.now()
2525
)
2626

27-
return await response_base.success({'msg': 'success'})
27+
return await response_base.success()
2828

2929

3030
@router.post('/async', summary='测试添加异步任务')
@@ -37,7 +37,7 @@ async def task_demo_add_async():
3737
replace_existing=True,
3838
start_date=datetime.datetime.now(),
3939
)
40-
return await response_base.success({'msg': 'success'})
40+
return await response_base.success()
4141

4242

4343
@router.post('/files', summary='测试文件上传')

backend/app/common/exception/errors.py

+17-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3+
"""
4+
全局业务异常类
5+
6+
业务代码执行异常时,可以使用 raise xxxError 触发内部错误,它尽可能实现带有后台任务的异常,但它不适用于**自定义响应状态码**
7+
如果要求使用**自定义响应状态码**,则可以通过 return await response_base.fail(res=CustomResponseCode.xxx) 直接返回
8+
""" # noqa: E501
39
from typing import Any
410

511
from fastapi import HTTPException
612
from starlette.background import BackgroundTask
713

8-
from backend.app.common.response.response_code import CustomCode
14+
from backend.app.common.response.response_code import CustomErrorCode, StandardResponseCode
915

1016

1117
class BaseExceptionMixin(Exception):
@@ -24,34 +30,34 @@ def __init__(self, *, code: int, msg: Any = None, headers: dict[str, Any] | None
2430

2531

2632
class CustomError(BaseExceptionMixin):
27-
def __init__(self, *, error: CustomCode, data: Any = None, background: BackgroundTask | None = None):
33+
def __init__(self, *, error: CustomErrorCode, data: Any = None, background: BackgroundTask | None = None):
2834
self.code = error.code
2935
super().__init__(msg=error.msg, data=data, background=background)
3036

3137

3238
class RequestError(BaseExceptionMixin):
33-
code = 400
39+
code = StandardResponseCode.HTTP_400
3440

3541
def __init__(self, *, msg: str = 'Bad Request', data: Any = None, background: BackgroundTask | None = None):
3642
super().__init__(msg=msg, data=data, background=background)
3743

3844

3945
class ForbiddenError(BaseExceptionMixin):
40-
code = 403
46+
code = StandardResponseCode.HTTP_403
4147

4248
def __init__(self, *, msg: str = 'Forbidden', data: Any = None, background: BackgroundTask | None = None):
4349
super().__init__(msg=msg, data=data, background=background)
4450

4551

4652
class NotFoundError(BaseExceptionMixin):
47-
code = 404
53+
code = StandardResponseCode.HTTP_404
4854

4955
def __init__(self, *, msg: str = 'Not Found', data: Any = None, background: BackgroundTask | None = None):
5056
super().__init__(msg=msg, data=data, background=background)
5157

5258

5359
class ServerError(BaseExceptionMixin):
54-
code = 500
60+
code = StandardResponseCode.HTTP_500
5561

5662
def __init__(
5763
self, *, msg: str = 'Internal Server Error', data: Any = None, background: BackgroundTask | None = None
@@ -60,21 +66,21 @@ def __init__(
6066

6167

6268
class GatewayError(BaseExceptionMixin):
63-
code = 502
69+
code = StandardResponseCode.HTTP_502
6470

6571
def __init__(self, *, msg: str = 'Bad Gateway', data: Any = None, background: BackgroundTask | None = None):
6672
super().__init__(msg=msg, data=data, background=background)
6773

6874

6975
class AuthorizationError(BaseExceptionMixin):
70-
code = 401
76+
code = StandardResponseCode.HTTP_401
7177

72-
def __init__(self, *, msg: str = 'Permission denied', data: Any = None, background: BackgroundTask | None = None):
78+
def __init__(self, *, msg: str = 'Permission Denied', data: Any = None, background: BackgroundTask | None = None):
7379
super().__init__(msg=msg, data=data, background=background)
7480

7581

7682
class TokenError(HTTPError):
77-
code = 401
83+
code = StandardResponseCode.HTTP_401
7884

79-
def __init__(self, *, msg: str = 'Not authenticated', headers: dict[str, Any] | None = None):
85+
def __init__(self, *, msg: str = 'Not Authenticated', headers: dict[str, Any] | None = None):
8086
super().__init__(code=self.code, msg=msg, headers=headers or {'WWW-Authenticate': 'Bearer'})

backend/app/common/exception/exception_handler.py

+33-47
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,14 @@
99
from starlette.exceptions import HTTPException
1010
from starlette.middleware.cors import CORSMiddleware
1111
from starlette.responses import JSONResponse
12-
from uvicorn.protocols.http.h11_impl import STATUS_PHRASES
1312

1413
from backend.app.common.exception.errors import BaseExceptionMixin
1514
from backend.app.common.log import log
16-
from backend.app.common.response.response_schema import response_base
15+
from backend.app.common.response.response_code import StandardResponseCode, CustomResponseCode
16+
from backend.app.common.response.response_schema import response_base, ResponseModel
1717
from backend.app.core.conf import settings
1818

1919

20-
def _get_exception_code(status_code):
21-
"""
22-
获取返回状态码, OpenAPI, Uvicorn... 可用状态码基于 RFC 定义, 详细代码见下方链接
23-
24-
`python 状态码标准支持 <https://github.com/python/cpython/blob/6e3cc72afeaee2532b4327776501eb8234ac787b/Lib/http
25-
/__init__.py#L7>`__
26-
27-
`IANA 状态码注册表 <https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml>`__
28-
29-
:param status_code:
30-
:return:
31-
"""
32-
try:
33-
STATUS_PHRASES[status_code]
34-
except Exception:
35-
code = 400
36-
else:
37-
code = status_code
38-
return code
39-
40-
4120
def register_exception(app: FastAPI):
4221
@app.exception_handler(HTTPException)
4322
async def http_exception_handler(request: Request, exc: HTTPException):
@@ -48,11 +27,11 @@ async def http_exception_handler(request: Request, exc: HTTPException):
4827
:param exc:
4928
:return:
5029
"""
51-
content = {'code': exc.status_code, 'msg': exc.detail}
30+
content = ResponseModel(code=exc.status_code, msg=exc.detail).dict()
5231
request.state.__request_http_exception__ = content # 用于在中间件中获取异常信息
5332
return JSONResponse(
54-
status_code=_get_exception_code(exc.status_code),
55-
content=await response_base.fail(**content),
33+
status_code=StandardResponseCode.HTTP_400,
34+
content=content if settings.ENVIRONMENT == 'dev' else await response_base.fail(CustomResponseCode.HTTP_400),
5635
headers=exc.headers,
5736
)
5837

@@ -94,13 +73,16 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
9473
message += f'{data.get(field, field) if field != "__root__" else ""} {msg}' + '.'
9574
elif isinstance(raw_error.exc, json.JSONDecodeError):
9675
message += 'json解析失败'
97-
content = {
98-
'code': 422,
99-
'msg': '请求参数非法' if len(message) == 0 else f'请求参数非法: {message}',
100-
'data': {'errors': exc.errors()} if message == '' and settings.UVICORN_RELOAD is True else None,
101-
}
76+
content = ResponseModel(
77+
code=StandardResponseCode.HTTP_422,
78+
msg='请求参数非法' if len(message) == 0 else f'请求参数非法: {message}',
79+
data={'errors': exc.errors()} if message == '' else None,
80+
).dict()
10281
request.state.__request_validation_exception__ = content # 用于在中间件中获取异常信息
103-
return JSONResponse(status_code=422, content=await response_base.fail(**content))
82+
return JSONResponse(
83+
status_code=StandardResponseCode.HTTP_422,
84+
content=content if settings.ENVIRONMENT == 'dev' else await response_base.fail(CustomResponseCode.HTTP_422),
85+
)
10486

10587
@app.exception_handler(Exception)
10688
async def all_exception_handler(request: Request, exc: Exception):
@@ -113,24 +95,28 @@ async def all_exception_handler(request: Request, exc: Exception):
11395
"""
11496
if isinstance(exc, BaseExceptionMixin):
11597
return JSONResponse(
116-
status_code=_get_exception_code(exc.code),
117-
content=await response_base.fail(code=exc.code, msg=str(exc.msg), data=exc.data if exc.data else None),
98+
status_code=StandardResponseCode.HTTP_400,
99+
content=ResponseModel(
100+
code=exc.code,
101+
msg=str(exc.msg),
102+
data=exc.data if exc.data else None,
103+
).dict(),
118104
background=exc.background,
119105
)
120106

121107
elif isinstance(exc, AssertionError):
122108
return JSONResponse(
123-
status_code=500,
124-
content=await response_base.fail(
125-
code=500,
109+
status_code=StandardResponseCode.HTTP_500,
110+
content=ResponseModel(
111+
code=StandardResponseCode.HTTP_500,
126112
msg=','.join(exc.args)
127113
if exc.args
128114
else exc.__repr__()
129115
if not exc.__repr__().startswith('AssertionError()')
130116
else exc.__doc__,
131-
)
117+
).dict()
132118
if settings.ENVIRONMENT == 'dev'
133-
else await response_base.fail(code=500, msg='Internal Server Error'),
119+
else await response_base.fail(CustomResponseCode.HTTP_500),
134120
)
135121

136122
else:
@@ -139,15 +125,15 @@ async def all_exception_handler(request: Request, exc: Exception):
139125
log.error(f'未知异常: {exc}')
140126
log.error(traceback.format_exc())
141127
return JSONResponse(
142-
status_code=500,
143-
content=await response_base.fail(code=500, msg=str(exc))
128+
status_code=StandardResponseCode.HTTP_500,
129+
content=ResponseModel(code=500, msg=str(exc)).dict()
144130
if settings.ENVIRONMENT == 'dev'
145-
else await response_base.fail(code=500, msg='Internal Server Error'),
131+
else await response_base.fail(StandardResponseCode.HTTP_500),
146132
)
147133

148134
if settings.MIDDLEWARE_CORS:
149135

150-
@app.exception_handler(500)
136+
@app.exception_handler(StandardResponseCode.HTTP_500)
151137
async def cors_status_code_500_exception_handler(request, exc):
152138
"""
153139
跨域 500 异常处理
@@ -159,12 +145,12 @@ async def cors_status_code_500_exception_handler(request, exc):
159145
:return:
160146
"""
161147
response = JSONResponse(
162-
status_code=exc.code if isinstance(exc, BaseExceptionMixin) else 500,
163-
content={'code': exc.code, 'msg': exc.msg, 'data': exc.data}
148+
status_code=exc.code if isinstance(exc, BaseExceptionMixin) else StandardResponseCode.HTTP_500,
149+
content=ResponseModel(code=exc.code, msg=exc.msg, data=exc.data).dict()
164150
if isinstance(exc, BaseExceptionMixin)
165-
else await response_base.fail(code=500, msg=str(exc))
151+
else ResponseModel(code=StandardResponseCode.HTTP_500, msg=str(exc)).dict()
166152
if settings.ENVIRONMENT == 'dev'
167-
else await response_base.fail(code=500, msg='Internal Server Error'),
153+
else await response_base.fail(CustomResponseCode.HTTP_500),
168154
background=exc.background if isinstance(exc, BaseExceptionMixin) else None,
169155
)
170156
origin = request.headers.get('origin')

0 commit comments

Comments
 (0)