Skip to content

Instantly share code, notes, and snippets.

@sugaryo
Last active November 19, 2019 18:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sugaryo/696c6401d6c64acabc006dc669f59d8e to your computer and use it in GitHub Desktop.
Save sugaryo/696c6401d6c64acabc006dc669f59d8e to your computer and use it in GitHub Desktop.
ControllerのExceptionHandlerを基底クラスで集約する実験
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
public abstract class BaseController {
private static final Logger log = LoggerFactory.getLogger( BaseController.class );
@RequestMapping("ex")
public String testException() throws Exception {
throw new RuntimeException( "エラー発生" );
}
@ExceptionHandler
private ResponseEntity<String> onError( Exception ex ) {
log.error( ex.getMessage(), ex );
HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
String detail = JsonMapper.map()
.put( "message", "API エラー" )
.put( "detail", ex.getMessage() )
.put( "status", status.value() )
.stringify();
return new ResponseEntity<String>( detail, status );
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("hoge")
@RestController
public class HogeController extends BaseController {
@GetMapping("do")
public void exec() {
throw new RuntimeException( "hoge do failed." );
}
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("moge")
@RestController
public class MogeController extends BaseController {
@GetMapping("do")
public void exec() {
throw new RuntimeException( "moge do failed." );
}
}

各コントローラ概要

BaseController

  • 抽象基底クラス。
  • コレ自体には @RestController を付けていないので コンポーネントスキャンの対象外 になる筈。
  • @ExceptionHandler でアノテートした例外ハンドラを持つ。
  • @RequestMapping("ex") でアノテートしたエンドポイントを持つ。

HogeController

  • BaseControllerから派生。
  • HogeControllerには @RestController でアノテートして、普通にRESTコントローラとして作成。
  • BaseControllerを継承しているので 相対パス /ex でアノテートしたエンドポイント を持っている筈。
  • 継承メソッドとは別に 相対パス /do でアノテートした エンドポイントも独自実装している。
  • このクラスでは例外ハンドラを独自実装しない。

MogeController

  • BaseControllerから派生。
  • MogeControllerには @RestController でアノテートして、普通にRESTコントローラとして作成。
  • BaseControllerを継承しているので 相対パス /ex でアノテートしたエンドポイント を持っている筈。
  • 継承メソッドとは別に 相対パス /do でアノテートした エンドポイントも独自実装している。
  • このクラスでは例外ハンドラを独自実装しない。

実験目的

  • 基底クラスが持っている例外ハンドラを派生クラスがきちんと使えているか。
    • localhost:port/hoge/do
    • localhost:port/moge/do
    • 上記エンドポイントがそれぞれ正しく機能しているかをみる。
  • 基底クラスで定義したエンドポイントを派生クラスがきちんと使えているか。
    • localhost:port/hoge/ex
    • localhost:port/moge/ex
    • 上記エンドポイントがそれぞれ正しく機能しているかをみる。

実験結果

/hoge/do

  • curl -X GET -v http://localhost:8989/hoge/do
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8989 (#0)
> GET /hoge/do HTTP/1.1
> Host: localhost:8989
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 503
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 67
< Date: Tue, 19 Nov 2019 18:02:28 GMT
< Connection: close
<
* Closing connection 0
{"detail":"hoge do failed.","message":"API エラー","status":503}

/moge/do

  • curl -X GET -v http://localhost:8989/moge/do
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8989 (#0)
> GET /moge/do HTTP/1.1
> Host: localhost:8989
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 503
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 67
< Date: Tue, 19 Nov 2019 18:04:15 GMT
< Connection: close
<
* Closing connection 0
{"detail":"moge do failed.","message":"API エラー","status":503}

/hoge/ex

  • curl -X GET -v http://localhost:8989/hoge/ex
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8989 (#0)
> GET /hoge/ex HTTP/1.1
> Host: localhost:8989
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 503
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 67
< Date: Tue, 19 Nov 2019 18:04:45 GMT
< Connection: close
<
* Closing connection 0
{"detail":"エラー発生","message":"API エラー","status":503}

/moge/ex

  • curl -X GET -v http://localhost:8989/moge/ex
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8989 (#0)
> GET /moge/ex HTTP/1.1
> Host: localhost:8989
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 503
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 67
< Date: Tue, 19 Nov 2019 18:05:35 GMT
< Connection: close
<
* Closing connection 0
{"detail":"エラー発生","message":"API エラー","status":503}

結果

基底クラスにアノテートしたメソッドも、派生クラスのインスタンスで正しくスキャン対象になってくれてるっぽい。 これで業務的な共通APIみたいなのを基底クラスで握ったり、例外ハンドラを集約して共通化したり出来そう。

確かJavaEEの時も、@ManagedBean@PostConstruct でアノテートしたメソッドは正しく派生クラスでも使えてたし、 このへんのDIコンテナ管理Beanの仕組みは上手く出来ているらしい。

「原理的に考えて普通じゃね?」って気もするけど、普通に予測したものが普通に動いてくれる安心感があるのはフレームワークの重要なところ。

何よりその確認を取るのは大事なこと。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment