Spring Bootの例外処理

例外処理

Spring Bootは標準で、REST clientへ例外情報とHTTP status codeを含むJSONを返します。browserにはWhitelabel Error Pageを表示します。

特定の例外にStatus Codeを指定する

package sample.springboot.web;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class MyException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public MyException(String msg) { super(msg); }
}
package sample.springboot.web;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class WebApiController {
    @RequestMapping(method=RequestMethod.GET)
    public void method1() { throw new MyException("test exception"); }
}
$ curl http://localhost:8080/api
{"timestamp":1430489386562,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}

例外classに@ResponseStatusを付けると、throw時のstatusを指定できます。browserには標準error pageが表示されます。

すべての例外を処理する

package sample.springboot.web;
import javax.servlet.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.*;

@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println(ex.getClass() + " : " + ex.getMessage());
        ModelAndView mv = new ModelAndView();
        mv.setViewName("my-error");
        return mv;
    }
}
<h1>My Error Page</h1>

WebApiControllerへ次のmethodを追加します。

@RequestMapping(value="/null", method=RequestMethod.GET)
public void method2() {
    throw new NullPointerException("test exception");
}
$ curl http://localhost:8080/api
{"timestamp":1430490625809,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}
$ curl http://localhost:8080/api/null
<h1>My Error Page</h1>

HandlerExceptionResolverを実装して@Componentで登録します。controllerで例外が発生するとresolveException()が呼ばれ、ModelAndViewを返せます。@ResponseStatus付き例外は先に処理されます。

Web APIではJSONを返す

REST controllerでは標準処理を使うようresolverを変更します。

@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (isRestController(handler)) return null;
        ModelAndView mv = new ModelAndView();
        mv.setViewName("my-error");
        return mv;
    }
    private boolean isRestController(Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod)handler;
            return method.getMethod().getDeclaringClass().isAnnotationPresent(RestController.class);
        }
        return false;
    }
}
$ curl http://localhost:8080/api/null
{"timestamp":1430490749586,"status":500,"error":"Internal Server Error","exception":"java.lang.NullPointerException","message":"test exception","path":"/api/null"}

resolveException()からnullを返すと標準処理へ委譲します。browser画面を表示する場合は通常の@Controllerからerror pageを返します。

Controller単位で例外処理を定義する

WebApiController@ExceptionHandler methodを追加します。

@ExceptionHandler(NullPointerException.class)
public String handling(NullPointerException e) {
    return "{\"message\":\"" + e.getMessage() + "\"}";
}
$ curl http://localhost:8080/api/null
{"message":"test exception"}

@ExceptionHandlerは指定した例外型を、そのcontroller内だけで処理します。