To better handle errors during application runtime and allow the client to process them easily, we need to define some error standards ⇒ all errors in the application will be mapped to 1–2 standard errors, and this is the task we need to implement in this section.
Errors are divided into two types: Application – errors belonging to the application (mapped to HTTP Errors as 5xx) and Client – errors belonging to the client (mapped to HTTP Errors as 4xx).
In the error directory, create files such as AppError.py, ClientError.py, main.py, detail.py, and __init__.py.
Open detail.py and add the following code.
from typing import List, Optional, TypedDict
class BaseErrorDetail(TypedDict, total=False):
source: str
desc: str
class ErrorDetails(TypedDict, total=False):
reasons: Optional[List[BaseErrorDetail]]

In AppError.py, add
A list of information for HTTP Server Errors.
from typing import Optional
from .detail import BaseErrorDetail, ErrorDetails
HTTPServerErrorDict = {
"InternalServerError": {
"Status": 500,
"Code": "INTERNAL_SERVER_ERROR",
},
"NotImplemented": {
"Status": 501,
"Code": "NOT_IMPLEMENTED",
},
"BadGateway": {
"Status": 502,
"Code": "BAD_GATEWAY",
},
"ServiceUnavailable": {
"Status": 503,
"Code": "SERVICE_UNAVAILABLE",
},
"GatewayTimeout": {
"Status": 504,
"Code": "GATEWAY_TIMEOUT",
},
"HttpVersionNotSupported": {
"Status": 505,
"Code": "HTTP_VERSION_NOT_SUPPORTED",
},
}
Next is the class definition of AppError.
class AppError(Exception):
"""
Lớp định nghĩa lỗi từ App hoặc phản hồi từ App.
"""
def __init__(
self,
message: str,
status_code: int = 500,
code: Optional[str] = None,
details: Optional[ErrorDetails] = None,
):
super().__init__(message)
self.status_code = status_code
self.code = code
self.details = details
@staticmethod
def create_error_detail(source: str, desc: str) -> BaseErrorDetail:
"""
Tạo ra một base error detail.
@static
@returns
"""
return {
"source": source,
"desc": desc,
}
def add_error_detail(self, detail: BaseErrorDetail):
"""
Thêm chi tiết lỗi vào tổng lỗi.
@param detail - chi tiết lỗi.
@returns
"""
if not self.details:
self.details = {"reasons": [detail]}
elif "reasons" not in self.details or self.details["reasons"] is None:
self.details["reasons"] = [detail]
else:
self.details["reasons"].append(detail)
def as_http_error(self, name: str):
"""
Xem error này chính là HTTP Error.
@param name - tên loại của HTTP Error.
"""
if name not in HTTPServerErrorDict:
raise ValueError(f"Unknown HTTP error type: {name}")
self.status_code = HTTPServerErrorDict[name]["Status"]
self.code = HTTPServerErrorDict[name]["Code"]
def to_plain(self):
"""
Trả về error là một plain object.
@returns
"""
return {
"message": str(self),
"code": self.code,
"details": self.details,
}


Similarly, in ClientError.py:
A list of HTTP Client Error information.
from typing import Optional
from .AppError import AppError
from .detail import BaseErrorDetail, ErrorDetails
HTTPClientErrorDict = {
"BadRequest": {
"Status": 400,
"Code": "BAD_REQUEST",
},
"Unauthorized": {
"Status": 401,
"Code": "UNAUTHORIZED",
},
"Forbidden": {
"Status": 403,
"Code": "FORBIDDEN",
},
"NotFound": {
"Status": 404,
"Code": "NOT_FOUND",
},
"MethodNotAllowed": {
"Status": 405,
"Code": "METHOD_NOT_ALLOWED",
},
"RequestTimeout": {
"Status": 408,
"Code": "REQUEST_TIMEOUT",
},
"Conflict": {
"Status": 409,
"Code": "CONFLICT",
},
"Gone": {
"Status": 410,
"Code": "GONE",
},
"UnprocessableEntity": {
"Status": 422,
"Code": "UNPROCESSABLE_ENTITY",
},
"TooManyRequests": {
"Status": 429,
"Code": "TOO_MANY_REQUESTS",
},
}
And the code for the ClientError class definition. ClientError will inherit from AppError.
class ClientError(AppError):
"""
Lớp định nghĩa lỗi từ client hoặc là lỗi nội bộ của app.
"""
def __init__(self, message: str, details: Optional[ErrorDetails] = None):
super().__init__(
message,
HTTPClientErrorDict["BadRequest"]["Status"],
HTTPClientErrorDict["BadRequest"]["Code"],
details,
)
def as_http_error(self, name: str):
"""
Xem error này chính là HTTP Error.
@param name - tên loại của HTTP Error.
"""
if name not in HTTPClientErrorDict:
raise ValueError(f"Unknown HTTP client error type: {name}")
self.status_code = HTTPClientErrorDict[name]["Status"]
self.code = HTTPClientErrorDict[name]["Code"]
@staticmethod
def create_error_detail(source: str, desc: str) -> BaseErrorDetail:
"""
Tạo ra một base error detail.
@static
@returns
"""
return {
"source": source,
"desc": desc,
}


In main.py, add the following code.
from .ClientError import ClientError
from .AppError import AppError
def is_standard_error(err):
"""Kiểm tra xem nếu một lỗi có phải là lỗi tiêu chuẩn (AppError, ClientError).
Args:
err: lỗi cần kiểm tra.
Returns:
bool: `True` nếu như là chuẩn lỗi, `False` nếu như không phải
"""
return isinstance(err, ClientError) or isinstance(err, AppError)

Here, we will add a function to check whether the passed object is a standard error.
Finally, in __init__.py, add the following code.
from .AppError import AppError, HTTPServerErrorDict
from .ClientError import ClientError, HTTPClientErrorDict
from .detail import BaseErrorDetail, ErrorDetails
from .main import is_standard_error
__all__ = [
"AppError",
"HTTPServerErrorDict",
"ClientError",
"HTTPClientErrorDict",
"BaseErrorDetail",
"ErrorDetails",
"is_standard_error",
]
