Java请求进入Python FastAPI 后,请求体为空,参数不合法

问题

最近在做 Python FastAPI 和 Java spring boot 对接接口的事情。

FastAPI 接口写好后,postman 和 swage 测试都正常,但是 Java 服务使用 HttpExchange 怎么着都请求 400,参数不合法。

检查了请求参数,看着也没问题,也都有值。

Python 服务经过 debug 日志发现,请求体是空:

python 复制代码
@app.post("/test")
async def audit_product_raw_debug(request: Request):
    # 打印原始 body(bytes)
    body_bytes = await request.body()
    logger.info(f"Raw request body (bytes): {body_bytes}")

    # 尝试 decode 为字符串
    try:
        body_str = body_bytes.decode('utf-8')
        logger.info(f"Raw request body (str): {body_str}")
    except Exception as e:
        logger.error(f"Failed to decode body: {e}")

    # 尝试手动解析 JSON(用于调试)
    try:
        import json
        body_json = json.loads(body_str)
        logger.info(f"Parsed JSON: {body_json}")
    except Exception as e:
        logger.error(f"Failed 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": f"Processed 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", host="192.168.0.103", port=8000, reload=True)

和一个简单的 Java 客户端请求:

java 复制代码
public class App 
{
    public static void main( String[] args ) throws IOException, InterruptedException {
        // 创建 HttpClient
        HttpClient client = HttpClient.newHttpClient();

        // 构造请求体
        String jsonRequest = "{\"data\": \"Hello, FastAPI!\"}";

        // 创建 POST 请求
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("http://192.168.0.103:8000/process"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(jsonRequest))
                .build();

        // 发送请求并接收响应
        HttpResponse<String> response = client.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 Content

Java 报错:

复制代码
Response status code: 422
Response body: {"detail":[{"type":"missing","loc":["body"],"msg":"Field required","input":null}]}

测试 2uv remove 'uvicorn[standard]' , uv add uvicorn , 重启 Python,Java客户端请求正常;

测试 3uv add websockets ,重启 Python,Java客户端请求正常。

三,问题的解释

关键日志应该是这个:

复制代码
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 协议,于是请求协议升级;而 uvicornstandard 的 http_tools 解析工具 将其视为 "不支持的协议升级请求" ,于是丢弃了请求体。

而 uvicorn 使用 h11 解析器,兼容性更好,依然保留了请求体。

下面通过抓包工具,看看 Java 请求头:

然后看看 postman 为什么能访问,它就没有 upgrade 请求头:

但是奇怪的是,postman 加了 upgrade 请求头依然能正常访问,可能 Java 底层 和 postman 底层还是有什么区别吧。

四,解决方案

方式1,Python 服务,使用 uvicorn + websockets(可选),而不是用 uvicornstandard

方式2,如果你想使用 uvicornstandard 高性能 http ,可以在 uvicorn 中强制使用 http1.1 ,这样 uvicornstandard 会直接忽略升级操作

py 复制代码
uvicorn.run(
        "app.main:app",
        host=settings.service_ip,
        port=settings.service_port,
        reload=settings.debug,
        log_level=settings.log_level.lower(),
        http="h11"
    )

方式3:Java 禁用升级请求头。但具体怎么操作,我还没有弄清楚,不同的客户端使用方式也不一样。至少,我在 Java 原生的 HttpClient 上强制 http1.1 ,还是没效果,还是发送了升级请求。

推荐使用方式 2 ,这个最简单。

相关推荐
Java后端的Ai之路5 小时前
20、Python - 备忘录模式
开发语言·人工智能·python·外观模式·备忘录模式
嘿嘿-666 小时前
Windows 一键使用 GPT-6 Astra:Codex CLI 配置教程
java·人工智能·windows·gpt·chatgpt·web
2601_962077716 小时前
基于Python与NLP的新闻事件信息抽取实战:从NER到时空标准化
python·nlp·transformers·spacy·新闻事件抽取
JavaPub-rodert6 小时前
LangChain 从入门到 Agent 实战:用 Python 搭建一个真正能调用工具和知识库的 AI 助手
人工智能·python·langchain
郝学胜-神的一滴6 小时前
Qt 高级编程 045:坐标体系深度实战
开发语言·c++·windows·python·qt·程序人生
策案案案7 小时前
YOLO: 将AI Agents嵌入到IntelliJ IDEA
java·大数据·人工智能·笔记·yolo·microsoft·intellij-idea
m0_380743877 小时前
给 OpenAI API 调用加上模型切换:GPT-5.1 和 Codex 的配置实践
人工智能·python·gpt
2601_962297487 小时前
在python3中、下列输出变量a的正确写法是_2020超星大数据Python免费答案
数据结构·python·算法·编程·字符串操作
“AI国潮设计-小江”8 小时前
Python实战 | SDXL精准控制“普宁英歌舞×星空蛋糕”IP落地,附核心Prompt与商用授权思路
开发语言·人工智能·python·prompt·aigc
公爵爱学习8 小时前
无人机视觉识别前序-Linux-YOLO安装
python·yolo·计算机视觉·conda·无人机