在 python(Flask、Fastapi)中创建一个代理服务器,它将映射到 s3 存储桶

问题描述 投票:0回答:1

我已在 aws s3 存储桶中部署了静态站点,其 URL 如下所示 -

https://{bucket_name}.s3.{zone}.amazonaws.com/website/{ID}/index.html

我想重定向所有进入服务器的流量,应该像这样转发到 s3 存储桶中 -

username.localhost:9000 -> https://{bucket_name}.s3.{zone}.amazonaws.com/website/{username}/index.html

如果您对nginx有任何建议请分享

我已经尝试过解决方案 -

import uvicorn
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import StreamingResponse
import httpx
import boto3

app = FastAPI()
S3_BUCKET_NAME = ${bucket}

async def proxy_request(request: Request):
    s3_client = boto3.client('s3')

    # Construct the S3 object key based on your requirements
    x = request.url.path.split("/")[-2:]
    y = '/'.join(x)
    s3_key = f"website/{y}"

    try:
        # Get the object from S3
        response = s3_client.get_object(Bucket=S3_BUCKET_NAME, Key=s3_key)
    except s3_client.exceptions.NoSuchKey:
        raise HTTPException(status_code=404, detail="Object not found")

    # Return the S3 object's content back to the client
    return StreamingResponse(
        content=response['Body'].iter_chunks(),
        status_code=200,
        headers={"Content-Type": response['ContentType']},
    )

@app.middleware("http")
async def proxy_middleware(request: Request, call_next):
    try:
        return await proxy_request(request)
    except HTTPException as exc:
        return exc

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=9000)


python flask nginx amazon-s3 proxy
1个回答
0
投票

您要求的解决方案彼此根本不同。您的摘要说代理,但描述说重定向。此外,您的本地主机 URL 有一个子域,仅当您将其映射到主机条目文件中时才可用。我将尝试涵盖 localhost 到 s3 重定向/转发的用例。

如果您想要的只是一个简单的重定向,

@app.route('/{_:path}')
async def s3_redirect(request: Request) -> RedirectResponse:
    # return RedirectResponse(request.url.replace())  # If you want to retain the path/query params
    return RedirectResponse("https://{bucket_name}.s3.{zone}.amazonaws.com/website/{username}/index.html")

如果您正在寻找 nginx 的解决方案,以下配置应该可以解决问题

server {
    listen 9000;

    location / {
        proxy_pass {s3_website};
    }
    # more locations to different endpoints
}

如果用例只是代理,我更喜欢 nginx 或等效的代理应用程序而不是使用 FastAPI。 但是,如果您仍然想使用 FastAPI,我会使用一种有点广泛的解决方案,如下所示。以下示例将充当基本代理服务。您不需要检查存储桶对象,因为 httpx 客户端会为您处理这些事情。

请注意,您的存储桶对象应该可以公开访问才能使用此解决方案。

import logging
from http import HTTPStatus

import httpx
import uvicorn.logging
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.routing import APIRoute

TARGET_URL = "http://127.0.0.1:8080"  # Change this to the URL you want to proxy requests to


async def proxy(request: Request) -> Response:
    """Proxy handler function to forward incoming requests to a target URL.

    Args:
        request: The incoming request object.

    Returns:
        Response: The response object with the forwarded content and headers.
    """
    try:
        async with httpx.AsyncClient() as client:
            body = await request.body()
            # noinspection PyTypeChecker
            response = await client.request(
                method=request.method,
                url=TARGET_URL + request.url.path,
                headers=dict(request.headers),
                cookies=request.cookies,
                params=dict(request.query_params),
                data=body.decode(),
            )

            # If the response content-type is text/html, we need to rewrite links in it
            content_type = response.headers.get("content-type", "")
            if "text/html" in content_type:
                content = response.text
                # Modify content if necessary (e.g., rewriting links)
                # content = modify_html_links(content)
            else:
                content = response.content
            response.headers.pop("content-encoding", None)

            return Response(content, response.status_code, response.headers, content_type)
    except httpx.RequestError as exc:
        logging.getLogger("uvicorn.error").error(exc)
        raise HTTPException(status_code=HTTPStatus.BAD_GATEWAY.value, detail=HTTPStatus.BAD_GATEWAY.phrase)


if __name__ == '__main__':
    uvicorn.run(
        app=FastAPI(
            routes=[
                APIRoute("/{_:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"], endpoint=proxy)
            ]
        )
    )
© www.soinside.com 2019 - 2024. All rights reserved.