Best practice to send response in Spring Boot
Asked Answered
T

2

7

I'm coding REST APIs in Spring Boot. I want to make sure that my code is readable to front-end developers using Swagger API development tool (Swagger). For example

@GetMapping("/getOne")
    public ResponseEntity<?> getOne(@RequestParam String id) {
        try {
            return new ResponseEntity<Branch>(branchService.getOne(id), HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<FindError>(new FindError(e.getMessage()), HttpStatus.BAD_REQUEST);
        }
    }

If the request is successful, response is a Branch object, if fails, the response is a FindError object which has only one attribute (message). So both can be carried out depends on the response. But the swagger UI doesn't show how the response should be shown, because I use "?" as generic type. Is this a best practice to catch an error? (This coding documentation Swagger is not useful to front-end developers since it doesn't show the response object). Or any best practice for the above problem?

There are a lot of method which return different object like Branch.

Terminal answered 22/4, 2019 at 5:0 Comment(2)
That's a horrible design, use proper http response code to indicate error conditions.Pastelist
@Terminal Controllers should not return ambiguous types. It creates a lot of issues in the front-end and is generally considered as bad design. You should look into ResponseEntityExceptionHandler for handling error responses.Orebro
A
24

First of all you should follow the best practices of a RESTful API . Don't use verbs, instead use nouns as URL.So instead of @GetMapping("/getOne") , you can write it as @GetMapping("/branch/{id}") . You can refer this blog https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/

@2ndly , Don't return a generic type as ? , instead you can user the specific type , here as Branch and do central exception handling . The following code snippet can help you :

@GetMapping("/branch/{id}")
public ResponseEntity<Branch> getBranch(@Pathvariable String id) {
{
    Branch branch = branchService.getOne(id);

    if(branch == null) {
         throw new RecordNotFoundException("Invalid Branch id : " + id);
    }
    return new ResponseEntity<Branch>(branch, HttpStatus.OK);
}

RecordNotFoundException.java

@ResponseStatus(HttpStatus.NOT_FOUND)
public class RecordNotFoundException extends RuntimeException
{
    public RecordNotFoundException(String exception) {
        super(exception);
    }
}

CustomExceptionHandler.java

@ControllerAdvice
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
        List<String> details = new ArrayList<>();
        details.add(ex.getLocalizedMessage());
        ErrorResponse error = new ErrorResponse("Server Error", details);
        return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(RecordNotFoundException.class)
    public final ResponseEntity<Object> handleRecordNotFoundException(RecordNotFoundException ex, WebRequest request) {
        List<String> details = new ArrayList<>();
        details.add(ex.getLocalizedMessage());
        ErrorResponse error = new ErrorResponse("Record Not Found", details);
        return new ResponseEntity(error, HttpStatus.NOT_FOUND);
    }
}

ErrorResponse.java

public class ErrorResponse
{
    public ErrorResponse(String message, List<String> details) {
        super();
        this.message = message;
        this.details = details;
    }

    private String message;

    private List<String> details;

    //Getter and setters
}

The above class handles multiple exceptions including RecordNotFoundException and you can also customize for payload validations too.

Test Cases :

1) HTTP GET /branch/1 [VALID]

HTTP Status : 200

{
    "id": 1,
    "name": "Branch 1",
    ...
}
2) HTTP GET /branch/23 [INVALID]

HTTP Status : 404

{
    "message": "Record Not Found",
    "details": [
        "Invalid Branch id : 23"
    ]
}
Alienee answered 22/4, 2019 at 8:17 Comment(2)
thank you, but if I get any other errors, will Exception.class handle it? Eg: Hibernate validation...Terminal
Yes it will handle.Alienee
S
1

I would recommend to do it like this .

@GetMapping("/getOne")
public Response getOne(@RequestParam String id) {
        ResponseEntity<Branch> resbranch;
        ResponseEntity<FindError> reserror;
        try {
            resbranch=new ResponseEntity<Branch>(branchService.getOne(id), HttpStatus.OK);
            return Response.status(200).entity(resbranch).build();

        } catch (Exception e) {
            reserror=new ResponseEntity<FindError>(new FindError(e.getMessage()), HttpStatus.BAD_REQUEST);
            return Response.status(400).entity(reserror).build();
        }
    }

200 is for OK and 400 is for bad request. Here there wont be anymore ambiguous types..

Soberminded answered 22/4, 2019 at 6:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.