Paddlex服务化代理处理跨域、替换Response中Json key
背景
前端直接调用paddlex serve服务时,由于前端会发送OPTIONS请求,paddlex默认会拒绝跨域请求。
另外由于前端限制,需要把Response中errorCode、errorMsg替换成code、message。
方案
Nginx
通过 Nginx 的 sub_filter 或 OpenResty 的 Lua 脚本修改 JSON 键名(适用于简单替换)。
Nginx是替换,存在把正常的内容替换的情况,不推荐。
Nginx 配置示例:
server {
listen 80;
server_name your_domain.com;
location /predict {
proxy_pass http://localhost:8000; # PaddleX 服务地址
sub_filter '"errorCode":' '"code":'; # 替换字段
sub_filter '"errorMsg":' '"message":';
sub_filter '"result":' '"data":';
sub_filter_once off;
sub_filter_types application/json;
}
}
修改配置后重载 Nginx:
sudo nginx -t && sudo systemctl reload nginx
uvicorn(Python代理)
Uvicorn 作为一款基于异步 I/O 的 ASGI 服务器,在高并发场景下表现优异,但其性能也受到配置、应用逻辑、硬件资源等因素影响。
1.编写代理
新建proxy.py文件:
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
import requests
import logging
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI()
# 跨域配置
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
PADDLEX_SERVER = "http://localhost:8080"
@app.post("/ocr")
async def predict(request: Request):
try:
# 1. 获取客户端请求体
client_json = await request.json()
# 2. 转发请求到 PaddleX 服务
response = requests.post(
f"{PADDLEX_SERVER}/ocr",
json=client_json,
timeout=30 # 添加超时控制
)
response.raise_for_status() # 自动抛出 HTTP 错误
# 3. 处理原始响应数据
try:
response_data = response.json()
except requests.exceptions.JSONDecodeError:
logger.error("PaddleX 返回了非 JSON 响应")
raise HTTPException(502, "后端服务响应格式错误")
# 4. 字段替换逻辑
if "errorCode" in response_data:
response_data["code"] = response_data.pop("errorCode")
if "errorMsg" in response_data:
response_data["message"] = response_data.pop("errorMsg")
if "result" in response_data:
response_data["data"] = response_data.pop("result")
if "code" in response_data and response_data["code"] == 0:
response_data["code"] = 200
# 5. 返回修改后的响应(移除 Content-Length 头)
headers = dict(response.headers)
headers.pop("Content-Length", None) # 删除原始 Content-Length
headers.pop("content-length", None) # 小写 key 兼容
headers.pop("server", None) # 小写 key 兼容
headers.pop("date", None) # 小写 key 兼容
return JSONResponse(
content=response_data,
status_code=response.status_code,
headers=headers
)
except requests.exceptions.RequestException as e:
logger.error(f"请求后端服务失败: {str(e)}")
raise HTTPException(504, f"后端服务通信失败: {str(e)}")
except Exception as e:
logger.error(f"未知错误: {str(e)}")
raise HTTPException(500, "服务器内部错误")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8090)
2.启动代理
nohup python paddleImageOcrProxy.py > paddleImageOcrProxy.log 2>&1 &
查看日志:
$ tail -fn 200 paddleImageOcrProxy.log
INFO: Started server process [18566]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8090 (Press CTRL+C to quit)
INFO: 10.90.201.61:10346 - "POST /ocr HTTP/1.1" 200 OK
INFO: 10.90.201.61:10357 - "POST /ocr HTTP/1.1" 200 OK
INFO: 10.90.201.61:10357 - "POST /ocr HTTP/1.1" 200 OK
INFO: 10.90.200.182:52519 - "OPTIONS /ocr HTTP/1.1" 200 OK
INFO: 10.90.200.182:52519 - "POST /ocr HTTP/1.1" 200 OK