10) Spring Web Lesson

Using ResponseEntity for Spring HTTP Responses

13 min to complete · By Ryan Desmond, Jared Larsen

You now have the required knowledge to set up static RESTful endpoints. That is to say, you know how to set up endpoints but don't yet know how to retrieve data from the user's request.

But, before you start creating endpoints that process variables and complex data structures, it is essential to learn about returning data to the client. It's also important to follow best practices for structuring your increasingly complicated web services. This lesson will cover different ways that you can return data in your HTTP responses, the next lesson will introduce best practices.

Example Location:
com.codingnomads.springweb.returningdatatoclient.usingresponseentity

Ways to Return Data

If you think back to the previous section, you may recall the different ways the HTTP protocol allows you to return data. Here's a quick reminder:

  • Response Body
  • Status Code
  • Response Headers

Even though this seems like a short list, these three methods afford you a lot of different ways to structure your data for transit.

What is ResponseEntity?

You know there are three areas where you can return data, but how do you access each? When a class is marked with @RestController, data returned from a method is automatically written to the HTTP response body. So the response body is fine, but how do you access the other two elements mentioned in the list above? The answer to this question is the ResponseEntity class.

In contrast to RequestEntity that you have encountered before (on the client side) which represents an HTTP request, ResponseEntity is a class that represents a complete HTTP response. It wraps the three important pieces of your response into a neat package ready for return to the user (from the server side).

Colorful illustration of a light bulb

Info: You may be asking yourself, but wait, is ResponseEntity required? The simple answer is no. If your method does not return a ResponseEntity, Spring will send a response with three default headers, the status code 200 OK, and - as you've heard before - whatever the method returns as the body.

Instantiating Spring ResponseEntity

Like most Spring classes, you can instantiate an object in two ways. You can either use the builder pattern or take the traditional route and use the one of the provided constructors. This lesson will show you both, and you can choose which to use based on personal preference.

Earlier in this course, you learned about HTTP status codes and how they are an important piece of any HTTP response. Setting a response status code using ResponseEntity is just a matter of using either:

  • The HttpStatus enum.
  • A 3 digit int.
  • A builder shortcut method.

Here are a few examples:

// return a 200 OK status code using constructor
return new ResponseEntity<>(HttpStatus.OK);

// return a 400 BAD REQUEST status code using constructor 
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);

// return a 500 INTERNAL SERVER ERROR using constructor
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);

// return 200 OK using builder
return ResponseEntity.ok().body(null);

// return 400 BAD REQUEST using builder
return ResponseEntity.badRequest().body(null);

// return 500 INTERNAL SERVER ERROR using builder
return ResponseEntity
        .status(HttpStatus.INTERNAL_SERVER_ERROR)
        .body(null);

You can see here that using a constructor when you only want a status code returned to the user is more concise than using the builder pattern. But again, it's up to you which one you want to use. Both work.

Please note that the HttpStatus enumeration helps you by matching words to the three-digit status codes. In most cases, this is very helpful since it means you don't need a list of status codes readily available. Still, it is also inhibiting if you want to utilize your own custom status codes in an application. Luckily, there's a way you can get around this. Using the builder pattern, you can enter an integer directly instead of using HttpStatus. Here's how:

return ResponseEntity.status(567).body(user);

The example above uses the builder pattern to specify status code 567 and instructs Spring to write the contents of the user object to the body of the response.

Illustration of a lighthouse

Info: This status code (567) is only indicated to be an internal server error, but the IETF standard says nothing specific about it. You can define it to be almost anything you want. Keep in mind that the definition will only be project-wide. Nobody will know what your specific status code means. If used externally, you should stick to the standard codes!

Unfortunately, you can't pass a custom status code through the ResponseBody constructors, but you can pass in a body and status (via HttpStatus enum value) as shown:

public ResponseEntity(T body, HttpStatus status)

You can include headers with this:

public ResponseEntity(T body, MultiValueMap<String, String> headers, HttpStatus status)

If you want to pass headers and status but not a body, there's a constructor for that too:

public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus status)

Using ResponseEntity

It wouldn't be helpful if all you got from this lesson were method signatures, so on to some example usages! This first example shows two ways of returning a test header, a user object assumed to be non-null and populated, and code 200 OK.

// using constructor
HttpHeaders headers = new HttpHeaders();
headers.add("TEST", "TEST VALUE");
return new ResponseEntity<>(user, headers, HttpStatus.OK);

// using builder
return ResponseEntity.ok().header("TEST","TEST VALUE").body(user);

Here's an example which returns two headers, an empty body, and a 400 BAD REQUEST status:

// using constructor
HttpHeaders headers = new HttpHeaders();
headers.add("TEST 1", "TEST 1 VALUE");
headers.add("TEST 2", "TEST 2 VALUE");

return new ResponseEntity<>(headers, HttpStatus.BAD_REQUEST);

// using builder option 1
return ResponseEntity.badRequest()
        .header("TEST1","TEST1 VALUE")
        .header("TEST2", "TEST2 VALUE")
        .build();

// using builder option 2
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
        .header("TEST1","TEST1 VALUE")
        .header("TEST2", "TEST2 VALUE")
        .body(null);

Now for an easier one. No headers, only a status and pre-made task object are returned for this hypothetical POSTing of a new Task object. (Remember TaskController in the "Shortcut Annotations" example package)

// using constructor
return new ResponseEntity<>(task, HttpStatus.OK);

// using builder 
return ResponseEntity.ok().body(task);

It is also possible to be more specific and return code 201 CREATED. However, when you return code 201 CREATED the IETF standard states that a Location header should be included in the response to let the user know how to access the newly created entity.

// constructor with no Location header - don't do this
return new ResponseEntity<>(task, HttpStatus.CREATED);

// constructor with Location header - the right way
HttpHeaders headers = new HttpHeaders();
headers.add("Location", "/tasks_api/tasks/" + task.getId());
return new ResponseEntity<>(headers, HttpStatus.CREATED);

// builder with no Location header - don't do this
return ResponseEntity.status(201).body(task);

// builder with manual Location header - works
return ResponseEntity
        .status(201)
        .header("Location", "/tasks_api/tasks/" + task.getId())
        .body(task);

// builder with created Location header - even better
return ResponseEntity
        .created(new URI("/tasks_api/tasks/" + task.getId()))
        .body(task);

The line-by-line nature of using the constructor makes it easier to understand. You can see that the Location header was manually entered, just like any other header. Using the builder pattern is a bit more complicated. The example shows both a manual entry of a 201 status and using created(). There are three perks to using created():

  1. First, created(URI uri) forces you to enter a URI parameter which automatically checks the formatting of the URL.
  2. Second, the created() method automatically adds the Location header.
  3. Third, since the Location header is added without any extra effort, and you are forced to enter a URI, it is difficult to forget to add the location.

Learn by Doing

Package: springweb.returningdatatoclient.usingresponseentity

  • User cURL and/or Postman to hit all endpoints defined in ResponseController, inspecting the body and the headers returned.
  • Define a new endpoint: /practice, which returns a ResponseEntity<?>.
  • Experiment with returning various ResponseEntity structures,
    mixing up status codes, headers, and body content each time.

Great practice! Please push your work to GitHub when you're done.

Summary: Spring ResponseEntity

In this lesson, you learned how to manipulate the HTTP response using Spring ResponseEntity. You can now use ResponseEntity to change HTTP:

  • Headers
  • Bodies
  • Status Codes

Spring ResponseEntity is used by utilizing its defined constructors or the available builder pattern. Both have pros and cons, and further exploration will help you determine your preference. Furthermore, all the above examples were provided entirely out of context. Seeing them in actual examples will help you to fill in the gaps.