20
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Spring Boot のデフォルトエラーメッセージを制御したい

Last updated at Posted at 2018-05-18

目的

Spring Boot で作成した REST API でエラーが発生したときに返る、デフォルトのエラーメッセージを制御したい。

背景

以下のような 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(); // 想定される Exception
        }
        
        return "Hello world!!";
    }

    @RequestMapping(value="/test-null")
    private String hello(){
        String s = null;
        s.equals("hoge"); // うっかり NPE 発生
        
        return "Hello world!!";
    }
}

すると、Spring Boot はデフォルトで以下のようなエラーメッセージを返却してくれる。

$ curl http://localhost:8080/test/0000 # 想定される 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
{"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 # パス間違い
{"timestamp":1526654639289,"status":404,"error":"Not Found","message":"No message available","path":"/wrong-path"}%

しかし、うっかりで発生してしまった NullPointerException がユーザに見えてしまうのはあまりにもかっこ悪いし、セキュリティ的にもよろしくない。Spring Boot を使っているのもばれてしまう。

想定されるエラーの制御

@ControllerAdvice アノテーションを付与したクラスを用意することで、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 {

    /**
     * MyCustomException が発生した際のハンドラ
     * 
     * @return レスポンス
     */
    @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.%

想定外のエラーに対する制御

上記に加えて、Exception.class を受ける ExceptionHandler を作ってしまえば、むりやり、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 {

    /**
     * MyCustomException が発生した際のハンドラ
     * 
     * @return レスポンス
     */
    @ExceptionHandler(MyCustomException.class)
    public ResponseEntity<String> handleControllerException() {
        return new ResponseEntity<String>("customized message for MyCustomError.", HttpStatus.INTERNAL_SERVER_ERROR);
    }
    
    /**
     * Exception が発生した際のハンドラ
     * 
     * @return レスポンス
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleControllerException2() {
        return new ResponseEntity<String>("customized message for any unexpected exception.", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

しかしこの方法では、パス間違いなど、RestController に入る以前のエラーは制御できない。

$ 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 # パス間違いに対してはデフォルトエラーが返ってしまう
{"timestamp":1526655182855,"status":404,"error":"Not Found","message":"No message available","path":"/wrong-path"}%

調べてみると、公式に以下のような記述が。

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.

要は、あらゆるエラーはデフォルトで /error 以下にマッピングされるとのこと。
好きに制御したい場合は、ErrorController を実現せよ、とのことなので、以下のようなクラスを用意する。

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";
    }
}

すると無事、デフォルトのエラーメッセージが表示されなくなった。

$ 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.%
20
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?