首页 > 解决方案 > 使用 aiohttp 将 fastapi.Request 转发到另一个 API

问题描述

动机

我正在构建一个执行授权检查的 API 网关,使用 Cloud Run 托管在 GCP 上。

在函数authorize_request中,我想使用 aiohttp 获取一个fastapi.Request对象,将其转发到不同的 API,然后以 a 形式返回响应fastapi.responses.JSONResponse——保留有效负载、标头和状态码。

问题

  1. 是我使用字典将方法字符串映射到函数的方法吗?Pythonic 和/或推荐?有没有更简单的方法?

  2. 我是否保留了可以合理预期的请求/响应的所有字段,并且做得正确?我注意到那里还有其他字段,例如request.proxy, request.cookies,但我认为这些与简单的 API 无关。我错了吗?不包含这些字段会导致 GCP 身份验证出现问题。ETC?

import aiohttp
import asyncio

from typing import Callable
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse


async def authorize_request(request: Request, base_url: str) -> JSONResponse:

    """ Forward a request (if authorized)

    Arguments 
    ---------
    request 
        the request to forward 
    base_url
        the base url of the new domain to forward the request to 

    Returns 
    -------
    response 
        the response generated after the request was forwarded
    """

    # Some authorization checks on the request bearer token take place here, but 
    # have been omitted since they aren't relevant

    # Forward the request
    # I'm aware that I shouldn't be creating a session for each request, this is 
    # a minimal example, and the session will be created/destroyed elsewhere
    async with aiohttp.ClientSession() as session:
        
        # Find the right function for the method in the request.
        # You might notice that I'm not raising a fastapi.HTTPException here.
        # This is intentional, since I'm capturing the request object using 
        # fastapi.middleware, and the relevant decorator doesn't handle these 
        # exceptions gracefully as it otherwise might
        methods = {
            'delete': session.delete,
            'get': session.get,
            'head': session.head,
            'options': session.options,
            'patch': session.patch,
            'post': session.post,
            'put': session.put
        }

        if request.method not in methods:
            message = f"method '{request.method}' not supported"
            return JSONResponse(status_code=400, content={'message': message})
        method = methods[request.method]

        # Make the request
        new_url = f'{base_url}{request.url.path}'
        response = await method(new_url,
                                data=request.body, 
                                headers=request.headers)

        # Return the response
        return JSONResponse(status_code=str(response.status), 
                            content=response.json)


@app.middleware('http')
async def authorize(request: Request, _call_next: Callable):

    """ Catch and process incoming requests 

    Arguments
    ---------
    request 
        the request to authorize and potentially forward
    """
    
    new_url = 'http://some-other-domain.com/api-name'
    return await authorize_request(request, new_url)

标签: pythonfastapiaiohttpstarlette

解决方案


推荐阅读