Java请求进入Python FastAPI 后,请求体为空,参数不合法 目录一问题背景二探索问题的过程三问题的解释四解决方案一问题背景最近在做 Python FastAPI 和 Java spring boot 对接接口的事情。FastAPI 接口写好后postman 和 swage 测试都正常但是 Java 服务使用HttpExchange怎么着都请求 400参数不合法。检查了请求参数看着也没问题也都有值。Python 服务经过 debug 日志发现请求体是空app.post(/test)asyncdefaudit_product_raw_debug(request:Request):# 打印原始 bodybytesbody_bytesawaitrequest.body()logger.info(fRaw request body (bytes):{body_bytes})# 尝试 decode 为字符串try:body_strbody_bytes.decode(utf-8)logger.info(fRaw request body (str):{body_str})exceptExceptionase:logger.error(fFailed to decode body:{e})# 尝试手动解析 JSON用于调试try:importjson body_jsonjson.loads(body_str)logger.info(fParsed JSON:{body_json})exceptExceptionase:logger.error(fFailed to parse JSON:{e})# 然后继续走正常逻辑或返回return{message:debug only}二探索问题的过程我写一个极其简单的 fast api demo uv add fastapi uvcorn# main.py from fastapi import FastAPI from pydantic import BaseModel # 创建 FastAPI 应用 app FastAPI() # 定义一个请求体的 Pydantic 模型表示接收的数据 class DataRequest(BaseModel): data: str # 假设这是你的 Python 逻辑代码 def process_data(data): return {message: fProcessed data: {data}} # 定义一个 POST 请求的接口 app.post(/process) async def process(request: DataRequest): # 调用你的 Python 业务逻辑 result process_data(request.data) return result if __name__ __main__: import uvicorn uvicorn.run(main:app, host192.168.0.103, port8000, reloadTrue)和一个简单的 Java 客户端请求publicclassApp{publicstaticvoidmain(String[]args)throwsIOException,InterruptedException{// 创建 HttpClientHttpClientclientHttpClient.newHttpClient();// 构造请求体StringjsonRequest{\data\: \Hello, FastAPI!\};// 创建 POST 请求HttpRequestrequestHttpRequest.newBuilder().uri(URI.create(http://192.168.0.103:8000/process)).header(Content-Type,application/json).POST(HttpRequest.BodyPublishers.ofString(jsonRequest)).build();// 发送请求并接收响应HttpResponseStringresponseclient.send(request,HttpResponse.BodyHandlers.ofString());// 输出响应System.out.println(Response status code: response.statusCode());System.out.println(Response body: response.body());// Response status code: 400//Response body: Invalid HTTP request received.}}经过测试发现Python web 可以正常获取到 Java 客户端的请求而Java 客户端也可以正常请求到 Python 的 http 响应。但关键来了当 Python 处理 Java 请求时打印了这么一个日志WARNING: Unsupported upgrade request. WARNING: No supported WebSocket library detected. Please use pip install uvicorn[standard], or install websockets or wsproto manually.于是我进行了如下测试测试 1执行uv add uvicorn[standard]运行 Python web 服务然后 Java 客户端测试结果真的复现了前面我提到的错误Python 请求体是空的日志WARNING: Unsupported upgrade request. WARNING: Invalid HTTP request received. INFO: 192.168.0.103:60011 - POST /process HTTP/1.1 422 Unprocessable ContentJava 报错Response status code: 422 Response body: {detail:[{type:missing,loc:[body],msg:Field required,input:null}]}测试 2uv remove uvicorn[standard], uv add uvicorn 重启 PythonJava客户端请求正常测试 3uv add websockets重启 PythonJava客户端请求正常。三问题的解释关键日志应该是这个WARNING: Unsupported upgrade request. WARNING: No supported WebSocket library detected. Please use pip install uvicorn[standard], or install websockets or wsproto manually.Java 客户端发送请求时会尝试使用 HTTP/2 协议于是请求协议升级而 uvicorn[standard] 的 http_tools 解析工具 将其视为 “不支持的协议升级请求” 于是丢弃了请求体。而 uvicorn 使用 h11 解析器兼容性更好依然保留了请求体。下面通过抓包工具看看 Java 请求头然后看看 postman 为什么能访问它就没有 upgrade 请求头但是奇怪的是postman 加了 upgrade 请求头依然能正常访问可能 Java 底层 和 postman 底层还是有什么区别吧。四解决方案方式1Python 服务使用 uvicorn websockets可选而不是用 uvicorn[standard]方式2如果你想使用 uvicorn[standard] 高性能 http 可以在 uvicorn 中强制使用 http1.1 这样 uvicorn[standard] 会直接忽略升级操作uvicorn.run(app.main:app,hostsettings.service_ip,portsettings.service_port,reloadsettings.debug,log_levelsettings.log_level.lower(),httph11)方式3Java 禁用升级请求头。但具体怎么操作我还没有弄清楚不同的客户端使用方式也不一样。至少我在 Java 原生的 HttpClient 上强制 http1.1 还是没效果还是发送了升级请求。推荐使用方式 2 这个最简单。