How to make a JSON POST request from Java client to Python FastAPI server?
Question:
I send a post request from a java springboot application like this:
String requestBody = gson.toJson(sbert);
System.out.println(requestBody); // If I print this, and use this in postman it works!
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();
This is essentially what my fastapi service looks like:
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)
When I print out the returned json object I get:
{"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]}
Which doesn’t make sense considering the same json object I use for the post request works perfectly fine when I use it in postman.
My fastapi server says: INFO: 127.0.0.1:65066 - "POST /sbert_similarity HTTP/1.1" 422 Unprocessable Entity
Anyone know what’s going on?
Thanks!
Edit:
So just a quick update it seems like from this code:
@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 content_type == 'application/json':
try:
json = await request.json()
return json
except JSONDecodeError:
return 'Invalid JSON data.'
else:
return 'Content-Type not supported.'
there was a JSONDecodeError for all post requests sent from the Java Springboot application, I still don’t know why that is though?
Second Edit:
So, now I have to ask why is the http client sending a null object as opposed to it’s actual json object?
Answers:
The example below deomonstrates how to make a JSON POST request using HttpURLConnection
. The issue in your code might or might not be with Apache’s HttpClient
(you would need to test this on your own), but might be originated from the requestBody
you send to the server; hence, I would suggest you manually specify a JSON string first (before using other Java serialization libraries, such as gson
, to convert Java Objects into JSON), as shown below, and see how that works.
Working Example
Python FastAPI server (taken from this answer)
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
user: str
@app.post('/')
def main(user: User):
return user
Java Client example
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class PostJSON {
public static void main(String[] args) throws IOException {
URL url = new URL("http://127.0.0.1:8000/");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept", "application/json");
con.setDoOutput(true);
String jsonInputString = "{"user": "foo"}";
try (OutputStream os = con.getOutputStream()) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(con.getResponseCode() + " " + response);
}
}
}
I send a post request from a java springboot application like this:
String requestBody = gson.toJson(sbert);
System.out.println(requestBody); // If I print this, and use this in postman it works!
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();
This is essentially what my fastapi service looks like:
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)
When I print out the returned json object I get:
{"detail":[{"loc":["body"],"msg":"field required","type":"value_error.missing"}]}
Which doesn’t make sense considering the same json object I use for the post request works perfectly fine when I use it in postman.
My fastapi server says: INFO: 127.0.0.1:65066 - "POST /sbert_similarity HTTP/1.1" 422 Unprocessable Entity
Anyone know what’s going on?
Thanks!
Edit:
So just a quick update it seems like from this code:
@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 content_type == 'application/json':
try:
json = await request.json()
return json
except JSONDecodeError:
return 'Invalid JSON data.'
else:
return 'Content-Type not supported.'
there was a JSONDecodeError for all post requests sent from the Java Springboot application, I still don’t know why that is though?
Second Edit:
So, now I have to ask why is the http client sending a null object as opposed to it’s actual json object?
The example below deomonstrates how to make a JSON POST request using HttpURLConnection
. The issue in your code might or might not be with Apache’s HttpClient
(you would need to test this on your own), but might be originated from the requestBody
you send to the server; hence, I would suggest you manually specify a JSON string first (before using other Java serialization libraries, such as gson
, to convert Java Objects into JSON), as shown below, and see how that works.
Working Example
Python FastAPI server (taken from this answer)
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class User(BaseModel):
user: str
@app.post('/')
def main(user: User):
return user
Java Client example
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class PostJSON {
public static void main(String[] args) throws IOException {
URL url = new URL("http://127.0.0.1:8000/");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept", "application/json");
con.setDoOutput(true);
String jsonInputString = "{"user": "foo"}";
try (OutputStream os = con.getOutputStream()) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(con.getResponseCode() + " " + response);
}
}
}