8
13

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】受け付けたリクエストの後処理を実装する方法について調べた

Posted at

外部サービスとの API ⇔ API な連携処理を実現する案件を担当することになりまして、
タイトルについて、いろいろと調査/実装したことをメモしておきます。

環境

  • Spring Framework 4.2.4.RELEASE
  • Spring Boot 1.3.2.RELEASE

要件

外部サービス側の担当者に以下のことを言われました。

  • 必ず 200 OK を返して欲しい。
    200 OK 以外を受け取ると、監視に引っかかって運用コストが生じるのでヤダ

上記を受けて、こちら側の API を設計していたときに考えたのは以下です。

  • リクエストを受け取ったらさっさと 200 OK を返す
  • こちらが実装したい内部処理は 200 OK を返した後の処理として実装する

上記のようにしてしまえば、まず外部サービス側の要求には合うかなと。
こちらとしても、先に返すもの返してしまった方が気楽なので。
コードイメージは以下です。

SampleController.java
@RestController
public class SampleController() {
    // 外部サービスからのリクエスト受付
    @RequestMapping(value = "/sample")
    public String sample(@Validated SampleForm form) {
        // レスポンスを返すための最低限の処理
        return "";
    }

    // 後処理(あくまでイメージ:この記事を読んでいくとこんな実装にはならない)
    private void after() {
        // レスポンスを返す以外に必要な処理
    }
}

実現方法

調査したところ、HandlerInterceptor#afterCompletion() によって実現できそうです。
以下の記事を参考にしました。
Spring MVC(+Spring Boot)上でのリクエスト共通処理の実装方法を理解する

HandlerInterceptor は後処理以外の用途に向けたメソッドも持つインタフェースですが、
今回は後処理のみ個別に実装したいため、
HandlerInterceptor の実装クラスである HandlerInterceptorAdapter を継承したクラスを用意します。
また、後処理という観点であれば HandlerInterceptor#postHandle() も候補に入りますが、
こちらは例外発生時には呼び出されないため、我々の要件に合わないことから、除外しました。
HandlerInterceptor#afterCompletion() は正常終了しようが例外が発生しようが呼び出されます。

SampleInterceptor.java
public class SampleInterceptor extends HandlerInterceptorAdapter {
    // 正常終了時/異常終了時を問わず後処理のみ実装したいため afterCompletion() のみオーバーライド
    @override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, Object handler, Exception ex) throws Exception {
      // 後処理の実装
    }
}

次に、我々の要件では、上記コードイメージの /sample に対するリクエストだけに後処理を適用したいです。
これについては、上記の記事にもあります以下の実装で対応できました。

WebMvcConfig.java
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    // どうも Bean 登録しないといけない
    @Bean
    public SampleInterceptor sampleInterceptor() {
        return new SampleInterceptor();
    }
    @Bean
    public HogeInterceptor hogeInterceptor() {
        return new HogeInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(sampleInterceptor())
                .addPathPatterns("/sample"); // ここで対象を指定している
        // 複数の Interceptor を指定したければ並べて書く。チェインして複数パスの指定も OK
        registry.addInterceptor(hogeInterceptor())
                .addPathPatterns("/hoge").addPathPatterns("/huga")
        // 異なる Interceptor で同じパスを指定すると、両方の afterCompletion() が実行される
        // 実行順序がどう決められるかは未調査
                .addPathPatterns("/sample");
                
    }
}

以上で求める処理を実現できました。
addPathPatterns で対象を指定しない場合は、すべての Controller が後処理の対象になるようです。

実際のコードは上記のサンプルコードだけではなく、もう少しいろいろあるのですが、
書き留めておきたいことはこれぐらいで。

8
13
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
8
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?