Spring AI 对接 vLLM 的 DeepSeek 报 400 避坑指南:请求体为何会凭空消失
【免费下载链接】spring-aiAn Application Framework for AI Engineering项目地址: https://gitcode.com/GitHub_Trending/spr/spring-ai
用 Spring AI 的 OpenAiChatModel 对接 vLLM 部署的 DeepSeek 模型,base-url、api-key、model 三项配置全对,curl 一发就通,框架调用却稳定报 HTTP 400、提示 "Field required"。这篇避坑指南从这条报错倒查起,讲清分块传输编码如何把请求体弄丢,以及怎么更换 HTTP 客户端绕开这个 400。
配置全对却 400:curl 能通,框架调不通
先看现象。用 Spring AI 的 spring-ai-openai 模块 中的 OpenAiChatModel 打向 vLLM 的 chat/completions 接口,消息内容怎么改都没用,返回始终是同一条 400:
400 - {"object":"error","message":"[{'type': 'missing', 'loc': ('body',), 'msg': 'Field required', 'input': None}]","type":"BadRequestError","param":null,"code":400}⚠️ 关键是"input": None:这不是"某个字段没填",而是服务端压根没收到请求体——它看到的 body 是空的。换成 curl 发同样的 JSON,同一个接口立刻返回 200。所以问题不在参数,而在"请求是怎么送出去的"。
顺着报错倒查:分块传输编码是隐形杀手
HTTP 请求体有两种常见寄法。一种是先写 Content-Length(总字节数),服务端照单全收;另一种是分块传输编码(chunked transfer encoding,一种不声明总长度、边发边补的请求体格式)——就像把文档拆成几页分别寄出,不提前说总页数,每寄一页附一个标记,最后一块标记"没有了"。
Spring 默认的 JDK HttpClient 在请求体以流式方式写出、长度未知时,就会走分块这条路。而 vLLM 的解析链对这种没有 Content-Length 的请求体处理不完整,Pydantic 校验时拿到的 body 是 None,于是报出上面那条 400。curl 为什么没事?它发的是定长 JSON,总带 Content-Length,服务端一次读完,根本不触发分块路径。
这就是"直发 HTTP 没问题、走框架却失败"的完整解释:差别不在 URL、不在 Header,而在请求体的传输方式。
如何更换 HTTP 客户端绕开 400 报错
🔧 思路很简单:换一个会先把请求体缓冲下来、算出总长度再发送的客户端。Jetty 是现成答案,两步走:
- 引入 Jetty 客户端依赖(reactive 版供 WebClient 使用):
<dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-reactive-httpclient</artifactId> </dependency>- 构造 OpenAiApi 时,把同步调用用的 RestClient 和流式调用用的 WebClient 底层传输都换成 Jetty:
OpenAiApi openAiApi = OpenAiApi.builder() .baseUrl(chatConfig.getBaseUrl()) .apiKey(chatConfig.getApiKey()) .restClientBuilder(RestClient.builder() .requestFactory(new JettyClientHttpRequestFactory())) .webClientBuilder(WebClient.builder() .clientConnector(new JettyClientHttpConnector())) .build();改完后请求体带 Content-Length 发出,vLLM 正常解析,400 消失。完整调用链可参考 OpenAiChatModel 源码。
- 对接 OpenAI 兼容服务前先 curl 验证一遍,把"配置问题"和"协议问题"分开;
- 再遇到 body "Field required" 这类 400,第一反应怀疑分块传输,而不是反复改参数;
- 持续关注 vLLM 是否补齐对 chunked 请求体的支持,修好后默认客户端即可换回。
【免费下载链接】spring-aiAn Application Framework for AI Engineering项目地址: https://gitcode.com/GitHub_Trending/spr/spring-ai
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考