[JAVA] Customize REST API error response with Spring Boot (Part 1)

Overview

I tried to customize the response at the time of error with Spring Boot, so I will describe it here as a memorandum. This time, I would like to customize the body part of the response as follows.

Change before

{
  "timestamp": "2019-03-14T15:07:50.631+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "MyException occurred",
  "path": "/××××/××××"
}

After change

{
  "timestamp": "2019-03-14T15:07:50.631+0000",
  "status": 500,
  "error": "Internal Server Error",
  "message": "MyException occurred",
  "path": "/××××/××××",
  "errorDetail": {
    "detailMessage": "××××××××××××"
  }
}

Prerequisites

environment

Process flow

The following four characters are roughly the characters this time.

--Controller class that accepts requests --Original exception class --Class for response body --Class to catch the unique exception that occurred

In addition, the general flow of the process to be implemented from now on is as follows.

  1. Explicitly throw from the controller a uniquely defined exception at the same time as the request
  2. (Create a class that handles the exception) Catch the throwed exception with the handle class
  3. Returns a customized response body

After that, we will implement the above processing. (Although the order is out of order ...)

Implementation

Mapping class definition

Here we define the mapping class for the response body.

ErrorResponseBody.java


package com.example.demo.errorResponse;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Setter;

import java.time.ZonedDateTime;

@Setter
public class ErrorResponseBody {

        @JsonProperty("timestamp")
        private ZonedDateTime exceptionOccurrenceTime;
        @JsonProperty("status")
        private int status;
        @JsonProperty("error")
        private String error;
        @JsonProperty("message")
        private String message;
        @JsonProperty("path")
        private String path;
        @JsonProperty("errorDetail")
        private ErrorDetail errorDetail;
}

ErrorDetail.java


package com.example.demo.errorResponse;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Setter;

@Setter
public class ErrorDetail {
    @JsonProperty("detailMessage")
    String detailMessage;
}

Definition of unique exception class

MyException.java


package com.example.demo.errorResponse;

import lombok.Getter;

@Getter
public class MyException extends RuntimeException {

    private ErrorDetail errorDetails;

    public MyException(String message, ErrorDetail errorDetails) {
        super(message);
        this.errorDetails = errorDetails;
    }
}

Controller definition

HelloController.java


package com.example.demo.errorResponse;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/exceptionhandle")
public class HelloController {

    @RequestMapping("/test")
    public String get() {
        ErrorDetail errorDetail = new ErrorDetail();
        errorDetail.setDetailMessage("Detailed message");
        throw new MyException("MyException occurred", errorDetail);
    }
}

Exception Handler class definition

This is the most important part. There are four points.

  1. Add @RestControllerAdvice annotation to the handle class
  2. Inherit the ResponseEntityExceptionHandler class
  3. Annotate @ExceptionHandler and specify the uniquely defined exception class to be supplemented.
  4. Pass the instance of the class defined for the response body to the second parameter of the super.handleExceptionInternal method (as an aside, the arguments of this method are in the following order).

--Target exception class --Response body

And finally it returns ResponseEntity.

HelloExceptionHandler.java


package com.example.demo.errorResponse;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.time.ZonedDateTime;

@RestControllerAdvice
public class HelloExceptionHandler extends ResponseEntityExceptionHandler {
    //Catch MyException thrown from Controller
    @ExceptionHandler(MyException.class)
    public ResponseEntity<Object> handleMyException(MyException exception, WebRequest request) {
        HttpHeaders headers = new HttpHeaders();

        return super.handleExceptionInternal(exception,
                createErrorResponseBody(exception, request),
                headers,
                HttpStatus.BAD_REQUEST,
                request);
    }

    //Create the body part of the response
    private ErrorResponseBody createErrorResponseBody(MyException exception, WebRequest request) {

        ErrorResponseBody errorResponseBody = new ErrorResponseBody();
        int responseCode = HttpStatus.BAD_REQUEST.value();
        String responseErrorMessage = HttpStatus.BAD_REQUEST.getReasonPhrase();
        String uri = ((ServletWebRequest) request).getRequest().getRequestURI();

        errorResponseBody.setExceptionOccurrenceTime(ZonedDateTime.now());
        errorResponseBody.setStatus(responseCode);
        errorResponseBody.setError(responseErrorMessage);
        errorResponseBody.setMessage(exception.getMessage());
        errorResponseBody.setPath(uri);
        errorResponseBody.setErrorDetail(exception.getErrorDetails());

        return errorResponseBody;
    }
}

As an aside, the handleExceptionInternal method of the parent class ResponseEntityExceptionHandler class looks like this: Since the part that passes the instance for error response is @Nullable Object body, the body part will not be displayed unless it is passed properly.

ResponseEntityExceptionHandler.java


	/**
	 * A single place to customize the response body of all Exception types.
	 * <p>The default implementation sets the {@link WebUtils#ERROR_EXCEPTION_ATTRIBUTE}
	 * request attribute and creates a {@link ResponseEntity} from the given
	 * body, headers, and status.
	 * @param ex the exception
	 * @param body the body for the response
	 * @param headers the headers for the response
	 * @param status the response status
	 * @param request the current request
	 */
	protected ResponseEntity<Object> handleExceptionInternal(
			Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {

		if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
			request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
		}
		return new ResponseEntity<>(body, headers, status);
	}

result

{
  "timestamp": "2019-03-18T10:11:13.795+09:00",
  "status": 400,
  "error": "Bad Request",
  "message": "MyException occurred",
  "path": "/exceptionhandle/test",
  "errorDetail": {
    "detailMessage": "Detailed message"
  }
}

Related article

Customize REST API error response with Spring Boot (Part 2)

Reference article

This time, I referred to the following article when writing the article.

Customize the error response of REST API created by Spring Boot Error handling with Spring Boot Handling exceptions with @ RestController of Spring Boot

Recommended Posts

Customize REST API error response with Spring Boot (Part 1)
Hello World (REST API) with Apache Camel + Spring Boot 2
[Spring Boot] Get user information with Rest API (beginner)
Implement a simple Rest API with Spring Security with Spring Boot 2.0
Spring with Kotorin --4 REST API design
REST API test with REST Assured Part 2
Implement REST API in Spring Boot
Implement REST API with Spring Boot and JPA (Application Layer)
Implement REST API with Spring Boot and JPA (Infrastructure layer)
Implement REST API with Spring Boot and JPA (domain layer)
Implement a simple Rest API with Spring Security & JWT with Spring Boot 2.0
Implement a simple Web REST API server with Spring Boot + MySQL
Change Spring Boot REST API request / response from CamelCase to SankeCase
[Beginner] Let's write REST API of Todo application with Spring Boot
Create a web api server with spring boot
Download with Spring Boot
I created an api domain with Spring Framework. Part 2
Automatically map DTOs to entities with Spring Boot API
Extend Spring Boot DefaultErrorViewResolver to dynamically customize error screens
A memorandum when creating a REST service with Spring Boot
I created an api domain with Spring Framework. Part 1
Introduce swagger-ui to REST API implemented in Spring Boot
Generate barcode with Spring Boot
Hello World with Spring Boot
Implement GraphQL with Spring Boot
Hello World with Spring Boot!
Run LIFF with Spring Boot
SNS login with Spring Boot
File upload with Spring Boot
Spring Boot starting with copy
Spring Boot starting with Docker
Hello World with Spring Boot
Set cookies with Spring Boot
REST API testing with REST Assured
Use Spring JDBC with Spring Boot
Add module with Spring Boot
Link API with Spring + Vue.js
Introduction to Spring Boot Part 1
Create microservices with Spring Boot
Send email with spring boot
Handle Java 8 date and time API with Thymeleaf with Spring Boot
Try hitting the zip code search API with Spring Boot
Let's find out how to receive in Request Body with REST API of Spring Boot
Use Basic Authentication with Spring Boot
Let's make a book management web application with Spring Boot part1
gRPC on Spring Boot with grpc-spring-boot-starter
Create an app with Spring Boot 2
Hot deploy with Spring Boot development
Database linkage with doma2 (Spring boot)
Let's make a book management web application with Spring Boot part3
Spring Boot: Restful API sample project
Spring Boot programming with VS Code
Inquiry application creation with Spring Boot
Part 1: Try using OAuth 2.0 Login supported by Spring Security 5 with Spring Boot
How to create your own Controller corresponding to / error with Spring Boot
Let's make a book management web application with Spring Boot part2
Get validation results with Spring Boot
spring boot access authorization RESTful API
I made a simple search form with Spring Boot + GitHub Search API.
(Intellij) Hello World with Spring Boot
Create an app with Spring Boot