{
"code": 200,
"msg": "ok",
"data": { some data }
}
{
"code": 404,
"msg": "http not found",
"data": null
}
我说一下我之前的实现,这种实现我认为很怪,而且并不能完美封装一些 500 错误,想请教一下有没有什么可以优雅的方式。
首先我使用一个 dataclasses.dataclass 进行定义返回数据结构(code,msg,data)
from dataclasses import dataclass
from typing import Any
@dataclass
class Result:
code: int
msg: str
data: Any
后面我需要打各种补丁
from dataclasses import asdict
from django.http import JsonResponse
from rest_framework import views
class SomeApiView(views.APIView):
def get(, request, *args, **kwargs) -> JsonResponse:
# do something
return JsonResponse(asdict(Result(200, 'ok', null)))
from dataclasses import asdict
from django.http import JsonResponse
from rest_framework import status, mixins
class MyRetrieveModelMixin(mixins.RetrieveModelMixin):
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return JsonResponse(asdict(Result(200, 'ok', serializer.data)))
# 配置
REST_FRAMEWORK = {
# ...
# 异常处理(统一封装为 code msg data 格式)
'EXCEPTION_HANDLER': 'utils.result_handler.custom_exception_handler',
# ...
}
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None:
# logger
# 进行一些处理 处理成 Result(code, msg, data)
# 这里对很多处理,尤其 serializer 中的处理 特别烦!
# 比如手机号唯一约束,前端就想要 code:400 msg:"该手机号已被使用"
return JsonResponse(...)
1
IurNusRay 2022-08-18 16:00:08 +08:00
1.返回封装这个,可以定义一个视图基类,改写它的返回 HTTP 响应的函数(比如 finalize_response ),然后让所有视图类继承它,应该没必要每个视图都手动写 code, msg 之类的吧
2.全局异常处理那个,我个人觉得倒是挺方便的,只是需要针对各种异常判断一下,统一成相同格式(至少不用自己到处写 try except ) |
2
passerby233 2022-08-18 17:31:08 +08:00
```python
print('test md.') ``` |
3
passerby233 2022-08-18 17:39:39 +08:00
1.全局异常
```python from rest_framework.exceptions import ValidationError from rest_framework.views import exception_handler def custom_exception_handler(exc, context): """ 自定义异常,需要在 settings.py 文件中进行全局配置 1.在视图中的 APIView 中使用时,需要在验证数据的时候传入 raise_exception=True 说明需要使用自定义异常 2.ModelViewSet 中非自定义 action 已经使用了 raise_exception=True,所以无需配置 """ response = exception_handler(exc, context) if response is not None: # 字段校验错误处理 if isinstance(exc, ValidationError): if isinstance(response.data, dict): # 取错误信息中的一组数据返回 error_data = list(dict(response.data).items())[0] # 该组数据的 key ,对应模型中的某个字段 error_key = error_data[0] # 该组数据的 value ,有可能是多个错误校验提示信息,这里只取第一条 error_value = error_data[1][0] response.data['message'] = f"{error_key}: {error_value}" for key in dict(response.data).keys(): # 删除多余错误信息 if key != 'message': response.data.pop(key) response.data['code'] = 40000 response.data['data'] = None if isinstance(response.data, list): response.data = {'code': 40000, 'message': response.data[0], 'data': None} return response if 'detail' in response.data: response.data = {'code': 40000, 'message': response.data.get('detail'), 'data': None} else: # 未知错误 response.data = {'code': 40000, 'message': str(response.data), 'data': None} return response return response ``` 2.自定义 response ```python from rest_framework.response import Response from rest_framework import serializers class JsonResponse(Response): """ 自定义接口响应数据格式类 1.在视图类中的 APIView 中使用该 JsonResponse 返回响应数据 2.ModelViewSet 、Mixin 下派生的 APIView 类、views.APIView 都需要自己重写并返回 JsonResponse 格式的数据 """ def __init__(self, data=None, code=None, msg=None, status=None, template_name=None, headers=None, exception=False, content_type=None): super().__init__(None, status=status) if isinstance(data, serializers.Serializer): msg = ( 'You passed a Serializer instance as data, but ' 'probably meant to pass serialized `.data` or ' '`.error`. representation.' ) raise AssertionError(msg) self.data = {'code': code, 'message': msg, 'data': data} self.template_name = template_name self.exception = exception self.content_type = content_type if headers: for name, value in headers.items(): self[name] = value ``` ModelViewSet 子类中重写 action 返回值 ```python from utils.drf_utils.custom_json_response import JsonResponse def create(self, request, *args, **kwargs): """ create task """ res = super().create(request, *args, **kwargs) return JsonResponse(data=res.data, msg='success', code=20000, status=status.HTTP_201_CREATED, headers=res.headers) ``` |
4
passerby233 2022-08-18 17:41:46 +08:00
跟你的差不多
|
5
BeautifulSoup 2022-08-18 17:48:32 +08:00
楼主加个 v 怎么样?我是上个帖“智慧党建”项目的,正好沟通交流。你说的这两个问题,我们也遇到了,有一些自己的解决办法可以跟你分享交流。
|
6
HashV2 OP @BeautifulSoup #5 base64: emhhb3poaWthaUFjdA==
|
7
HashV2 OP @IurNusRay #1 modelviewset 肯定是继承的,很多特殊的借口是要用 apiview 一个一个写的,不过 dataclass 定义了 code map 的 正常的返回只需要写 data ,如果有错误给个 code 就好
|
8
HashV2 OP @passerby233 #3 v2 回复是不支持 markdown 的
|
9
HashV2 OP @passerby233 #3 哈哈哈哈 我才发现你里面的详细逻辑和我几乎一摸一样,尤其这一条,笑死
# 该组数据的 value ,有可能是多个错误校验提示信息,这里只取第一条 |
10
huangzhiyia 2022-08-31 16:47:10 +08:00 via Android
如果有 docs 生成的需求更难搞
|
11
gaogang 2022-09-05 18:06:39 +08:00
基类中重写__getattribute__方法, 方法里面如果 get 的是接口方法都给装饰下在返回(装饰器中封装下默认的 http response )
|