曦远 (DealiAxy)一份私人的文字刊物

StarBlog
返回文章列表折页手记阅读全文
Django

Wrapping Django API Responses in a Unified Format

Introduction I've been really busy lately. I started a new project, and to get a product out quickly I chose Django for the backend, then picked up the project scaffolding I had packaged before, DjangoStarter. Because I wrote quite a bit of .NetCore backend code recently and gained some new insights into CRUD API development, I definitely have to revamp DjangoStarter as well, to make it more convenient to use~ Side note: I finally actually started using react on the frontend. I have to say ts + react is really nice to use,

曦远 (DealiAxy)约 11 分钟
目录5 个章节

Introduction

I've been really busy lately. I started a new project, and to get a product out quickly I chose Django for the backend, then picked up the project scaffolding I had packaged before, DjangoStarter.

Because I wrote quite a bit of .NetCore backend code recently and gained some new insights into CRUD API development, I definitely have to revamp DjangoStarter as well, to make it more convenient to use~

Side note: I finally actually started using react on the frontend. I have to say ts + react is really nice to use. The recommendation from "Frontend Guru" was really solid (nice

The Main Topic

To be precise, this is implemented together with DjangoRestFramework. The core code consists of two parts: one is the error-handling wrapper, and the other is the response wrapper renderer.

Whether the request errors or not, it will be wrapped by our custom renderer.

Code

Let's get to the code~

First, add a new Python package to the project. Place it wherever makes sense for you; in the DjangoStarter template, I put it at utils/response. The next two files will both go in this directory.

The first is the error-handling exception.py file:

from typing import Dict

from rest_framework.exceptions import ValidationError
from rest_framework.views import exception_handler
from rest_framework.views import Response
from rest_framework.utils.serializer_helpers import ReturnDict
from rest_framework import status


def custom_handler(err: ValidationError, context: dict):
    # 先调用REST framework默认的异常处理方法获得标准错误响应对象
    response: Response = exception_handler(err, context)

    if response is None:
        return Response({
            'message': f'服务器错误:{err}'
        }, status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True)

    else:
        # 因为RestFramework默认的报错信息是 detail 字段,这里取出系统的报错信息
        msg = response.data.pop('detail', response.reason_phrase)
        res = {'message': msg}
        res.update(response.data)
        return Response(res, status=response.status_code, exception=True)

Then the custom renderer.py code:

from rest_framework.renderers import JSONRenderer


class CustomRenderer(JSONRenderer):
    # 重构render方法
    def render(self, data, accepted_media_type=None, renderer_context=None):
        if renderer_context:
            # 响应的信息,成功和错误的都是这个
            # 成功和异常响应的信息,异常信息在前面自定义异常处理中已经处理为{'message': 'error'}这种格式

            # 如果返回的data为字典
            if isinstance(data, dict):
                # 响应信息中有message和code这两个key,则获取响应信息中的message和code,并且将原本data中的这两个key删除,放在自定义响应信息里
                # 响应信息中没有则将msg内容改为请求成功 code改为请求的状态码
                msg = data.pop('message', '请求成功')
                code = data.pop('code', renderer_context["response"].status_code)
            # 如果不是字典则将msg内容改为请求成功 code改为响应的状态码
            else:
                msg = '请求成功'
                code = renderer_context["response"].status_code

            # 自定义返回的格式
            ret = {
                'message': msg,
                'code': code,
                'data': data,
            }
            # 返回JSON数据
            return super().render(ret, accepted_media_type, renderer_context)
        else:
            return super().render(data, accepted_media_type, renderer_context)

The key parts are commented, so it should be easy to follow.

After wrapping, every API's return value looks like this:

{
    "message": "msg",
    "code": 200,
    "data": {
        "data1": "123"
    }
}

Configuration

For the code above to take effect, you still need to configure RestFramework.

Edit the settings.py file and add these two settings under the REST_FRAMEWORK node. Make sure the paths match the two files above. Here I'm using the DjangoStarter project as an example.

REST_FRAMEWORK = {
    # 全局配置异常模块
    'EXCEPTION_HANDLER': 'utils.response.exception.custom_handler',
    # 修改默认返回JSON的renderer的类
    'DEFAULT_RENDERER_CLASSES': ('utils.response.renderer.CustomRenderer',),
}

That's it~ It's that simple.

(Simpler than wrapping return values in AspNetCore……)

The full code is available on GitHub: https://github.com/Deali-Axy/DjangoStarter

References

_EOF_

Wrapping Django API Responses in a Unified Format

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

告别单调,Django后台主页改造

前言 之前我做了个Django的项目,为了让管理后台更加美观,我对Django(应该说是SimpleUI的)默认的Admin后台主页进行改造,具体可以看这篇文章:项目完成 - 基于Django3.x版本 - 开发部署小结 之前的两篇关于Django3.x开发部署小结的文章介绍的都是数据库、接口、性能方面的,看到有朋友留言对后台主页的改造表示有兴趣,所以就写一篇来介绍一下~ 分析 改造基于我定制的

2022-06-28 · Django · 约 1 分钟

DjangoAdmin使用合集

DjangoAdmin DjangoAdmin本身就是一套大而全的系统,官方文档中介绍了很多配置方法,但仍然有大量的骚操作是文档中没有的,所以遇到特殊需求的时候,求助文档不一定有用。 在我看来 DjangoAdmin 虽然能快速生成一套管理后台,但如果要做大量特殊需求的定制,其成本不亚于用 Vue/React 重新开发一套,简单的使用成本不高,但深入定制的话需要对 DjangoAdmin 的工作流

2023-04-09 · Django · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…