|
| 1 | +from fastapi import FastAPI, Request, HTTPException |
| 2 | +from fastapi.responses import StreamingResponse |
| 3 | +from fastapi.middleware.cors import CORSMiddleware |
| 4 | +import httpx |
| 5 | + |
| 6 | +app = FastAPI() |
| 7 | + |
| 8 | +# 设置跨域支持 |
| 9 | +app.add_middleware( |
| 10 | + CORSMiddleware, |
| 11 | + allow_origins=["*"], # 允许所有域名访问,生产环境中可以指定具体域名 |
| 12 | + allow_credentials=True, |
| 13 | + allow_methods=["*"], # 允许所有方法 |
| 14 | + allow_headers=["*"], # 允许所有请求头 |
| 15 | +) |
| 16 | + |
| 17 | + |
| 18 | +@app.api_route('/proxy/{full_url:path}', methods=['GET', 'POST', 'PUT', 'DELETE']) |
| 19 | +async def proxy(request: Request, full_url: str): |
| 20 | + target_url = full_url if full_url.startswith("http") else f"http://{full_url}" |
| 21 | + timeout = httpx.Timeout(60.0, read=120.0) |
| 22 | + |
| 23 | + async with httpx.AsyncClient(timeout=timeout) as client: |
| 24 | + try: |
| 25 | + # 处理 GET 请求 |
| 26 | + if request.method == "GET": |
| 27 | + response = await client.get(target_url, headers=request.headers, params=request.query_params, |
| 28 | + stream=True) |
| 29 | + |
| 30 | + # 处理 POST 请求 |
| 31 | + elif request.method == "POST": |
| 32 | + content_type = request.headers.get('content-type', '') # 默认空字符串 |
| 33 | + |
| 34 | + # 处理 JSON 数据 |
| 35 | + if 'application/json' in content_type: |
| 36 | + json_data = await request.json() |
| 37 | + response = await client.post(target_url, json=json_data, headers=request.headers, stream=True) |
| 38 | + |
| 39 | + # 处理 URL 编码的表单数据 |
| 40 | + elif 'application/x-www-form-urlencoded' in content_type: |
| 41 | + form_data = dict(await request.form()) |
| 42 | + response = await client.post(target_url, data=form_data, headers=request.headers, stream=True) |
| 43 | + |
| 44 | + # 处理 multipart/form-data 表单数据 |
| 45 | + elif 'multipart/form-data' in content_type: |
| 46 | + form = await request.form() |
| 47 | + files = {key: (form[key].filename, await form[key].read()) for key in form if |
| 48 | + isinstance(form[key], UploadFile)} |
| 49 | + fields = {key: form[key] for key in form if not isinstance(form[key], UploadFile)} |
| 50 | + response = await client.post(target_url, data=fields, files=files, headers=request.headers, |
| 51 | + stream=True) |
| 52 | + |
| 53 | + # 如果没有指定 Content-Type,处理默认的 URL 编码表单数据 |
| 54 | + elif content_type == '' or 'text/plain' in content_type: |
| 55 | + form_data = dict(await request.form()) |
| 56 | + response = await client.post(target_url, data=form_data, headers=request.headers, stream=True) |
| 57 | + |
| 58 | + else: |
| 59 | + raise HTTPException(status_code=400, detail="Unsupported Content-Type for POST request") |
| 60 | + |
| 61 | + # 处理 PUT 请求 |
| 62 | + elif request.method == "PUT": |
| 63 | + json_data = await request.json() |
| 64 | + response = await client.put(target_url, json=json_data, headers=request.headers, stream=True) |
| 65 | + |
| 66 | + # 处理 DELETE 请求 |
| 67 | + elif request.method == "DELETE": |
| 68 | + response = await client.delete(target_url, headers=request.headers, stream=True) |
| 69 | + |
| 70 | + else: |
| 71 | + raise HTTPException(status_code=405, detail="Method not allowed") |
| 72 | + |
| 73 | + except httpx.RequestError as e: |
| 74 | + raise HTTPException(status_code=500, detail=str(e)) |
| 75 | + |
| 76 | + return StreamingResponse(response.aiter_bytes(), status_code=response.status_code, headers=dict(response.headers)) |
| 77 | + |
| 78 | + |
| 79 | +if __name__ == "__main__": |
| 80 | + import uvicorn |
| 81 | + |
| 82 | + uvicorn.run(app, host="0.0.0.0", port=8000) |
0 commit comments