Catching general exceptions in Fast API and Custom Message for custom exceptions.

huangapple go评论64阅读模式
英文:

Catching general exceptions in Fast API and Custom Message for custom exceptions

问题

I am trying to implement Exception Handling in FastAPI.

  1. 我尝试在FastAPI中实现异常处理。我在Try块中使用Exception块,尝试实现通用异常处理,如果除了Try块中提到的HTTP异常之外发生其他异常。在通用异常中,我想以格式{ "details": "实际异常详情" }打印实际异常的详细信息。然而,在下面的实现中,即使在Try中引发任何异常,它也会进入Except块并打印该异常。

  2. 对于Try块中特定的异常,我尝试使用一个CustomException类,该类可以打印自定义异常的详细信息,提供异常的名称。

自定义异常类

class CustomException(Exception):
    def __init__(self, name: str):
        self.name = name

在第二个问题中,我尝试以下方式实现,但这种方式不起作用

else:
    raise HTTPException(status_code = 422, detail = CustomException(name = "Invalid Student Rank"), headers={"Error": "Invalid Student Rank"})

错误处理如下:

@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
    print(f"HTTP Error: {repr(exc)}")
    return await http_exception_handler(request, exc)
    
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    print(f"Invalid Data: {exc}")
    return await request_validation_exception_handler(request, exc)
英文:

I am trying to implement Exception Handling in FastAPI.

  1. I have Try along with Exception block in which I am trying to implement Generic Exception if any other exception occurs than HTTPExceptions mentioned in Try Block.
    In generic exception, I would like to print details of actual exception in format { "details": "Actual Exception Details" }
    However, in below implementation, even though any exception in Try is raised it goes to Except block and prints that exception.

  2. I Try block specific exceptions, I am trying to have a CustomException class that can print the details of custom exception provded name of exception.

@app.get("/school/studentclass")
async def get_student_class(id: int, stream: str, rank: int = 1):
    try:      
        student = Student(id=id,stream=stream,rank=1)
        if (student.id != 0):
            if (student.stream is not None or  student.stream != ''):
                if(student.rank!= 0):
                   // Student Class is used to represent API Output : Student_Class_Name and Student_Class_Room and Student_Class_Teacher
                    studentClass = StudentClass()
                    // Processing Logic to get Student Class 
                                        
                    return JSONResponse(content=studentClass)
                else:
                    raise HTTPException(status_code = 422, detail = "Student Rank is not right", headers={"X-Error": "Student Rank is not right"})
            else:
                raise HTTPException(status_code = 422, detail="Student Stream is not right", headers={"X-Error": "Student Stream is not right"})
        else:
            raise HTTPException(status_code = 422, detail="Student Id is not right", headers={"X-Error": "Student Id is not right"})
except Exception as e:
        raise HTTPException(status_code = 418, detail=str(e))

Custom Exception Class

class CustomException(Exception):
    def __init__(self, name: str):
        self.name = name

For the second question, I tried to implement as below but it isn't working this way

else:
    raise HTTPException(status_code = 422, detail = CustomException(name = "Invalid Student Rank"), headers={"Error": "Invalid Student Rank"})

The Error Handling is done as follows:

@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
    print(f"HTTP Error: {repr(exc)}")
    return await http_exception_handler(request, exc)
    
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    print(f"Invalid Data: {exc}")
    return await request_validation_exception_handler(request, exc)

答案1

得分: 1

你可以使用Pydantic中的conintconstr来限制参数的允许值范围,这还会在文档中提供必要的信息。如果包含在文档中,它们将自动生成包含有关值过低的描述信息的422错误。

我还强烈建议遵循“尽早返回”规则(即使在这里使用异常)。然而,在这种情况下,我们可以让pydantic为我们进行所有验证,并自动使FastAPI在提交的值不符合规范时返回422错误:

from pydantic import constr, conint

@app.get("/school/studentclass")
async def get_student_class(id: conint(gt=0), stream: constr(min_length=1), rank: conint(gt=0) = 1):
    student = Student(id=id, stream=stream, rank=rank)

    # 学生班级用于表示API输出:Student_Class_Name和Student_Class_Room和Student_Class_Teacher

    student_class = StudentClass()
    # 获取学生班级的处理逻辑

    return {"content": student_class)  # 我还建议避免使用"content",因为它毫无意义

我不清楚你的最后一个except是用来捕获什么异常的(即在Student内部可以抛出什么异常),但你不应该自己处理它(如果抛出了自定义异常,使用FastAPI的内置异常处理来处理它)。

如果你想更进一步,如果这是你经常做的事情,将生成学生对象的过程移到一个单独的依赖项中:

from pydantic import constr, conint
from typing import Annotated


async def student_from_url_id(id: conint(gt=0), stream: constr(min_length=1), rank: conint(gt=0) = 1):
    return Student(id=id, stream=stream, rank=rank)


@app.get("/school/studentclass")
async def student_class_endpoint(student: Annotated[Student, Depends(student_from_url_id)]):
    student_class = StudentClass()
    # 获取学生班级的处理逻辑

    return {"content": student_class)

这样,你的依赖项可以在需要从URL参数获取学生的任何地方重复使用。

{
  "detail": [
    {
      "loc": [
        "query",
        "rank"
      ],
      "msg": "ensure this value is greater than 0",
      "type": "value_error.number.not_gt",
      "ctx": {
        "limit_value": 0
      }
    }
  ]
}
英文:

You can use conint and constr from Pydantic to limit the ranges of allowed values for your parameters, which will also supply the necessary information if included in documentation. They will generate 422 errors automagically that includes descriptive information about values being too low.

I also strongly suggest following the "return early" rule (even if you're using exceptions here). However, in this case we can make pydantic do all the validation for us, and automagically have FastAPI return a 422 error if the submitted values doesn't conform:

from pydantic import constr, conint

@app.get("/school/studentclass")
async def get_student_class(id: conint(gt=0), stream: constr(min_length=1), rank: conint(gt=0) = 1):
    student = Student(id=id, stream=stream, rank=rank)

    # Student Class is used to represent API Output : Student_Class_Name and Student_Class_Room and Student_Class_Teacher
    
    student_class = StudentClass()
    # Processing Logic to get Student Class 
                                        
    return {"content": student_class)  # I'd also recommend avoiding "content" here as it conveys no meaning at all

I have no idea what your final except is meant to catch (i.e. what exception can be thrown inside Student), but you shouldn't have to handle that yourself (if a custom exception is thrown, use FastAPI's built-in exception handling to handle it).

If you want to take it a bit step further, where this is a common thing you do, move generating the Student object to a separate dependency instead:

from pydantic import constr, conint
from typing import Annotated


async def student_from_url_id(id: conint(gt=0), stream: constr(min_length=1), rank: conint(gt=0) = 1):
    return Student(id=id, stream=stream, rank=rank)


@app.get("/school/studentclass")
async def student_class_endpoint(student: Annotated[Student, Depends(student_from_url_id)])
    student_class = StudentClass()
    # Processing Logic to get Student Class 
                                        
    return {"content": student_class)

That way your dependency can be re-used everywhere you need to fetch a student from a URL-argument for example.

{
  detail: [
    {
      loc: [
        "query",
        "rank"
      ],
      msg: "ensure this value is greater than 0",
      type: "value_error.number.not_gt",
      ctx: {
        limit_value: 0
      }
    }
  ]
}

huangapple
  • 本文由 发表于 2023年5月15日 06:25:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76249931.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定