[java] What is the best way to return different types of ResponseEntity in Spring MVC or Spring-Boot

I have written simple rest application using Spring MVC 4 (or Spring-Boot). Within the controller I have return ResponseEntity. But in some cases I want to give success JSON and if there is validation error I want to give error JSON. Currently success and error responses are totally different, So I have created 2 classes for error and success. Within the controller I want to return ResponseEntity<Success> , if the internal logic is okay. Otherwise I want to return ResponseEntity<Error>. Is there any way to do it.

Success and Error are the 2 classes that i use to represent success and error response.

This question is related to java json spring-mvc spring-boot

The answer is


Note: if you upgrade from spring boot 1 to spring boot 2 there is a ResponseStatusException which has a Http error code and a description.

So, you can effectively use generics they way it is intended.

The only case which is a bit challenging for me, is the response type for a status 204 (ok with no body). I tend to mark those methods as ResponseEntity<?>, because ResponseEntity<Void> is less predictive.


You can use a map with your object or string like bellow :

@RequestMapping(value = "/path", 
        method = RequestMethod.GET, 
        produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ResponseEntity<Map<String,String>> getData(){

    Map<String,String> response = new HashMap<String, String>();

    boolean isValid = // some logic
    if (isValid){
        response.put("ok", "success saving data");
        return ResponseEntity.accepted().body(response);
    }
    else{
        response.put("error", "an error expected on processing file");
        return ResponseEntity.badRequest().body(response);
    }

}

For exceptional cases, I will recommend you to adopt RFC-7807 Problem Details for HTTP APIs standard in your application.

Zalando's Problems for Spring provides a good integration with Spring Boot, you can integrate it easily with your existing Spring Boot based application. Just like what JHipster did.

After adopting RFC-7087 in your application, just throw Exception in your controller method, and you will get a detailed and standard error response like:

   {
    "type": "https://example.com/probs/validation-error",
    "title": "Request parameter is malformed.",
    "status": 400
    "detail": "Validation error, value of xxx should be a positive number.",
    "instance": "/account/12345/msgs/abc",
   }

You can return generic wildcard <?> to return Success and Error on a same request mapping method

public ResponseEntity<?> method() {
    boolean b = // some logic
    if (b)
        return new ResponseEntity<Success>(HttpStatus.OK);
    else
        return new ResponseEntity<Error>(HttpStatus.CONFLICT); //appropriate error code
}

@Mark Norman answer is the correct approach


Spring 2 introduced ResponseStatusException using this you can return String, different HTTP status code, DTO at the same time.

@PostMapping("/save")
public ResponseEntity<UserDto> saveUser(@RequestBody UserDto userDto) {
    if(userDto.getId() != null) {
        throw new ResponseStatusException(HttpStatus.NOT_ACCEPTABLE,"A new user cannot already have an ID");
    }
    return ResponseEntity.ok(userService.saveUser(userDto));
}

Here is a way that I would do it:

public ResponseEntity < ? extends BaseResponse > message(@PathVariable String player) { //REST Endpoint.

 try {
  Integer.parseInt(player);
  return new ResponseEntity < ErrorResponse > (new ErrorResponse("111", "player is not found"), HttpStatus.BAD_REQUEST);
 } catch (Exception e) {


 }
 Message msg = new Message(player, "Hello " + player);
 return new ResponseEntity < Message > (msg, HttpStatus.OK);

}

@RequestMapping(value = "/getAll/{player}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity < List < ? extends BaseResponse >> messageAll(@PathVariable String player) { //REST Endpoint.

 try {
  Integer.parseInt(player);
  List < ErrorResponse > errs = new ArrayList < ErrorResponse > ();
  errs.add(new ErrorResponse("111", "player is not found"));
  return new ResponseEntity < List < ? extends BaseResponse >> (errs, HttpStatus.BAD_REQUEST);
 } catch (Exception e) {


 }
 Message msg = new Message(player, "Hello " + player);
 List < Message > msgList = new ArrayList < Message > ();
 msgList.add(msg);
 return new ResponseEntity < List < ? extends BaseResponse >> (msgList, HttpStatus.OK);

}

I used to use a class like this. The statusCode is set when there is an error with the error message set in message. Data is stored either in the Map or in a List as and when appropriate.

/**
* 
*/
package com.test.presentation.response;

import java.util.Collection;
import java.util.Map;

/**
 * A simple POJO to send JSON response to ajax requests. This POJO enables  us to
 * send messages and error codes with the actual objects in the application.
 * 
 * 
 */
@SuppressWarnings("rawtypes")
public class GenericResponse {

/**
 * An array that contains the actual objects
 */
private Collection rows;

/**
 * An Map that contains the actual objects
 */
private Map mapData;

/**
 * A String containing error code. Set to 1 if there is an error
 */
private int statusCode = 0;

/**
 * A String containing error message.
 */
private String message;

/**
 * An array that contains the actual objects
 * 
 * @return the rows
 */
public Collection getRows() {
    return rows;
}

/**
 * An array that contains the actual objects
 * 
 * @param rows
 *            the rows to set
 */
public void setRows(Collection rows) {
    this.rows = rows;
}

/**
 * An Map that contains the actual objects
 * 
 * @return the mapData
 */
public Map getMapData() {
    return mapData;
}

/**
 * An Map that contains the actual objects
 * 
 * @param mapData
 *            the mapData to set
 */
public void setMapData(Map mapData) {
    this.mapData = mapData;
}

/**
 * A String containing error code.
 * 
 * @return the errorCode
 */
public int getStatusCode() {
    return statusCode;
}

/**
 * A String containing error code.
 * 
 * @param errorCode
 *            the errorCode to set
 */
public void setStatusCode(int errorCode) {
    this.statusCode = errorCode;
}

/**
 * A String containing error message.
 * 
 * @return the errorMessage
 */
public String getMessage() {
    return message;
}

/**
 * A String containing error message.
 * 
 * @param errorMessage
 *            the errorMessage to set
 */
public void setMessage(String errorMessage) {
    this.message = errorMessage;
}

}

Hope this helps.


Its possible to return ResponseEntity without using generics, such as follows,

public ResponseEntity method() {
    boolean isValid = // some logic
    if (isValid){
        return new ResponseEntity(new Success(), HttpStatus.OK);
    }
    else{
        return new ResponseEntity(new Error(), HttpStatus.BAD_REQUEST);
    }
}

You can also implement like this to return Success and Error on a same request mapping method,use Object class(Parent class of every class in java) :-

public ResponseEntity< Object> method() {                                                                                                                                                                                                                                                                                                                                                                                  
    boolean b = //  logic  here   
      if (b)  
        return new ResponseEntity< Object>(HttpStatus.OK);      
    else      
        return new ResponseEntity< Object>(HttpStatus.CONFLICT); //appropriate error code   
}

i am not sure but, I think you can use @ResponseEntity and @ResponseBody and send 2 different one is Success and second is error message like :

@RequestMapping(value ="/book2", produces =MediaType.APPLICATION_JSON_VALUE )
@ResponseBody
Book bookInfo2() {
    Book book = new Book();
    book.setBookName("Ramcharitmanas");
    book.setWriter("TulasiDas");
    return book;
}

@RequestMapping(value ="/book3", produces =MediaType.APPLICATION_JSON_VALUE )
public ResponseEntity<Book> bookInfo3() {
    Book book = new Book();
    book.setBookName("Ramayan");
    book.setWriter("Valmiki");
    return ResponseEntity.accepted().body(book);
}

For more detail refer to this: http://www.concretepage.com/spring-4/spring-4-mvc-jsonp-example-with-rest-responsebody-responseentity


Using custom exception class you can return different HTTP status code and dto object.

@PostMapping("/save")
public ResponseEntity<UserDto> saveUser(@RequestBody UserDto userDto) {
    if(userDto.getId() != null) {
        throw new UserNotFoundException("A new user cannot already have an ID");
    }
    return ResponseEntity.ok(userService.saveUser(userDto));
}

Exception class

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "user not found")
public class UserNotFoundException extends RuntimeException {

    public UserNotFoundException(String message) {

        super(message);
    }
}

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to json

Use NSInteger as array index Uncaught SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) HTTP POST with Json on Body - Flutter/Dart Importing json file in TypeScript json.decoder.JSONDecodeError: Extra data: line 2 column 1 (char 190) Angular 5 Service to read local .json file How to import JSON File into a TypeScript file? Use Async/Await with Axios in React.js Uncaught SyntaxError: Unexpected token u in JSON at position 0 how to remove json object key and value.?

Examples related to spring-mvc

Two Page Login with Spring Security 3.2.x ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Spring 5.0.3 RequestRejectedException: The request was rejected because the URL was not normalized The type WebMvcConfigurerAdapter is deprecated RestClientException: Could not extract response. no suitable HttpMessageConverter found Spring boot: Unable to start embedded Tomcat servlet container UnsatisfiedDependencyException: Error creating bean with name 8080 port already taken issue when trying to redeploy project from Spring Tool Suite IDE Error creating bean with name 'entityManagerFactory' defined in class path resource : Invocation of init method failed Difference between the annotations @GetMapping and @RequestMapping(method = RequestMethod.GET)

Examples related to spring-boot

Access blocked by CORS policy: Response to preflight request doesn't pass access control check Why am I getting Unknown error in line 1 of pom.xml? Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured How to resolve Unable to load authentication plugin 'caching_sha2_password' issue ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName ERROR Source option 1.5 is no longer supported. Use 1.6 or later How to start up spring-boot application via command line? JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value