error-handling-rfc9457

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

RFC 9457 Problem Details

RFC 9457 问题详情

Standardize API error responses with machine-readable problem details.
使用机器可读的问题详情标准化API错误响应。

RFC 9457 vs RFC 7807

RFC 9457 与 RFC 7807 对比

FeatureRFC 7807 (Old)RFC 9457 (Current)
StatusObsoleteActive Standard
Multiple problemsNot specifiedExplicitly supported
Error registryNoYes (IANA registry)
Extension fieldsImplicitExplicitly allowed
特性RFC 7807(旧版)RFC 9457(现行标准)
状态已废弃现行标准
多问题支持未明确规定明确支持
错误注册中心有(IANA注册中心)
扩展字段隐式支持明确允许

Problem Details Schema

问题详情 Schema

python
from pydantic import BaseModel, Field, HttpUrl
from typing import Any

class ProblemDetail(BaseModel):
    """RFC 9457 Problem Details for HTTP APIs."""

    type: HttpUrl = Field(
        default="about:blank",
        description="URI identifying the problem type"
    )
    title: str = Field(
        description="Short, human-readable summary"
    )
    status: int = Field(
        ge=400, le=599,
        description="HTTP status code"
    )
    detail: str | None = Field(
        default=None,
        description="Human-readable explanation specific to this occurrence"
    )
    instance: str | None = Field(
        default=None,
        description="URI reference identifying the specific occurrence"
    )

    model_config = {"extra": "allow"}  # Allow extension fields
python
from pydantic import BaseModel, Field, HttpUrl
from typing import Any

class ProblemDetail(BaseModel):
    """RFC 9457 Problem Details for HTTP APIs."""

    type: HttpUrl = Field(
        default="about:blank",
        description="URI identifying the problem type"
    )
    title: str = Field(
        description="Short, human-readable summary"
    )
    status: int = Field(
        ge=400, le=599,
        description="HTTP status code"
    )
    detail: str | None = Field(
        default=None,
        description="Human-readable explanation specific to this occurrence"
    )
    instance: str | None = Field(
        default=None,
        description="URI reference identifying the specific occurrence"
    )

    model_config = {"extra": "allow"}  # Allow extension fields

FastAPI Integration

FastAPI 集成

Exception Classes

异常类

python
from fastapi import HTTPException
from typing import Any

class ProblemException(HTTPException):
    """Base exception for RFC 9457 problem details."""

    def __init__(
        self,
        status_code: int,
        problem_type: str,
        title: str,
        detail: str | None = None,
        instance: str | None = None,
        **extensions: Any,
    ):
        self.problem_type = problem_type
        self.title = title
        self.detail = detail
        self.instance = instance
        self.extensions = extensions
        super().__init__(status_code=status_code, detail=detail)

    def to_problem_detail(self) -> dict[str, Any]:
        result = {
            "type": self.problem_type,
            "title": self.title,
            "status": self.status_code,
        }
        if self.detail:
            result["detail"] = self.detail
        if self.instance:
            result["instance"] = self.instance
        result.update(self.extensions)
        return result
python
from fastapi import HTTPException
from typing import Any

class ProblemException(HTTPException):
    """Base exception for RFC 9457 problem details."""

    def __init__(
        self,
        status_code: int,
        problem_type: str,
        title: str,
        detail: str | None = None,
        instance: str | None = None,
        **extensions: Any,
    ):
        self.problem_type = problem_type
        self.title = title
        self.detail = detail
        self.instance = instance
        self.extensions = extensions
        super().__init__(status_code=status_code, detail=detail)

    def to_problem_detail(self) -> dict[str, Any]:
        result = {
            "type": self.problem_type,
            "title": self.title,
            "status": self.status_code,
        }
        if self.detail:
            result["detail"] = self.detail
        if self.instance:
            result["instance"] = self.instance
        result.update(self.extensions)
        return result

Specific Problem Types

特定问题类型

python
class ValidationProblem(ProblemException):
    def __init__(self, errors: list[dict], instance: str | None = None):
        super().__init__(
            status_code=422,
            problem_type="https://api.example.com/problems/validation-error",
            title="Validation Error",
            detail="One or more fields failed validation",
            instance=instance,
            errors=errors,  # Extension field
        )

class NotFoundProblem(ProblemException):
    def __init__(self, resource: str, resource_id: str, instance: str | None = None):
        super().__init__(
            status_code=404,
            problem_type="https://api.example.com/problems/resource-not-found",
            title="Resource Not Found",
            detail=f"{resource} with ID '{resource_id}' was not found",
            instance=instance,
            resource=resource,
            resource_id=resource_id,
        )

class RateLimitProblem(ProblemException):
    def __init__(self, retry_after: int, instance: str | None = None):
        super().__init__(
            status_code=429,
            problem_type="https://api.example.com/problems/rate-limit-exceeded",
            title="Too Many Requests",
            detail="Rate limit exceeded. Please retry later.",
            instance=instance,
            retry_after=retry_after,
        )
python
class ValidationProblem(ProblemException):
    def __init__(self, errors: list[dict], instance: str | None = None):
        super().__init__(
            status_code=422,
            problem_type="https://api.example.com/problems/validation-error",
            title="Validation Error",
            detail="One or more fields failed validation",
            instance=instance,
            errors=errors,  # Extension field
        )

class NotFoundProblem(ProblemException):
    def __init__(self, resource: str, resource_id: str, instance: str | None = None):
        super().__init__(
            status_code=404,
            problem_type="https://api.example.com/problems/resource-not-found",
            title="Resource Not Found",
            detail=f"{resource} with ID '{resource_id}' was not found",
            instance=instance,
            resource=resource,
            resource_id=resource_id,
        )

class RateLimitProblem(ProblemException):
    def __init__(self, retry_after: int, instance: str | None = None):
        super().__init__(
            status_code=429,
            problem_type="https://api.example.com/problems/rate-limit-exceeded",
            title="Too Many Requests",
            detail="Rate limit exceeded. Please retry later.",
            instance=instance,
            retry_after=retry_after,
        )

Exception Handler

异常处理器

python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError

app = FastAPI()

@app.exception_handler(ProblemException)
async def problem_exception_handler(request: Request, exc: ProblemException):
    return JSONResponse(
        status_code=exc.status_code,
        content=exc.to_problem_detail(),
        media_type="application/problem+json",
    )

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    errors = [
        {"field": ".".join(str(loc) for loc in err["loc"]), "message": err["msg"]}
        for err in exc.errors()
    ]
    problem = ValidationProblem(errors=errors, instance=str(request.url))
    return JSONResponse(
        status_code=422,
        content=problem.to_problem_detail(),
        media_type="application/problem+json",
    )

@app.exception_handler(Exception)
async def generic_exception_handler(request: Request, exc: Exception):
    return JSONResponse(
        status_code=500,
        content={
            "type": "https://api.example.com/problems/internal-error",
            "title": "Internal Server Error",
            "status": 500,
            "detail": "An unexpected error occurred",
            "instance": str(request.url),
        },
        media_type="application/problem+json",
    )
python
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError

app = FastAPI()

@app.exception_handler(ProblemException)
async def problem_exception_handler(request: Request, exc: ProblemException):
    return JSONResponse(
        status_code=exc.status_code,
        content=exc.to_problem_detail(),
        media_type="application/problem+json",
    )

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    errors = [
        {"field": ".".join(str(loc) for loc in err["loc"]), "message": err["msg"]}
        for err in exc.errors()
    ]
    problem = ValidationProblem(errors=errors, instance=str(request.url))
    return JSONResponse(
        status_code=422,
        content=problem.to_problem_detail(),
        media_type="application/problem+json",
    )

@app.exception_handler(Exception)
async def generic_exception_handler(request: Request, exc: Exception):
    return JSONResponse(
        status_code=500,
        content={
            "type": "https://api.example.com/problems/internal-error",
            "title": "Internal Server Error",
            "status": 500,
            "detail": "An unexpected error occurred",
            "instance": str(request.url),
        },
        media_type="application/problem+json",
    )

Usage in Endpoints

端点中的使用示例

python
@router.get("/api/v1/analyses/{analysis_id}")
async def get_analysis(
    analysis_id: str,
    request: Request,
    service: AnalysisService = Depends(get_analysis_service),
):
    analysis = await service.get_by_id(analysis_id)
    if not analysis:
        raise NotFoundProblem(
            resource="Analysis",
            resource_id=analysis_id,
            instance=str(request.url),
        )
    return analysis
python
@router.get("/api/v1/analyses/{analysis_id}")
async def get_analysis(
    analysis_id: str,
    request: Request,
    service: AnalysisService = Depends(get_analysis_service),
):
    analysis = await service.get_by_id(analysis_id)
    if not analysis:
        raise NotFoundProblem(
            resource="Analysis",
            resource_id=analysis_id,
            instance=str(request.url),
        )
    return analysis

Response Examples

响应示例

404 Not Found

404 资源未找到

json
{
  "type": "https://api.example.com/problems/resource-not-found",
  "title": "Resource Not Found",
  "status": 404,
  "detail": "Analysis with ID 'abc123' was not found",
  "instance": "/api/v1/analyses/abc123",
  "resource": "Analysis",
  "resource_id": "abc123"
}
json
{
  "type": "https://api.example.com/problems/resource-not-found",
  "title": "Resource Not Found",
  "status": 404,
  "detail": "Analysis with ID 'abc123' was not found",
  "instance": "/api/v1/analyses/abc123",
  "resource": "Analysis",
  "resource_id": "abc123"
}

422 Validation Error

422 验证错误

json
{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "One or more fields failed validation",
  "instance": "/api/v1/analyses",
  "errors": [
    {"field": "source_url", "message": "Invalid URL format"},
    {"field": "depth", "message": "Must be between 1 and 3"}
  ]
}
json
{
  "type": "https://api.example.com/problems/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "One or more fields failed validation",
  "instance": "/api/v1/analyses",
  "errors": [
    {"field": "source_url", "message": "Invalid URL format"},
    {"field": "depth", "message": "Must be between 1 and 3"}
  ]
}

429 Rate Limited

429 请求超限

json
{
  "type": "https://api.example.com/problems/rate-limit-exceeded",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Please retry later.",
  "instance": "/api/v1/analyses",
  "retry_after": 60
}
json
{
  "type": "https://api.example.com/problems/rate-limit-exceeded",
  "title": "Too Many Requests",
  "status": 429,
  "detail": "Rate limit exceeded. Please retry later.",
  "instance": "/api/v1/analyses",
  "retry_after": 60
}

Error Type Registry

错误类型注册中心

python
undefined
python
undefined

app/core/problem_types.py

app/core/problem_types.py

PROBLEM_TYPES = { "validation-error": { "uri": "https://api.example.com/problems/validation-error", "title": "Validation Error", "status": 422, }, "resource-not-found": { "uri": "https://api.example.com/problems/resource-not-found", "title": "Resource Not Found", "status": 404, }, "rate-limit-exceeded": { "uri": "https://api.example.com/problems/rate-limit-exceeded", "title": "Too Many Requests", "status": 429, }, "unauthorized": { "uri": "https://api.example.com/problems/unauthorized", "title": "Unauthorized", "status": 401, }, "forbidden": { "uri": "https://api.example.com/problems/forbidden", "title": "Forbidden", "status": 403, }, "conflict": { "uri": "https://api.example.com/problems/conflict", "title": "Conflict", "status": 409, }, "internal-error": { "uri": "https://api.example.com/problems/internal-error", "title": "Internal Server Error", "status": 500, }, }
undefined
PROBLEM_TYPES = { "validation-error": { "uri": "https://api.example.com/problems/validation-error", "title": "Validation Error", "status": 422, }, "resource-not-found": { "uri": "https://api.example.com/problems/resource-not-found", "title": "Resource Not Found", "status": 404, }, "rate-limit-exceeded": { "uri": "https://api.example.com/problems/rate-limit-exceeded", "title": "Too Many Requests", "status": 429, }, "unauthorized": { "uri": "https://api.example.com/problems/unauthorized", "title": "Unauthorized", "status": 401, }, "forbidden": { "uri": "https://api.example.com/problems/forbidden", "title": "Forbidden", "status": 403, }, "conflict": { "uri": "https://api.example.com/problems/conflict", "title": "Conflict", "status": 409, }, "internal-error": { "uri": "https://api.example.com/problems/internal-error", "title": "Internal Server Error", "status": 500, }, }
undefined

Anti-Patterns (FORBIDDEN)

反模式(禁止使用)

python
undefined
python
undefined

NEVER return plain text errors

NEVER return plain text errors

return Response("Not found", status_code=404)
return Response("Not found", status_code=404)

NEVER use inconsistent error formats

NEVER use inconsistent error formats

return {"error": "Not found"} # Different from other errors return {"message": "Validation failed", "errors": [...]}
return {"error": "Not found"} # Different from other errors return {"message": "Validation failed", "errors": [...]}

NEVER expose internal details in production

NEVER expose internal details in production

return {"detail": str(exc), "traceback": traceback.format_exc()}
return {"detail": str(exc), "traceback": traceback.format_exc()}

NEVER use generic 500 for everything

NEVER use generic 500 for everything

except Exception: raise HTTPException(500, "Something went wrong")
undefined
except Exception: raise HTTPException(500, "Something went wrong")
undefined

Key Decisions

关键决策

DecisionRecommendation
Media type
application/problem+json
Type URIUse your API domain +
/problems/
DetailInclude only for user-actionable info
ExtensionsUse for machine-readable context
LoggingLog problem types for monitoring
决策建议
媒体类型
application/problem+json
类型URI使用你的API域名 +
/problems/
详情仅包含用户可采取行动的信息
扩展字段用于机器可读的上下文信息
日志记录记录问题类型以用于监控

Related Skills

相关技能

  • api-design-framework
    - REST API patterns
  • observability-monitoring
    - Error tracking
  • input-validation
    - Validation patterns
  • api-design-framework
    - REST API 设计模式
  • observability-monitoring
    - 错误追踪
  • input-validation
    - 验证模式

Capability Details

能力详情

problem-details

problem-details

Keywords: problem details, RFC 9457, RFC 7807, structured error Solves:
  • How to standardize API error responses?
  • What format for API errors?
关键词: 问题详情, RFC 9457, RFC 7807, 结构化错误 解决的问题:
  • 如何标准化API错误响应?
  • API错误应采用何种格式?

fastapi-errors

fastapi-errors

Keywords: fastapi exception, error handler, HTTPException Solves:
  • How to handle errors in FastAPI?
  • Custom exception handlers
关键词: fastapi 异常, 错误处理器, HTTPException 解决的问题:
  • 如何在FastAPI中处理错误?
  • 自定义异常处理器

error-registry

error-registry

Keywords: error registry, problem types, error catalog Solves:
  • How to document all API errors?
  • Error type management
关键词: 错误注册中心, 问题类型, 错误目录 解决的问题:
  • 如何记录所有API错误?
  • 错误类型管理