minizibi
minizibi

Reputation: 671

Configure ResponseEntity body while throwing exception

I created an generic exception DTO which also extends RuntimeException. In this way it's possible to use it in both application and as DTO. The problem is when I applying my DTO to ResponseEntity constructor. Then i got additional whole stack trace:

{
"cause": null,
"stackTrace": [
    {
        "methodName": "handleConstraintViolations",
        "fileName": "ProductExceptionHandler.java",
        "lineNumber": 66,
        "className": "api.product.infrastructure.ProductExceptionHandler",
        "nativeMethod": false
    },
    {
        "methodName": "invoke0",
        "fileName": "NativeMethodAccessorImpl.java",
        "lineNumber": -2,
        "className": "sun.reflect.NativeMethodAccessorImpl",
        "nativeMethod": true
    },
    {
        "methodName": "invoke",
        "fileName": "NativeMethodAccessorImpl.java",
        "lineNumber": 62,
        "className": "sun.reflect.NativeMethodAccessorImpl",
        "nativeMethod": false
    },
    {
        "methodName": "invoke",
        "fileName": "DelegatingMethodAccessorImpl.java",
        "lineNumber": 43,
        "className": "sun.reflect.DelegatingMethodAccessorImpl",
        "nativeMethod": false
    },
    {
        "methodName": "invoke",
        "fileName": "Method.java",
        "lineNumber": 498,
        "className": "java.lang.reflect.Method",
        "nativeMethod": false
    },
    {
        "methodName": "doInvoke",
        "fileName": "InvocableHandlerMethod.java",
        "lineNumber": 205,
        "className": 
"org.springframework.web.method.support.InvocableHandlerMethod",
        "nativeMethod": false
    },
....
"status": "UNPROCESSABLE_ENTITY",
"message": "Constraint violation(s)",
"errors": [
    "name size must be between 1 and 2147483647"
],
"localizedMessage": "Constraint violation(s)",
"suppressed": []
}

My Exception DTO class:

@Getter
@EqualsAndHashCode(callSuper = true)
public class ProductException extends RuntimeException implements Serializable {

private final HttpStatus status;
private final String message;
private final String[] errors;

public ProductException(HttpStatus status, String message, String... errors) {
    this.status = status;
    this.message = message;
    this.errors = errors;
}

public ProductException(HttpStatus status, String message, Set<ConstraintViolation<?>> constraintViolation) {
    this.status = status;
    this.message = message;
    this.errors = constraintViolation.stream()
            .map(violation -> violation.getPropertyPath().toString() + " " + violation.getMessage())
            .toArray(String[]::new);
}
}

And I'm calling this like:

 @ExceptionHandler(TransactionSystemException.class)
public ResponseEntity<ProductException> handleConstraintViolations(TransactionSystemException tse) {
    ConstraintViolationException cve = (ConstraintViolationException) tse.getMostSpecificCause();
    ProductException productException = new ProductException(HttpStatus.UNPROCESSABLE_ENTITY, "Constraint violation(s)", cve.getConstraintViolations());
    return new ResponseEntity<>(productException, new HttpHeaders(), productException.getStatus());

I have two solutions in my mind, first is creating adapter which transfers my Exception to one's non extending RuntimeException, another idea is to create a method like .toBody() in my Exception. The best would be prevent ResponseEntity from adding attributes like "cause", "stacktrace" etc. Any ideas?

Upvotes: 0

Views: 153

Answers (1)

gWombat
gWombat

Reputation: 517

first is creating adapter which transfers my Exception to one's non extending RuntimeException

In my opinion this is your best choice. By doing this, you will have a good control on the information that will be available for the client that will get the exception (in your app or as a DTO, as you said).

In your @ExceptionHandler you just have to bind the useful information which need to be returned or not (and maybe log what should be logged).

Upvotes: 1

Related Questions