简体   繁体   中英

Spring Boot Rest Controller: Return default Error JSON

Within my API I like to protect the user details endpoints, so that normal logged in users can only access their user profile. Therefor I am writing the controller:

@RequestMapping(value = URL_USER + "/{id}", method = RequestMethod.GET)
@ResponseBody
public PersistentEntityResource get(PersistentEntityResourceAssembler persistentEntityResourceAssembler, @PathVariable Long id) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    ApplicationUser loggedInUser = applicationUserService.findByUsername(authentication.getName());
    ApplicationUser applicationUser = applicationUserService.findById(id);

    if (applicationUser.getId().equals(loggedInUser.getId())) {
        return persistentEntityResourceAssembler.toFullResource(applicationUser);
    }

    throw new IllegalAccessException();
} 

In stead of raiseing an Exception which leads to InternalServerExcetption , I like to return the default spring boot error json, like the following:

{
    "timestamp": "2019-05-08T11:42:23.064+0000",
    "status": 403,
    "error": "Forbidden",
    "message": "Access Denied",
    "path": "/user/2"
}

I would prefere a solution, which works as well for other Erros like 404. What would be the easiest way to achieve that goal?

You can use below methods for the same

public class FooController{

    //...
    @ExceptionHandler({ CustomException1.class, CustomException2.class })
    public String handleException() {
        return "the intended body";
    }
}

Alternatively you can put this logic in as a Global Exceptions Handler using @ControllerAdvice

@ControllerAdvice
public class RestResponseEntityExceptionHandler
        extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value
            = { IllegalArgumentException.class, IllegalStateException.class })
    protected ResponseEntity<Object> handleConflict(
            RuntimeException ex, WebRequest request) {
        String bodyOfResponse = "This should be application specific";
        return handleExceptionInternal(ex, bodyOfResponse,
                new HttpHeaders(), HttpStatus.CONFLICT, request);
    }
}

After some research I found a solution which looks quite elegant:

RestController Method looks like that:

@RequestMapping(value = URL_USER + "/{id}", method = RequestMethod.GET)
@ResponseBody
public PersistentEntityResource get(PersistentEntityResourceAssembler persistentEntityResourceAssembler, @PathVariable Long id) {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

    ApplicationUser loggedInUser = applicationUserService.findByUsername(authentication.getName());
    ApplicationUser applicationUser = applicationUserService.findById(id);

    if (applicationUser.getId().equals(loggedInUser.getId())) {
        return persistentEntityResourceAssembler.toFullResource(applicationUser);
    }

    throw new ForbiddenRequestException("Access not allowed");
}

Addionally I have implemented the class ForbiddenRequestException :

@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class ForbiddenRequestException extends RuntimeException {
    public ForbiddenRequestException(String message) {
        super(message);
    }
}

And at least to remove the trace from the JSON by setting server.error.include-stacktrace=never in the properties, this might not be ideal, but I think in production you should do this anyway.

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