Django后端相应类设计

通用的ApiResponse类:用于生成统一的 API 响应格式。每个响应都包含以下字段(每个接口最终的返回数据格式):

  • status_code:HTTP 状态码(如 200、400、500 等)
  • message:响应的描述信息(如 "Success"、"Resource not found" 等)
  • data:返回的数据(如果有)
  • trace_id:请求的唯一标识符

trace_id的作用:

  • 唯一标识请求:每个请求都有一个唯一的 TraceID
  • 日志关联:在日志中记录 TraceID,便于快速定位问题

ApiResponse类核心方法:

1、success 200 请求成功

2、created 201 创建资源成功

3、bad_request 400 后端必要的参数与前端传递的参数不一致。例如缺少必填字段、参数类型错误(如期望是数字,但传入了字符串)

4、unauthorized 401 用户鉴权问题,例如token失效,没有提供有效的身份验证信息

5、forbidden 403 禁止访问,客户端没有权限访问资源

6、not_found 404 请求的资源不存在,URL 路径错误。访问的 API 路径错误等

7、validation_error 422 请求格式正确,但语义错误,服务器无法处理。例如:邮箱格式不正确、密码长度不符合要求。请求数据不符合业务规则

8、conflict 409 资源冲突,请求与当前资源状态冲突.例如:创建资源时,资源已存在。用户重名等

9、internal_server_error 500 服务器内部错误,无法完成请求。例如:数据库连接失败,代码逻辑错误导致的异常

10、service_unavailable 503 服务不可用,服务器暂时无法处理请求。例如:服务器正在维护,第三方 API 服务失效等

中间件middlewares作用:

Django 中的一个全局异常处理器,它的主要作用是捕获 Django 视图函数或中间件中抛出的异常,并根据异常类型返回统一的错误响应

同时,它还支持 TraceID 的生成和传递,便于追踪请求的完整调用链路。

复制代码
1、中间件会捕获所有未处理的异常(包括 Django 内置异常和自定义异常),根据异常类型,生成对应的错误响应
2、最终会使用 ApiResponse 类生成统一的错误响应格式(status_code、message、data、trace_id)
3、从请求头中获取 TraceID,如果未提供则自动生成一个新的。将异常信息和 TraceID 记录到日志中,便于后续排查问题

整体说明:

1、使用可参考test.py,接口最终返回的数据,需要经过结果处理类ApiResponse,统一进行处理

2、在视图(接口)中,尽量使用ApiResponse类的10个核心方法,捕捉可能发生的异常

3、中间件会捕捉,代码未处理的异常,最终规范为status_code、message、data、trace_id四个字段(中间键与ApiResponse结果处理类是互补的作用)

4、后端返回的数据,最终会规范为status_code、message、data、trace_id四个字段

python 复制代码
class StatusCode:
    # 成功状态码
    SUCCESS = 200  # 请求成功
    CREATED = 201  # 创建资源成功

    # 客户端错误(4XX)
    BAD_REQUEST = 400  # 参数错误
    UNAUTHORIZED = 401  # 未授权
    FORBIDDEN = 403  # 禁止访问
    NOT_FOUND = 404  # 资源未找到
    VALIDATION_ERROR = 422  # 参数校验错误
    CONFLICT = 409  # 资源冲突

    # 服务端错误(5XX)
    SERVER_ERROR = 500  # 服务器内部错误
    SERVICE_UNAVAILABLE = 503  # 服务不可用


class ApiException(Exception):
    """
    基础异常类,所有自定义异常继承此类。
    """

    def __init__(self, message, status_code):
        super().__init__(message)
        self.status_code = status_code
        self.message = message


# 客户端错误(4XX)
class BadRequestException(ApiException):
    """400 Bad Request"""

    def __init__(self, message="Bad request"):
        super().__init__(message, status_code=StatusCode.BAD_REQUEST)


class UnauthorizedException(ApiException):
    """401 Unauthorized"""

    def __init__(self, message="Unauthorized"):
        super().__init__(message, status_code=StatusCode.UNAUTHORIZED)


class ForbiddenException(ApiException):
    """403 Forbidden"""

    def __init__(self, message="Forbidden"):
        super().__init__(message, status_code=StatusCode.FORBIDDEN)


class NotFoundException(ApiException):
    """404 Not Found"""

    def __init__(self, message="Resource not found"):
        super().__init__(message, status_code=StatusCode.NOT_FOUND)


class ValidationErrorException(ApiException):
    """422 Validation Error"""

    def __init__(self, message="Validation error"):
        super().__init__(message, status_code=StatusCode.VALIDATION_ERROR)


class ConflictException(ApiException):
    """409 Conflict"""

    def __init__(self, message="Conflict"):
        super().__init__(message, status_code=StatusCode.CONFLICT)


# 服务端错误(5XX)
class InternalServerErrorException(ApiException):
    """500 Internal Server Error"""

    def __init__(self, message="Internal server error"):
        super().__init__(message, status_code=StatusCode.SERVER_ERROR)


class ServiceUnavailableException(ApiException):
    """503 Service Unavailable"""

    def __init__(self, message="Service unavailable"):
        super().__init__(message, status_code=StatusCode.SERVICE_UNAVAILABLE)

以上为自定义的异常处理类,下述为自定义的中间件

python 复制代码
import logging
import uuid

from django.core.exceptions import PermissionDenied
from django.http import Http404

from 响应类设计.exceptions import *
from 响应类设计.response_handler import ApiResponse

logger = logging.getLogger(__name__)


class ExceptionHandlingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # 从请求头中获取 Trace ID,如果没有则生成一个新的
        trace_id = request.headers.get('X-Trace-ID', str(uuid.uuid4()))
        request.trace_id = trace_id  # 将 Trace ID 绑定到请求对象

        try:
            response = self.get_response(request)
            return response
        except Exception as e:
            return self.handle_exception(e, trace_id)

    def handle_exception(self, exception, trace_id):
        # 记录异常日志,包含 Trace ID
        logger.error(f"Trace ID: {trace_id}, Error: {str(exception)}", exc_info=True)

        if isinstance(exception, Http404):
            return ApiResponse.not_found(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, PermissionDenied):
            return ApiResponse.forbidden(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, BadRequestException):
            return ApiResponse.bad_request(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, UnauthorizedException):
            return ApiResponse.unauthorized(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, ForbiddenException):
            return ApiResponse.forbidden(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, NotFoundException):
            return ApiResponse.not_found(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, ValidationErrorException):
            return ApiResponse.validation_error(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, ConflictException):
            return ApiResponse.conflict(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, InternalServerErrorException):
            return ApiResponse.internal_server_error(message=str(exception), trace_id=trace_id)
        elif isinstance(exception, ServiceUnavailableException):
            return ApiResponse.service_unavailable(message=str(exception), trace_id=trace_id)
        else:
            # 未知异常,返回 500 状态码
            return ApiResponse.internal_server_error(message="An unexpected error occurred.", trace_id=trace_id)
python 复制代码
from django.http import JsonResponse
import uuid

from 响应类设计.exceptions import StatusCode


class ApiResponse:
    """
    通用的 API 响应类,支持 Trace ID 和多种状态码。
    """

    def __init__(self, status_code, message, data=None, trace_id=None):
        self.status_code = status_code
        self.message = message
        self.data = data
        self.trace_id = trace_id or str(uuid.uuid4())  # 生成唯一的 Trace ID

    def to_dict(self):
        """
        将响应数据转换为字典格式。
        """
        return {
            'status_code': self.status_code,
            'message': self.message,
            'data': self.data if self.data is not None else {},
            'trace_id': self.trace_id  # 包含 Trace ID
        }

    def to_json_response(self):
        """
        将响应数据转换为 Django 的 JsonResponse。
        """
        return JsonResponse(self.to_dict(), status=self.status_code)

    # 成功响应
    @staticmethod
    def success(message="Success", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.SUCCESS, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    # 资源创建成功
    @staticmethod
    def created(message="Resource created", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.CREATED, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    # 客户端错误
    @staticmethod
    def bad_request(message="Bad request", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.BAD_REQUEST, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    @staticmethod
    def unauthorized(message="Unauthorized", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.UNAUTHORIZED, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    @staticmethod
    def forbidden(message="Forbidden", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.FORBIDDEN, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    @staticmethod
    def not_found(message="Resource not found", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.NOT_FOUND, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    @staticmethod
    def validation_error(message="Validation error", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.VALIDATION_ERROR, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    @staticmethod
    def conflict(message="Conflict", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.CONFLICT, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    # 服务端错误
    @staticmethod
    def internal_server_error(message="Internal server error", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.SERVER_ERROR, message=message, data=data,
                           trace_id=trace_id).to_json_response()

    @staticmethod
    def service_unavailable(message="Service unavailable", data=None, trace_id=None):
        return ApiResponse(status_code=StatusCode.SERVICE_UNAVAILABLE, message=message, data=data,
                           trace_id=trace_id).to_json_response()

上述为定义的结果处理类,一下为使用说明案例

python 复制代码
from django.views import View
import uuid
from .exceptions import BadRequestException, NotFoundException
from .response_handler import ApiResponse


def get_trace_id(request):
    """
    从请求头中获取 Trace ID,如果没有则生成一个新的。
    """
    return request.headers.get('X-Trace-ID', str(uuid.uuid4()))


class MyView(View):
    def get(self, request, *args, **kwargs):
        """
        示例 GET 请求视图,展示如何使用 Trace ID。
        """
        # 获取或生成 Trace ID
        trace_id = get_trace_id(request)

        # 示例:从 URL 参数中获取资源 ID
        resource_id = kwargs.get('resource_id')
        if not resource_id:
            # 如果资源 ID 不存在,返回 400 错误
            raise BadRequestException("Resource ID is required.")

        # 模拟资源查找
        resource = self.get_resource(resource_id)
        if not resource:
            # 如果资源未找到,返回 404 错误
            raise NotFoundException("Resource not found.")

        # 返回成功响应
        return ApiResponse.success(
            message="Resource retrieved successfully",
            data=resource,
            trace_id=trace_id
        )

    def post(self, request, *args, **kwargs):
        """
        示例 POST 请求视图,展示如何使用 Trace ID。
        """
        # 获取或生成 Trace ID
        trace_id = get_trace_id(request)

        # 示例:从请求体中获取数据
        data = request.POST
        if not data.get('name'):
            # 如果名称字段不存在,返回 400 错误
            raise BadRequestException("Name is required.")

        # 模拟创建资源
        resource = self.create_resource(data)
        return ApiResponse.created(
            message="Resource created successfully",
            data=resource,
            trace_id=trace_id
        )

    def get_resource(self, resource_id):
        """
        模拟资源查找逻辑。
        """
        # 这里可以替换为实际的数据库查询逻辑
        if resource_id == "123":
            return {"id": "123", "name": "Example Resource"}
        return None

    def create_resource(self, data):
        """
        模拟资源创建逻辑。
        """
        # 这里可以替换为实际的数据库插入逻辑
        return {"id": str(uuid.uuid4()), "name": data.get('name')}
相关推荐
点云SLAM13 分钟前
PyTorch 中contiguous函数使用详解和代码演示
人工智能·pytorch·python·3d深度学习·contiguous函数·张量内存布局优化·张量操作
尘浮72826 分钟前
60天python训练计划----day45
开发语言·python
哆啦A梦的口袋呀37 分钟前
基于Python学习《Head First设计模式》第六章 命令模式
python·学习·设计模式
努力搬砖的咸鱼39 分钟前
从零开始搭建 Pytest 测试框架(Python 3.8 + PyCharm 版)
python·pycharm·pytest
Calvex42 分钟前
PyCharm集成Conda环境
python·pycharm·conda
一千柯橘1 小时前
python 项目搭建(类比 node 来学习)
python
sduwcgg1 小时前
python的numpy的MKL加速
开发语言·python·numpy
大模型真好玩1 小时前
可视化神器WandB,大模型训练的必备工具!
人工智能·python·mcp
东方佑1 小时前
使用 Python 自动化 Word 文档样式复制与内容生成
python·自动化·word
钢铁男儿1 小时前
Python 接口:从协议到抽象基 类(定义并使用一个抽象基类)
开发语言·python