Search code examples
javascripthttppostxmlhttprequesthttpserver

Body of POST request from javascript application in browser using XMLHttpRequest is empty when arriving at the server


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());

    }
}

Solution

  • 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:

    • Only the below 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

    • client <=> POST request <=> Server

    For unsafe request, there is a preflight request (a special OPTIONS action) to ask the server

    • if client origin is allowed and
    • what can the client do

    before making the real request, in your case the POST request.

    1. client <=> pre-flight (OPTIONS) request <=> Server
    2. client <=> POST request <=> Server

    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>