| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- # -*- coding: utf-8 -*-
- # @Author : YY
- import dataclasses
- from datetime import date
- import decimal
- import uuid
- import typing as t
- from flask import Response
- from flask.json.provider import DefaultJSONProvider
- from werkzeug.exceptions import HTTPException, default_exceptions
- from werkzeug.http import http_date
- from ruoyi_common.base.model import AjaxResponse
- from ruoyi_common.constant import HttpStatus
- from ruoyi_common.utils.base import UtilException
- WSGIEnvironment: t.TypeAlias = dict[str, t.Any]
- def _update_exceptions():
- """
- 更新异常
- """
- for code in default_exceptions.keys():
- exc = default_exceptions[code]
- if isinstance(exc, HTTPException):
- new_exc = HttpException.from_http_exception(exc)
- default_exceptions[code] = new_exc
- else:
- continue
- _update_exceptions()
- del _update_exceptions
- class HttpException(HTTPException):
- code: int | None = None
- description: str | None = None
- def __init__(
- self,
- description: str | None = None,
- response: Response | None = None,
- ) -> None:
- super().__init__()
- if description is not None:
- self.description = description
- self.response = response
- @classmethod
- def from_http_exception(cls, exc: HTTPException) -> "HttpException":
- """
- 从HTTPException转换为HttpException
- Args:
- exc (HTTPException): werkezeug的HTTPException
- Returns:
- HttpException: HttpException
- """
- error = cls(description=exc.description, response=exc.response)
- error.code = exc.code
- return error
- @property
- def name(self) -> str:
- """
- 状态名称
- Returns:
- str: 状态名称
- """
- from werkzeug.http import HTTP_STATUS_CODES
- return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore
- def get_description(
- self,
- environ: WSGIEnvironment | None = None,
- scope: dict[str, t.Any] | None = None,
- ) -> str:
- """
- 异常描述
- Args:
- environ (WSGIEnvironment, optional): 环境变量. Defaults to None.
- scope (dict[str, t.Any], optional): 作用域. Defaults to None.
- Returns:
- str: 异常描述
- """
- return self.description or ""
- def get_body(
- self,
- environ: WSGIEnvironment | None = None,
- scope: dict[str, t.Any] | None = None,
- ) -> str:
- """
- 异常响应体
- Args:
- environ (WSGIEnvironment, optional): 环境变量. Defaults to None.
- scope (dict[str, t.Any], optional): 作用域. Defaults to None.
- Returns:
- str: 异常响应体
- """
- ajax_resposne = AjaxResponse.from_error(msg=self.description)
- ajax_resposne.code = self.code
- return ajax_resposne.model_dump_json(
- exclude_unset=True,
- exclude_none=True,
- )
- def get_headers(
- self,
- environ: WSGIEnvironment | None = None,
- scope: dict[str, t.Any] | None = None,
- ) -> list[tuple[str, str]]:
- """
- 异常请求头
- Args:
- environ (WSGIEnvironment, optional): 环境变量. Defaults to None.
- scope (dict[str, t.Any], optional): 作用域. Defaults to None.
- Returns:
- list[tuple[str, str]]: 异常请求头
- """
- return [("Content-Type", "application/json")]
- def json_default(obj):
- """
- 转化成可序列化对象
- Args:
- obj : 待序列化对象
- Returns:
- _type_: 可序列化对象
- """
- if isinstance(obj, date):
- return http_date(obj)
- if isinstance(obj, decimal.Decimal):
- return str(obj)
- if isinstance(obj, uuid.UUID):
- return obj.hex
- if dataclasses and dataclasses.is_dataclass(obj):
- return dataclasses.asdict(obj)
- if isinstance(obj, AjaxResponse):
- return obj.model_dump_json()
- if hasattr(obj, "__html__"):
- return str(obj.__html__())
- raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
- class JsonProvider(DefaultJSONProvider):
- """
- 自定义json序列化
- Args:
- DefaultJSONProvider: 默认flask的json序列化
- """
- default = staticmethod(json_default)
- def handle_http_exception(error: HTTPException) -> Response:
- """
- 处理http异常
- Args:
- error (HttpException): http异常
- Returns:
- ResponseReturnValue: 响应体
- """
- if not isinstance(error, HttpException):
- error = HttpException.from_http_exception(error)
- return error.get_response()
- def handle_util_exception(error: UtilException) -> Response:
- """
- 处理业务工具类异常,保持和若依Java版一致的json结构
- 注意:HTTP状态码始终返回200,业务状态码放在响应体的code字段中
- 这样前端可以正确读取msg字段,而不是显示"接口XXX异常"
- """
- status = getattr(error, "status", HttpStatus.ERROR)
- ajax_response = AjaxResponse.from_error(msg=str(error))
- ajax_response.code = status
- response = Response(
- response=ajax_response.model_dump_json(
- exclude_unset=True,
- exclude_none=True,
- ),
- status=HttpStatus.SUCCESS, # HTTP状态码始终返回200,业务状态码在响应体的code中
- mimetype="application/json"
- )
- return response
|