Tartiflette + FastApi身份验证

时间:2020-10-23 17:19:52

标签: graphql fastapi

我正在使用tartiflette-asgi使用FastApi构建tartiflette应用程序,但我找不到一种使常规FastApi身份验证或依赖项注入有效的方法。

问题在于如何构建和安装tartiflette应用程序。做的时候

app = FastApi()

gql_app = TartifletteApp(..)
app.mount("/graphql", gql_app)

我无法指定依赖项来执行我的标头验证。我已经尝试过使用FastApi include_router,但是它根本无法与TartifletteApp一起使用。我也尝试过像

这样的小技巧
gql_app = TartifletteApp(..)

app.include_router(
    gql_app.router,
    prefix="/graphql",
    # dependencies=[Depends(get_current_user)],   # here I would add a token and get a user
)

我得到了错误

File "/usr/local/lib/python3.6/site-packages/uvicorn/protocols/http/h11_impl.py", line 389, in run_asgi
     result = await app(self.scope, self.receive, self.send)
   File "/usr/local/lib/python3.6/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
     return await self.app(scope, receive, send)
   File "/usr/local/lib/python3.6/site-packages/fastapi/applications.py", line 181, in __call__
     await super().__call__(scope, receive, send)  # pragma: no cover
   File "/usr/local/lib/python3.6/site-packages/starlette/applications.py", line 111, in __call__
     await self.middleware_stack(scope, receive, send)
   File "/usr/local/lib/python3.6/site-packages/starlette/middleware/errors.py", line 181, in __call__
     raise exc from None
   File "/usr/local/lib/python3.6/site-packages/starlette/middleware/errors.py", line 159, in __call__
     await self.app(scope, receive, _send)
   File "/usr/local/lib/python3.6/site-packages/starlette/exceptions.py", line 82, in __call__
     raise exc from None
   File "/usr/local/lib/python3.6/site-packages/starlette/exceptions.py", line 71, in __call__
     await self.app(scope, receive, sender)
   File "/usr/local/lib/python3.6/site-packages/starlette/routing.py", line 566, in __call__
     await route.handle(scope, receive, send)
   File "/usr/local/lib/python3.6/site-packages/starlette/routing.py", line 227, in handle
     await self.app(scope, receive, send)
   File "/usr/local/lib/python3.6/site-packages/tartiflette_asgi/_endpoints.py", line 84, in dispatch
     graphiql = get_graphql_config(request).graphiql
   File "/usr/local/lib/python3.6/site-packages/tartiflette_asgi/_middleware.py", line 18, in get_graphql_config
     config = conn["graphql"]
   File "/usr/local/lib/python3.6/site-packages/starlette/requests.py", line 68, in __getitem__
     return self.scope[key]
 KeyError: 'graphql'

我可以将标头验证实现为graphql中间件,但我希望可以在FastApi级别上做到这一点,以便将其应用于每个端点。

关于如何解决此问题的任何建议?

2 个答案:

答案 0 :(得分:0)

首先创建一个基本身份验证,然后将其添加到Dependencies中,而不是获取当前用户,这将向您的端点添加一个基本身份验证

from fastapi.security import HTTPBasic, HTTPBasicCredentials

security = HTTPBasic()

app.include_router(
gql_app.router,
prefix="/graphql",
dependencies=[Depends(security)], 
)

答案 1 :(得分:0)

我设法在没有tartiflette-asgi的情况下解决了这个问题。解决方案已发布here

它看起来像:

ps axu |                 ##Running ps axu command to get the process in system. Sending its output as standard input to awk command.
awk '                    ##Starting awk program from here.
NR==1 {print;next}       ##If its header/first line then simply print it and move cursor to next line.
$3 > 0{                  ##Checking condition if 3rd field is greater than 0 then do following.
  print $0,strftime(" %a %d %b %Y %H:%M:%S %p %Z", systime())  ##Using systime function to get time in mentioned different options manner with strftime.
}' 


%a --> The locale’s abbreviated weekday name.
%d --> The day of the month as a decimal number (01–31).
%b --> The locale’s abbreviated month name.
%Y --> The full year as a decimal number (e.g. 2011).
%H --> The hour (24-hour clock) as a decimal number (00–23).
%M --> The minute as a decimal number (00–59).
%S --> The second as a decimal number (00–60).
%p --> The locale’s equivalent of the AM/PM designations associated with a 12-hour clock.
%Z --> The time zone name or abbreviation; no characters if no time zone is determinable.

所以我可以做

import os
import json
import typing

from starlette.background import BackgroundTasks
from starlette.datastructures import QueryParams
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response

from tartiflette import Engine


class GraphQLApp:

    def __init__(self, app, modules, schema_path=None, error_coercer=None):
        self.engine = Engine(
            sdl=schema_path or os.path.join(os.path.dirname(__file__), "schema"),
            modules=modules,
            error_coercer=error_coercer,
        )

        app.on_event("startup")(self._cook)

    async def _cook(self):
        await self.engine.cook()

    def _build_context(self, **kwargs):
        return kwargs or {}  # add custom logic when needed here

    async def _get_response(self, request: Request, data: QueryParams, context: dict) -> Response:
        try:
            query = data["query"]
        except KeyError:
            return PlainTextResponse("No GraphQL query found in the request", 400)

        def _format_error(error: typing.Any) -> dict:
            import ast

            try:
                return ast.literal_eval(str(error))
            except ValueError:
                return {"message": "Internal Server Error"}

        background = BackgroundTasks()
        context = {"req": request, "background": background, **self._build_context(**context)}

        result: dict = await self.engine.execute(
            query,
            context=context,
            variables=data.get("variables"),
            operation_name=data.get("operationName"),
        )

        content = {"data": result["data"]}
        has_errors = "errors" in result
        if has_errors:
            content["errors"] = [_format_error(error) for error in result["errors"]]

        status = 400 if has_errors else 200
        return JSONResponse(content=content, status_code=status, background=background)

    async def process_request(self, request: Request, context: dict = None) -> Response:
        content_type = request.headers.get("Content-Type", "")
        if "application/json" in content_type:
            try:
                data = await request.json()
            except json.JSONDecodeError:
                return JSONResponse({"error": "Invalid JSON."}, 400)

        elif "application/graphql" in content_type:
            body = await request.body()
            data = {"query": body.decode()}

        elif "query" in request.query_params:
            data = request.query_params

        else:
            return PlainTextResponse("Unsupported Media Type", 415)

        return await self._get_response(request, data=data, context=context or {})
相关问题