serializer.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # -*- coding: utf-8 -*-
  2. # @Author : YY
  3. import dataclasses
  4. from datetime import date
  5. import decimal
  6. import uuid
  7. import typing as t
  8. from flask import Response
  9. from flask.json.provider import DefaultJSONProvider
  10. from werkzeug.exceptions import HTTPException, default_exceptions
  11. from werkzeug.http import http_date
  12. from ruoyi_common.base.model import AjaxResponse
  13. WSGIEnvironment: t.TypeAlias = dict[str, t.Any]
  14. def _update_exceptions():
  15. """
  16. 更新异常
  17. """
  18. for code in default_exceptions.keys():
  19. exc = default_exceptions[code]
  20. if isinstance(exc, HTTPException):
  21. new_exc = HttpException.from_http_exception(exc)
  22. default_exceptions[code] = new_exc
  23. else:
  24. continue
  25. _update_exceptions()
  26. del _update_exceptions
  27. class HttpException(HTTPException):
  28. code: int | None = None
  29. description: str | None = None
  30. def __init__(
  31. self,
  32. description: str | None = None,
  33. response: Response | None = None,
  34. ) -> None:
  35. super().__init__()
  36. if description is not None:
  37. self.description = description
  38. self.response = response
  39. @classmethod
  40. def from_http_exception(cls, exc: HTTPException) -> "HttpException":
  41. """
  42. 从HTTPException转换为HttpException
  43. Args:
  44. exc (HTTPException): werkezeug的HTTPException
  45. Returns:
  46. HttpException: HttpException
  47. """
  48. error = cls(description=exc.description, response=exc.response)
  49. error.code = exc.code
  50. return error
  51. @property
  52. def name(self) -> str:
  53. """
  54. 状态名称
  55. Returns:
  56. str: 状态名称
  57. """
  58. from werkzeug.http import HTTP_STATUS_CODES
  59. return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore
  60. def get_description(
  61. self,
  62. environ: WSGIEnvironment | None = None,
  63. scope: dict[str, t.Any] | None = None,
  64. ) -> str:
  65. """
  66. 异常描述
  67. Args:
  68. environ (WSGIEnvironment, optional): 环境变量. Defaults to None.
  69. scope (dict[str, t.Any], optional): 作用域. Defaults to None.
  70. Returns:
  71. str: 异常描述
  72. """
  73. return self.description or ""
  74. def get_body(
  75. self,
  76. environ: WSGIEnvironment | None = None,
  77. scope: dict[str, t.Any] | None = None,
  78. ) -> str:
  79. """
  80. 异常响应体
  81. Args:
  82. environ (WSGIEnvironment, optional): 环境变量. Defaults to None.
  83. scope (dict[str, t.Any], optional): 作用域. Defaults to None.
  84. Returns:
  85. str: 异常响应体
  86. """
  87. ajax_resposne = AjaxResponse.from_error(msg=self.description)
  88. ajax_resposne.code = self.code
  89. return ajax_resposne.model_dump_json(
  90. exclude_unset = True,
  91. exclude_none = True,
  92. )
  93. def get_headers(
  94. self,
  95. environ: WSGIEnvironment | None = None,
  96. scope: dict[str, t.Any] | None = None,
  97. ) -> list[tuple[str, str]]:
  98. """
  99. 异常请求头
  100. Args:
  101. environ (WSGIEnvironment, optional): 环境变量. Defaults to None.
  102. scope (dict[str, t.Any], optional): 作用域. Defaults to None.
  103. Returns:
  104. list[tuple[str, str]]: 异常请求头
  105. """
  106. return [("Content-Type", "application/json")]
  107. def json_default(obj):
  108. """
  109. 转化成可序列化对象
  110. Args:
  111. obj : 待序列化对象
  112. Returns:
  113. _type_: 可序列化对象
  114. """
  115. if isinstance(obj, date):
  116. return http_date(obj)
  117. if isinstance(obj, decimal.Decimal):
  118. return str(obj)
  119. if isinstance(obj, uuid.UUID):
  120. return obj.hex
  121. if dataclasses and dataclasses.is_dataclass(obj):
  122. return dataclasses.asdict(obj)
  123. if isinstance(obj, AjaxResponse):
  124. return obj.model_dump_json()
  125. if hasattr(obj, "__html__"):
  126. return str(obj.__html__())
  127. raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
  128. class JsonProvider(DefaultJSONProvider):
  129. """
  130. 自定义json序列化
  131. Args:
  132. DefaultJSONProvider: 默认flask的json序列化
  133. """
  134. default = staticmethod(json_default)
  135. def handle_http_exception(error:HTTPException) -> Response:
  136. """
  137. 处理http异常
  138. Args:
  139. error (HttpException): http异常
  140. Returns:
  141. ResponseReturnValue: 响应体
  142. """
  143. if not isinstance(error, HttpException):
  144. error = HttpException.from_http_exception(error)
  145. return error.get_response()