You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java SpringBoot向FastAPI发送JSON POST请求遇422错误及解码异常求助

问题:SpringBoot发送POST请求到FastAPI出现JSON解析错误

问题重现

SpringBoot请求代码

String requestBody = gson.toJson(sbert);
System.out.println(requestBody); // 打印内容在Postman中可正常请求
HttpRequest add_request = HttpRequest.newBuilder()
        .uri(URI.create("http://localhost:4557/sbert_similarity"))
        .POST(HttpRequest.BodyPublishers.ofString(requestBody))
        .header("Content-Type", "application/json")
        .build();
HttpResponse<String> response = client.sendAsync(add_request,
        HttpResponse.BodyHandlers.ofString()).get();

FastAPI服务代码

from fastapi import FastAPI, Request
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer, util
model = SentenceTransformer('all-MiniLM-L6-v2')
import pprint as pp
import uvicorn
from typing import Any

app = FastAPI()
class Item(BaseModel):
    user_input: str
    document: str
    
class ResponseItem(BaseModel):
    similarity: float

@app.post("/sbert_similarity")
def check_similarity(item: Item) -> ResponseItem:
    pp.pprint(item)
    sentences1 = [item.user_input]
    sentences2 = [item.document]
    cosine_score = 0
    embeddings1 = model.encode(sentences1, convert_to_tensor=True)
    embeddings2 = model.encode(sentences2, convert_to_tensor=True)
    cosine_score = util.cos_sim(embeddings1, embeddings2)
    
    return {
        "similarity" : cosine_score
    }

if __name__=="__main__":
    uvicorn.run("similarity_server:app",host='0.0.0.0', port=4557, reload=True, workers=3)

错误现象

  • SpringBoot请求返回:
{"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]}
  • FastAPI日志显示:INFO: 127.0.0.1:65066 - "POST /sbert_similarity HTTP/1.1" 422 Unprocessable Entity
  • 添加测试接口/sbert_similarity_v3后,请求触发JSONDecodeError,疑似发送null或无效JSON。
  • 相同请求体在Postman中可正常调用接口。

原因排查与解决方案

1. 字段命名不匹配(最可能原因)

FastAPI的Item类字段为下划线命名(user_input),若Java的sbert类字段是驼峰命名(如userInput),默认Gson会序列化为"userInput",导致FastAPI无法识别必填字段,返回field required错误。

解决方法:配置Gson使用下划线命名策略:

Gson gson = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .create();
String requestBody = gson.toJson(sbert);

2. 请求头Content-Type重复或无效

Java的HttpRequest.header()方法会添加头而非覆盖,若客户端已有重复的Content-Type头,会引发FastAPI解析异常。

解决方法:改用setHeader()确保唯一的Content-Type,并明确编码:

HttpRequest add_request = HttpRequest.newBuilder()
        .uri(URI.create("http://localhost:4557/sbert_similarity"))
        .POST(HttpRequest.BodyPublishers.ofString(requestBody))
        .setHeader("Content-Type", "application/json; charset=utf-8")
        .build();

3. 验证实际发送的请求体

修改FastAPI测试接口,打印原始请求体,确认是否与本地打印内容一致:

@app.post("/sbert_similarity_v3")
async def check_similarity(request: Request) -> Any:
    content_type = request.headers.get('Content-Type')
    
    if content_type is None:
        return 'No Content-Type provided.'
    elif 'application/json' in content_type:
        try:
            raw_body = await request.body()
            print(f"Received raw body: {raw_body.decode('utf-8')}")
            json_data = await request.json()
            return json_data
        except Exception as e:
            print(f"Error: {str(e)}")
            return f'Invalid request: {str(e)}'
    else:
        return 'Content-Type not supported.'

查看FastAPI控制台输出,若原始body为空或格式错误,排查Gson序列化逻辑或sbert对象的字段值是否正确。

4. 异步请求阻塞问题

sendAsync(...).get()会阻塞线程等待结果,若请求未完整发送就被中断,可能导致body不完整。可改用同步请求测试:

HttpResponse<String> response = client.send(add_request, HttpResponse.BodyHandlers.ofString());

内容的提问来源于stack exchange,提问作者Steveit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 15:32:19