1. Overview

HTTP requests for verbs such as POST, PUT, and PATCH optionally sends a request body payload as part of the request. These data can be in the format of key-value pairs or some other serialization format such as JSON and XML. However, it is sometimes unclear how we can send a request body and have the server parse it as a JSON object.

In this tutorial, we’ll be learning how to send JSON objects as the request body correctly with the help of the Content-Type HTTP header.

2. Linux Command Line HTTP Clients

In Linux, curl and wget are the common terminal-based HTTP clients. Given its popularity in the Linux ecosystem, we’ll be using them in this article for demonstration purposes.

To obtain curl and wget, we can install them using the package manager. For example, in Ubuntu Linux, we can run apt-get install to obtain these programs:

$ apt-get install curl
$ apt-get install wget

3. HTTPBin

HTTPBin is an online HTTP echo service that’s free for use. It essentially accepts HTTP requests and echoes the request in the response. This service is useful for debugging and inspecting our HTTP requests.

For example, we can send a request to the /post endpoint with the POST HTTP verb and see what the server sees in our request:

$ curl -X POST https://httpbin.org/post
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.68.0",
    "X-Amzn-Trace-Id": "Root=1-624a70cb-2818fca21d9d71f86fe110d3"
  },
  "json": null,
  "origin": "",
  "url": "https://httpbin.org/post"
}

Now, if we add a custom header X-Baeldung-Title, we’ll see it reflects in the headers field of the response:

$ curl -X POST -H "X-Baeldung-Type: linux-article" https://httpbin.org/post
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.68.0",
    "X-Amzn-Trace-Id": "Root=1-624a7118-3902adc8559ae0385170b8fa",
    "X-Baeldung-Type": "linux-article"
  },
  "json": null,
  "origin": "",
  "url": "https://httpbin.org/post"
}

In this article, we’ll be sending our requests to HTTPBin so that we can inspect the actual request as seen by the server.

4. HTTP Request Body and Its Type

The request body can come in a variety of formats for different purposes. For example, a simple web form passes the request body as a key-value pair to the server. On the other hand, a more complex data structure might warrant the use of JSON or XML representation in the request body.

To correctly parse and interpret the request body, the server relies on the Content-Type header in the request. Essentially, the Content-Type header specifies a MIME type telling the server what kind of data is in the request body. For example, using the application/json MIME type tells the server that the request body is a valid JSON object. For a list of commonly used MIME types, we can refer to the MDN documentation.

5. Sending JSON Data

The key to getting the server to correctly interpret the request body as JSON is to set the Content-Type request header appropriately.

5.1. Common Pitfall

One of the most common mistakes we make when sending JSON as the payload is not specifying the MIME type of the request body. Most HTTP clients such as curl and wget default to the application/x-www-form-urlencoded MIME type for any request body. If the endpoint is expecting a properly formed JSON object, it will result in an often cryptic error message.

Let’s see the default behavior in action:

$ curl -X POST -d '{"test": "data"}' https://httpbin.org/post
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "{\"test\": \"data\"}": ""
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "16",
    "Content-Type": "application/x-www-form-urlencoded"
  }
}

From the response, we can see curl has passed the header Content-Type: application/x-www-form-urlencoded. This results in the server processing our passed-in request body as key-value pairs. We can observe the same behavior with wget:

$ wget -q -O- --post-data '{"test": "data"}' https://httpbin.org/post
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "{\"test\": \"data\"}": ""
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "16",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org"
  }
}

5.1. Sending JSON With curl

To send JSON data with curl, we specify the string representation of the JSON payload with the -d. Additionally, it’s also important that we pass in the header Content-Type: application/json using the -H option:

$ curl -X POST "https://httpbin.org/post" -d '{"site": "baeldung", "articleId": 100, "author": {"name": "alice", "id": 1}}' -H "content-type: application/json"
{
  "args": {},
  "data": "{\"site\": \"baeldung\", \"articleId\": 100, \"author\": {\"name\": \"alice\", \"id\": 1}}",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Content-Length": "76",
    "Content-Type": "application/json",
    "Host": "httpbin.org"
  },
  "json": {
    "articleId": 100,
    "author": {
      "id": 1,
      "name": "alice"
    },
    "site": "baeldung"
  }
}

From the response, we can see that the server correctly interprets our data as a JSON and parse it accordingly.

5.2. Sending JSON With wget

With wget, we specify the string representation of the JSON payload with the –post-data argument. Then, we specify the application/json MIME type using the –header argument:

$ wget -q -O- --post-data '{"site": "baeldung", "articleId": 100, "author": {"name": "alice", "id": 1}}' -H "https://httpbin.org/post" --header "content-type: application/json"

Besides that, we can use –post-file to read the file’s content and send it as a request body with wget. For example, we can send the content of article_payload.json to the server with –post-file argument:

$ wget -q -O- --post-file ./article_payload.json -H "https://httpbin.org/post" --header "content-type: application/json"
{
  "args": {},
  "data": "{\n    \"site\": \"baeldung\", \n    \"articleId\": 100, \n    \"author\": {\n\t\"name\": \"alice\", \n\t\"id\": 1\n    }\n}\n",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "identity",
    "Content-Length": "102",
    "Content-Type": "application/json",
    "Host": "httpbin.org"
  },
  "json": {
    "articleId": 100,
    "author": {
      "id": 1,
      "name": "alice"
    },
    "site": "baeldung"
  }
}

6. Summary

In this tutorial, we’ve looked briefly at the HTTP request body and how the interpretation of data type is affected by the Content-Type header. Then, we see how the absence of Content-Type causes the server to misinterpret the request body. Finally, we’ve looked at the ways to specify content-type header using curl and wget.

Comments are closed on this article!