Reputation: 25
I have two applications: one web client and one java server. The web client sends an HTTP POST request with a JSON body of data, and the server should receive the data and display it on the screen. The problem is that when the server reads the body of the request, there is nothing to read. What is wrong?
Edit: I realized that the problem is on the browser side (since I could send and read an HTTP POST request from some other website), but I still don't know what the problem is. Is this related to the browser running the code? When I use Chrome I get the described problem. When I use Firefox or IE the java server isn't even notified; it doesn't even run the handle method that is supposed to run when it gets an HTTP request.
It worked to read data at the server when I coded the content type as url encoded. I think it was: x-www-form-urlencoded. But I want to send data as JSON.
I use XMLHttpRequest for the web client, as you can see below.The web client:
<!DOCTYPE html>
<html>
<body>
<script>
function handleInput(){
var title = "title";
var reviewer = "reviewer";
const xhr = new XMLHttpRequest();
var searchInfo = {
title:title,
reviewer:reviewer
};
xhr.open('POST', 'http://localhost:8001');
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
xhr.send(JSON.stringify(searchInfo));
}
</script>
</body>
</html
The server:
import java.io.IOException;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class Server {
public static void main(String[] args) {
try {
HttpServer server = HttpServer.create(new InetSocketAddress("localhost", 8001), 0);
HttpHandler handler = new MyHttpHandler();
HttpContext context = server.createContext("/");
context.setHandler(handler);
server.start();
System.out.println("Server started on port 8001");
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
The servers HTTP handler:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
public class MyHttpHandler implements HttpHandler {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
InputStream is = httpExchange.getRequestBody();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuilder content = new StringBuilder();
String line;
while ((line = br.readLine()) != null) { br.readLine is always null!!!
content.append(line);
content.append("\n");
}
System.out.println("Content: " + content.toString());
}
}
Upvotes: 1
Views: 610
Reputation: 5692
As your server (java side, live in localhost:8001) and client (xhttp side, live in somewhere else) are separated. The request is considered as cross origin requests (CORs).
There are 2 types of cross origin requests - Safe and unsafe:
Content-Type
are considered as SAFE in cross site request:
application/x-www-form-urlencoded
multipart/form-data
text/plain
So, application/json;
is considered UNSAFE.
For safe request, there is not much restriction on server so you can get the response back while using application/x-www-form-urlencoded
For unsafe request, there is a preflight request (a special OPTIONS action) to ask the server
before making the real request, in your case the POST request.
So the problem is there is no handling of preflight request and the request cannot be processed by server. To deal with it, you need to change your handler as follow:
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
public class MyHttpHandler implements HttpHandler {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
Headers headers = httpExchange.getResponseHeaders();
System.out.println(httpExchange.getRequestMethod());
headers.add("Access-Control-Allow-Origin", "*"); // * means allow all origin, in production, it should be the origin you trust e.g. http://client.abc.com
if (httpExchange.getRequestMethod().equalsIgnoreCase("OPTIONS")) {
headers.add("Access-Control-Allow-Headers", "Content-Type"); // allow clients to pass in content-type
headers.add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
httpExchange.sendResponseHeaders(204, -1);
return;
}
InputStream is = httpExchange.getRequestBody();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
StringBuilder content = new StringBuilder();
String line;
while ((line = br.readLine()) != null) { // br.readLine is always null!!!
content.append(line);
content.append("\n");
}
System.out.println("Content: " + content.toString());
// added response as well to complete the request
String response = "Good" ;
httpExchange.sendResponseHeaders(200, response.length());
OutputStream os = httpExchange.getResponseBody();
os.write(response.getBytes(StandardCharsets.UTF_8));
os.close();
httpExchange.close();
}
}
HTML
<!DOCTYPE html>
<html>
<body>
<button onclick="handleInput()">Click</button>
<script>
function handleInput() {
var title = "title";
var reviewer = "reviewer";
const xhr = new XMLHttpRequest();
xhr.onload = function() {
console.log('done')
}
var searchInfo = {
title: title,
reviewer: reviewer,
};
xhr.open("POST", "http://localhost:8001");
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xhr.send(JSON.stringify(searchInfo));
}
</script>
</body>
</html>
To further understanding CORs:
https://javascript.info/fetch-crossorigin
For your html
<form name="searchForm" id="searchForm" onSubmit="handleInput(event)">
...
</form>
<script>
function handleInput(e){
e.preventDefault()
...
}
</script>
Upvotes: 2