简体   繁体   中英

Spring throw CustomObject to JSON in controller

I'm wanting to return/throw an object depending on some logic inside the controller.

For example lets say something required a user to login I'd want to return an appropriate message

throw a new ReturnLoginMessage("Must login", "xx@xx.com", 111)

I can't just throw ReturnLoginMessage as it'd just return the following:

"exception": "com.userapi.returntypes.ReturnLoginMessage"

Which is not useful at all! I'd want the JSON representation of that returnloginmessage object.

{
"message": "Must login", 
"email": "xx@xx.com", 
"code": "111"
}

Is this possible given the following controller? What this controller does is check to see if the user already exists, if it does, they must login (which is why I'm returning the returnloginmessage object.

@RequestMapping(value="login/email", method=RequestMethod.POST,  produces = {MediaType.APPLICATION_JSON_VALUE })
UserCredential login(@RequestBody UserFacebookLoginContext body) throws IOException, InvalidKeySpecException,
            NoSuchAlgorithmException, ConfirmLogin, ClassNotFoundException, ConfirmResponse, InvalidRequestException, ConfirmLoginEmail {

... Logic that checks to see if user exists....
if (user does exist) 
    throw  new ReturnLoginMessage("Must login", "xx@xx.com", 111); // THis returns "exception": "com.userapi.returntypes.ReturnLoginMessage"
else if(other condition)
    throw new OtherMessage("...", "...");
else 
    return UserCredential;

Is there away I can return a JSON representation of the ReturnLoginMessage ? Given the following Java object?

public class ReturnLoginMessage extends Exception {
    private int code;
    private String message;
    private String email;
    private Date timestamp;

    public ReturnLoginMessage(int code, String message) {
        this.code = code;
        this.email = email;
        this.timestamp = new Date();
    }

    public ReturnLoginMessage(String message, int code, String email) {
        super(message);
        this.code = code;
        this.email = email;
        this.timestamp = new Date();
    }

    ... Getters .... Setters ...
}

You could achieve this by declaring a new ExceptionHandler method for ReturnLoginMessage.class . See this answer.

Other approach would be to extend the return value of the login to something more generic. For example:

  1. Declare it return Object instead of UserCredentials and simply return ReturnLoginMessage instead of throwing it.

  2. Declare it return Map<String,Object> and construct the proper key/value pairs for both scenarios. See this answer

I acheived almost same thing(perhaps) by this way

first

add a ExceptionHandler at your REST Controller and return your exception as a JSON (put a @ResponseBody).

@Controller
@RequestMapping("/rest")
public class RestfulTodoController {

    @ExceptionHandler(ReturnLoginMessage.class)
    @ResponseBody
    public ReturnLoginMessage handleException(final ReturnLoginMessage e) {
        return e;
    }
    // and your login handler method

}

check whether response is Json and also returning type is ReturnLoginMessage at advice that extends AbstractMappingJacksonResponseBodyAdvice, if response is ReturnLoginMessage then modify response as you like

@ControllerAdvice
public class MyResponseBodyAdvice extends AbstractMappingJacksonResponseBodyAdvice {

    @Override
    public boolean supports(final MethodParameter returnType,
            final Class<? extends HttpMessageConverter<?>> converterType) {
        return super.supports(returnType, converterType)
                && null != returnType.getMethodAnnotation(ResponseBody.class);
    }

    @Override
    protected void beforeBodyWriteInternal(final MappingJacksonValue bodyContainer, final MediaType contentType,
            final MethodParameter returnType, final ServerHttpRequest request, final ServerHttpResponse response) {
            if (bodyContainer.getValue() instanceof ReturnLoginMessage) {
                //set some object as you like 
                bodyContainer.setValue();
            }
        }
    }

}

finally

register above your advice as a Bean

@Configuration
public class WebAppConfiguration {

    @Bean
    public MyResponseBodyAdvice myResponseBodyAdvice() {
        return new MyResponseBodyAdvice();
    }

}

Use Fasterxml Jackson library best JSON parser for Java
Fasterxml Jackson convert Java Collection directly into JSON.

Maven Dependency

  <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>${faster-xml-version}</version>
  </dependency>
  <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${faster-xml-version}</version>
   </dependency>
   <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>${faster-xml-version}</version>
  </dependency>

Controller Code

@RequestMapping(value="login/email", method=RequestMethod.POST,               produces = {MediaType.APPLICATION_JSON_VALUE })
@ResponseBody
 Map<String, Object> login(@RequestBody UserFacebookLoginContext body)   throws IOException, InvalidKeySpecException,
        NoSuchAlgorithmException, ConfirmLogin,    
 ClassNotFoundException, ConfirmResponse, InvalidRequestException, 
 ConfirmLoginEmail {
  ... Logic that checks to see if user exists....
if (user does exist) 
   return Utility.getSuccessResponse("message","Must    
login","email","xx@xx.com","code",111);

 else if(other condition)
 return Utility.getSuccessResponse("", "");
  else 
 return Utility.getSuccessResponse("userCredential", UserCredential);

Utility Class

 public class Utility {

public static Map<String, Object> getSuccessResponse(Object... responseContent) {

    if (responseContent.length % 2 != 0) {
        throw new IllegalArgumentException();
    }
    Map<String, Object> responseObject = new HashMap<String, Object>();
    Map<String, Object> responseList = new HashMap<String, Object>();

    for (int i = 0; i < responseContent.length; i = i + 2) {
        responseList.put(responseContent[i].toString(), responseContent[i + 1]);
    }
    responseObject.put("result", responseList);
    return responseObject;
}

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM