【问题标题】:FastAPI + GraphQL getting error NoneType is callable when raise ExceptionFastAPI + GraphQL 出现错误 NoneType is callable when raise Exception
【发布时间】:2021-05-04 12:39:00
【问题描述】:

我在尝试使用 FastAPI + GraphQL (graphene) 引发验证错误时遇到问题。 我有一个解析器代码:

class Query(graphene.ObjectType):  
  list_categories = graphene.List(CategoryGrapheneModel)
  get_category = graphene.Field(CategoryGrapheneModel, id=graphene.Argument(graphene.Int, required=True))

  @staticmethod
  def resolve_list_categories(parent, info):
    return Category.all()

  @staticmethod
  def resolve_get_category(parent, info, id):
    try:
      category = Category.find_or_fail(id)
      return category
    except ModelNotFound as ex:
      raise Exception('Category not found')

但是我没有得到 400 HTTP 响应和消息,而是得到 500 Internal Server Error with traceback:

Traceback (most recent call last):
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py", line 394, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/fastapi/applications.py", line 199, in __call__
    await super().__call__(scope, receive, send)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    raise exc from None
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
    await self.app(scope, receive, sender)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/routing.py", line 566, in __call__
    await route.handle(scope, receive, send)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/routing.py", line 227, in handle
    await self.app(scope, receive, send)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/graphql.py", line 52, in __call__
    response = await self.handle_graphql(request)
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/graphql.py", line 105, in handle_graphql
    [format_graphql_error(err) for err in result.errors]
  File "/Users/vitalyradchik/Devel/upwork/tipolim/backend/.venv/lib/python3.9/site-packages/starlette/graphql.py", line 105, in <listcomp>
    [format_graphql_error(err) for err in result.errors]
TypeError: 'NoneType' object is not callable

谷歌搜索没有给我一个解决方案。所以请帮忙。

【问题讨论】:

    标签: graphql fastapi graphene-python


    【解决方案1】:

    处理好了。 问题出在 GraphQLApp 中,调用了未定义的 format_graphql_errors (None)。

    为了解决一个问题,我从 GraphQLApp 创建了一个自定义子类,并从 graphql.error.graphql_error 包中将 format_graphql_errors 更改为 format_error。

    import json
    import typing
    
    from starlette.graphql import GraphQLApp
    from starlette import status
    from starlette.background import BackgroundTasks
    from starlette.concurrency import run_in_threadpool
    from starlette.requests import Request
    from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response
    from starlette.types import Receive, Scope, Send
    from graphql.error.graphql_error import format_error
    
    
    class CustomGraphQLApp(GraphQLApp):
      async def handle_graphql(self, request: Request) -> Response:
        if request.method in ("GET", "HEAD"):
          if "text/html" in request.headers.get("Accept", ""):
            if not self.graphiql:
              return PlainTextResponse(
                "Not Found", status_code=status.HTTP_404_NOT_FOUND
              )
            return await self.handle_graphiql(request)
    
          data = request.query_params  # type: typing.Mapping[str, typing.Any]
    
        elif request.method == "POST":
          content_type = request.headers.get("Content-Type", "")
    
          if "application/json" in content_type:
            data = await request.json()
          elif "application/graphql" in content_type:
            body = await request.body()
            text = body.decode()
            data = {"query": text}
          elif "query" in request.query_params:
            data = request.query_params
          else:
            return PlainTextResponse(
                "Unsupported Media Type",
                status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
            )
    
        else:
          return PlainTextResponse(
            "Method Not Allowed", status_code=status.HTTP_405_METHOD_NOT_ALLOWED
          )
    
        try:
          query = data["query"]
          variables = data.get("variables")
          operation_name = data.get("operationName")
        except KeyError:
          return PlainTextResponse(
            "No GraphQL query found in the request",
            status_code=status.HTTP_400_BAD_REQUEST,
          )
    
        background = BackgroundTasks()
        context = {"request": request, "background": background}
    
        result = await self.execute(
          query, variables=variables, context=context, operation_name=operation_name
        )
        error_data = (
          [format_error(err) for err in result.errors]
          if result.errors
          else None
        )
        response_data = {"data": result.data}
        if error_data:
          response_data["errors"] = error_data
        status_code = (
          status.HTTP_400_BAD_REQUEST if result.errors else status.HTTP_200_OK
        )
    
        return JSONResponse(
          response_data, status_code=status_code, background=background
        )
    

    我希望这对其他人有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-25
      • 1970-01-01
      • 2013-01-19
      • 2021-01-18
      • 1970-01-01
      • 2022-12-26
      • 2020-03-09
      相关资源
      最近更新 更多