25

I was wondering how to correctly implement a Spring Controller which is supposed to serve as a REST Service. Especially I want to try and make the interface as RESTful as possible. Also i'd like to make use of HTTP Error codes so my Clients can act accordingly.

I was wondering how to implement my Methods, so they return JSON if everything works fine(in the body of the response) or toss a http error code as well as a custom reason why it didnt work(maybe errors that came from the DAO or the database). However I'm not sure which one is the right way? return a String and add the values to return to a Model, or return a HashMap and put my stuff in there? or return the objects directly? but then what if an error occures and i cannot return said Class? return null instead? I post 2-3 ways of doing it that i could imagine:

@RequestMapping(value="/addUser", method= RequestMethod.POST)
public String addUser(@RequestBody User user, HttpServletResponse response, Model model) throws Exception{

    try{
        userService.addUser(user);
        model.addAttribute("user", userService.getUser(user.getUsername(), user.getPassword()));
        return "user";
    }catch(Exception e){
        model.addAttribute("error", e.toString());
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
        return "error";
    }
}

Or rather this way:

@RequestMapping(value="/addUser", method= RequestMethod.POST)
public @ResponseBody Map addUser(@RequestBody User user, HttpServletResponse response){
    Map map = new HashMap();
    try{
        userService.addUser(user);
        map.put("success", true);
        map.put("username", user.getUsername());
    }catch (KeyAlreadyExistsException e){
        map.put("success", false);
        map.put("Error", e.toString());
        response.sendError(HttpServletResponse.SC_FORBIDDEN, e.toString());
    }catch(Exception e){
        map.put("success", false);
        map.put("Error", e.toString());
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
    }
    finally {
        return map;
    }
}

I realize code is not "just right" but i cannot figure out how to make it the way it needs to be. Maybe some experiences responses would help? Thx for the support already

5 Answers 5

31

You could also catch your exceptions with @ExceptionHandler annotated methos within your Rest Controller.

@ExceptionHandler(Exception.class)
@ResponseBody
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public String handleException(Exception e) {
    return "return error object instead";
}

this will make your acutal controller/business logic cleaner.

Sign up to request clarification or add additional context in comments.

5 Comments

I like this idea, i actually just did that! i created an error ressource as advised by Bart and then handle all errors in the ctrl in this method. I could probably further generalize and handle the errors application wide, but one step at a time.
you can create multiple ExceptionHandlers in your Controller, for each Exception Type.
i found it to seem cleaner if i just have one exceptionhandler method per controller instead of a whole array of them and then take care of checking which error was thrown within this handler
well, i prefer the per-exception-exception-resolver approach :-) you can easily handle exception hierarchies
Be careful with this approach as this will hide Spring's internal exception handling like MissingPathVariableException or ConversionNotSupportedException and return a status code of 500 instead of 400 for example. Ensure you extend the class ResponseEntityExceptionHandler if you add a handler for Exception.class.
16

Firstly, I think you should always return a object when returning JSON. Even when something goes horribly wrong.

When something goes wrong you simply set response.setStatus() and return a resource describing the error.

public class ErrorResource implements Resource {
    private final int status;
    private final String message;

    public ErrorResource(int s, String m) {
        status = s;
        message = m;
    }

    public int getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }
}

The resource gets serialized and the outcome would be

{"status":500, "message":"Yay!"}

Using a Map will work but I would like to advise you to write some resource classes which define the object to be returned. They would be easier to maintain. Maps don't offer any structure while structure is a very important part when creating a REST service.

I don't think you should return a resource with the raw exception message embedded. It can potentially leak information you don't want anyone to see.

5 Comments

So you would say I have a main class "returnRessource" from which all other classes which i intend to return, no matter if errors or actual values should inherit from? Otherwise I dont really get how you intend to get by if you have a method lets say public User getUser(){...}and it blows, you cant just return another class you gotta return the User class. Or do you think it should be public Object getUser(){...}??
I should maybe also add that I intend to write my endpoint using Angular. Here its very convenient to act according to http response codes. however I would still agree that a json object is better than a default tomcat error page. How do I return an object then instead of the response.sendError method? I mean while still having the http error code set
You can make an interface Resource and use that as the return type. If a class called User can be returned as a resource it should implement the interface. So it becomes public Resource getUser(){...}. And since ErrorResource also implements that same interface. You can return it without any hassle.
this would seem somewhat good but also makes me having to mix my entity code with my controller problems. I want to return an object User which is also persisted to the databse, having to have that object implement an interface which is needed for making my controller code "green" is not so clean in my eyes. i understand that is works but well, it just doesnt feel right.
but i like the idea of an errorressource class which can be put into the response body. together with reagten's answer it it complete
8

you can use @ExceptionHandler with @ControllerAdvice check this link

Comments

0

use ResponseEntity class to exploit error with http status code.

You could try the following code:

@RequestMapping(value = "/profile", method = RequestMethod.GET) 
@ResponseBody @ResponseStatus(value = HttpStatus.OK) 

public ResponseEntity<UserVO> getUserProfile() 
{ 
   string userName = getUserAuthentication().getName(); 
   if (StringUtils.isEmpty(userName)) RestUtil.defaultJsonResponse(""); 
   User user = userService.getUserByUserNameWithCounters(userName); 
   return RestUtil.getJsonResponse(new UserVO(user)); 
}

2 Comments

Add some more explanation.
sorry i don't get to formatting well, but try this code -> @RequestMapping(value = "/profile", method = RequestMethod.GET) @ResponseBody @ResponseStatus(value = HttpStatus.OK) public ResponseEntity<UserVO> getUserProfile() { String userName = getUserAuthentication().getName(); if (StringUtils.isEmpty(userName)) RestUtil.defaultJsonResponse(""); User user = userService.getUserByUserNameWithCounters(userName); return RestUtil.getJsonResponse(new UserVO(user)); }
0

If you want your whole Exception with stackTrace to be transmitted toward your client, as @Bart said you should send an "ErrorResource" object.

A library has it off-the-shelf :

<dependency>
  <groupId>com.github.zg2pro</groupId>
  <artifactId>spring-rest-basis</artifactId>
  <version>0.2</version>
</dependency>

add it to your project and then just add a "@ControllerAdvice" class to your beans, as it is explained in the project wiki.

That should handle your errors nicely!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.