How to implement service response Too Many Requests

Hi!

Please help.
How to throw exception from service in middleware with 429 error?
When I make in service method
throw new HttpClientErrorException(HttpStatus.TOO_MANY_REQUESTS);
I receive HTTP 500 error from tomcat.

How to do this in correct way?

Hi,
you may try to create your own exception handler that will process your exception and return a ResponseEntity with a proper http status. See this topic: REST API custom exception handling

Thanks.
Today realized

public class CustomRestControllerExceptionHandler {
private Logger log = LoggerFactory.getLogger(CustomRestControllerExceptionHandler.class);

@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity<ErrorInfo> handleException(Exception e) {
    log.error("Exception in REST controller", e);
    ErrorInfo errorInfo = new ErrorInfo("Server error", e.getMessage());
    if (e instanceof RestAPIException) {
        String details = ((RestAPIException) e).getDetails();
        HttpStatus status = ((RestAPIException) e).getHttpStatus();
        ErrorInfo server_error = new ErrorInfo( e.getMessage(), details);
        if (details.contains("403")) {
            status = HttpStatus.FORBIDDEN;
        }
        if (details.contains("429")) {
            status = HttpStatus.TOO_MANY_REQUESTS;
        }
        return new ResponseEntity<>(server_error, status);
    }
    return new ResponseEntity<>(errorInfo, HttpStatus.INTERNAL_SERVER_ERROR);
}

May be exist more conventient way to get HttpsStatus from fired exception?

Could you clarify? You get the status in this line:

HttpStatus status = ((RestAPIException) e).getHttpStatus();

What is the problem?

Yes. Problem with this status. It is always 500.
And it’s need to parse text from detail to detect correct HttpStatus

How do you raise the RestAPIException? What status value do you pass to the constructor?

Answer is in first question. I asked how to return 429 http code from service.
Now I call

throw new HttpClientErrorException(HttpStatus.TOO_MANY_REQUESTS);

Why not to:

throw new RestAPIException(message, details, httpStatus);

?

Oh, you throw it from the middleware service, as I see from the first message.

Then maybe your exception handler should handle not the Exception.class but your exception class (HttpClientErrorException.class in your case)?

@ExceptionHandler(HttpClientErrorException.class)
@ResponseBody
public ResponseEntity<ErrorInfo> handleException(HttpClientErrorException e) {

When I use this in service bean there is compile time error

package com.haulmont.restapi.exception does not exist
import com.haulmont.restapi.exception.RestAPIException;

You can do the following.

Instead of the HttpClientErrorException throw your own custom exception:

@SupportedByClient
public class MyRestException extends RuntimeException {

    private HttpStatus httpStatus;

    public MyRestException(String message, HttpStatus httpStatus) {
        super(message);
        this.httpStatus = httpStatus;
    }

    public HttpStatus getHttpStatus() {
        return httpStatus;
    }
}

Place the exception to the global module. Note that the exception has the @SupportedByClient annotation.

Your exception handler will look like this:

@ControllerAdvice("com.haulmont.restapi.controllers")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyRestControllerExceptionHandler {

    @ExceptionHandler(RestAPIException.class)
    @ResponseBody
    public ResponseEntity<ErrorInfo> handleException(RestAPIException e) {
        Throwable cause = e.getCause();
        if (cause instanceof MyRestException) {
            ErrorInfo errorInfo = new ErrorInfo(cause.getMessage(), cause.getMessage());
            return new ResponseEntity<>(errorInfo, ((MyRestException) cause).getHttpStatus());
        }
        ErrorInfo errorInfo = new ErrorInfo(e.getMessage(), e.getDetails());
        return new ResponseEntity<>(errorInfo, e.getHttpStatus());
    }
}
1 Like

Max, last solution works Ok.
Thank you!