I want to control the default error message returned when an error occurs in the REST API created by Spring Boot.
Suppose you have prepared the following RestController.
MyRestController.java
package demo;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyRestController {
@RequestMapping(value="/test/{id}")
private String hello(@PathVariable String id){
if ("0000".equals(id)) {
throw new MyCustomException(); //Expected Exception
}
return "Hello world!!";
}
@RequestMapping(value="/test-null")
private String hello(){
String s = null;
s.equals("hoge"); //Inadvertent NPE outbreak
return "Hello world!!";
}
}
Spring Boot will then return the following error message by default.
$ curl http://localhost:8080/test/0000 #Expected MyCustomError
{"timestamp":1526654632290,"status":500,"error":"Internal Server Error","exception":"my.spring.MySpringBootSample.MyCustomError","message":"No message available","path":"/test/0000"}%
$ curl http://localhost:8080/test-null #NPE that I accidentally put out
{"timestamp":1526654636310,"status":500,"error":"Internal Server Error","exception":"java.lang.NullPointerException","message":"No message available","path":"/test-null"}%
$ curl http://localhost:8080/wrong-path #Wrong path
{"timestamp":1526654639289,"status":404,"error":"Not Found","message":"No message available","path":"/wrong-path"}%
However, it's too cool and not good for security to see the NullPointerException that happened accidentally to the user. I'm also exposed to using Spring Boot.
By preparing a class with @ControllerAdvice
annotation, it is possible to control error messages across RestController.
MyControllerAdvice.java
package demo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class MyControllerAdvice {
/**
*Handler when MyCustomException occurs
*
* @return response
*/
@ExceptionHandler(MyCustomException.class)
public ResponseEntity<String> handleControllerException() {
return new ResponseEntity<String>("customized message for MyCustomError.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
$ curl http://localhost:8080/test/0000
customized message for MyCustomError.%
In addition to the above, if you create an ExceptionHandler that receives Exception.class, it is possible to return a custom error message uniformly for all unexpected errors that occur in RestController.
MyControllerAdvice.java
package demo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class MyControllerAdvice {
/**
*Handler when MyCustomException occurs
*
* @return response
*/
@ExceptionHandler(MyCustomException.class)
public ResponseEntity<String> handleControllerException() {
return new ResponseEntity<String>("customized message for MyCustomError.", HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
*Handler when an Exception occurs
*
* @return response
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleControllerException2() {
return new ResponseEntity<String>("customized message for any unexpected exception.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
However, this method does not control errors before entering RestController, such as incorrect paths.
$ curl http://localhost:8080/test/0000
customized message for MyCustomError.%
$ curl http://localhost:8080/test-null
customized message for any unexpected exception.%
$ curl http://localhost:8080/wrong-path #Default error is returned for wrong path
{"timestamp":1526655182855,"status":404,"error":"Not Found","message":"No message available","path":"/wrong-path"}%
When I looked it up, it was [Official](https://docs.spring.io/spring-boot/docs/1.5.3.RELEASE/reference/html/boot-features-developing-web-applications.html#boot-features -error-handling) has the following description.
Spring Boot provides an /error mapping by default that handles all errors in a sensible way, and it is registered as a ‘global’ error page in the servlet container.
The point is that every error is mapped under / error
by default.
If you want to control it as you like, realize ErrorController, so prepare the following class.
MyErrorController.java
package demo;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyErrorController implements ErrorController {
@RequestMapping("/error")
public ResponseEntity<String> handleError() {
return new ResponseEntity<String>("customized message for any unhandled error.", HttpStatus.INTERNAL_SERVER_ERROR);
}
@Override
public String getErrorPath() {
return "/error";
}
}
Then, the default error message disappeared.
$ curl http://localhost:8080/test/0000
customized message for MyCustomError.%
$ curl http://localhost:8080/test-null
customized message for any unexpected exception.%
$ curl http://localhost:8080/wrong-path
customized message for any unhandled error.%
Recommended Posts