spring.svg

【Spring Boot】Interceptorによる共通処理

SpringBoot

Interceptor とは

Interceptor は、コントローラーの実行前後で共通の処理を追加するための仕組みです。 主にログ出力や認可処理などの用途で使用されます。

HandlerInterceptor

Interceptor 用のクラスは、HandlerInterceptorインターフェースを実装する形で作成します。

public class MyInterceptor implements HandlerInterceptor {
  //...
}

HandlerInterceptorには以下の 3 つのメソッドがあり、これらをオーバーライドすることで処理を定義します。

preHandle

@Override
public boolean preHandle(HttpServletRequest request,
    HttpServletResponse response, Object handler) throws Exception {
  //...
  return true;
}

preHandle()は、コントローラーの実行前の処理を定義します。 リクエスト内容のログ出力や認可処理などを行ったりします。

戻り値の真偽値は、trueであればコントローラーの処理を実行し、falseであればコントローラーの処理を実行せずに200のレスポンスを返します。

引数のhandlerは、リクエストに紐づくコントローラーメソッドに該当します。 以下のようにすることで、メソッドの情報を参照できます。

Method method = ((HandlerMethod) handler).getMethod();

postHandle

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
    Object handler, @Nullable ModelAndView modelAndView) throws Exception {
  //...
}

postHandle()は、コントローラーの実行後の処理を定義します。 正確には、MVC の場合はテンプレートエンジンによるレンダリングの前に実行される処理で、REST API の場合はレスポンス送信前の処理となります。

引数のhandlerは、preHandle()と同様にコントローラーメソッドに該当します。

modelAndViewは、MVC の場合に設定した Model と View の情報に該当します。 ここで処理に共通する情報を Model に追加するなどを行います。

modelAndView.addObject("info", "Hello World!");

REST API の場合は、関係のないものなのでmodelAndViewnullとなります。

コントローラーで例外がスローされた場合、postHandle()は実行されません。

afterCompletion

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
    Object handler, @Nullable Exception ex) throws Exception {
  //...
}

afterCompletion()は、クライアントへレスポンスを送信した後の処理となります。 MVC の場合は、テンプレートエンジンによるレンダリングが完了してレスポンスを送信していることになります。

引数のhandlerは、他と同様にコントローラーメソッドに該当します。

exは、レンダリングで発生した例外が設定されます。 コントローラーでスローされた例外が設定されるわけではないので注意してください。 REST API ではレンダリング処理がないため、必ずnullとなります。

afterCompletion()postHandle()と異なり、コントローラーで例外がスローされても実行されます。 そのため、コントローラー実行後に必ず行いたい処理はafterCompletion()に定義するとよいです。

Interceptor の登録

登録手順

作成した Interceptor を実行させるためには、WebMvcConfigurerインターフェースを実装したConfigurationクラスで登録する必要があります。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
  //...
}

登録手順として、初めに作成した Interceptor を DI コンテナに登録します。

@Bean
MyInterceptor myInterceptor() {
  return new MyInterceptor();
}

次に Interceptor 登録用のメソッドであるaddInterceptors()をオーバーライドします。

@Override
public void addInterceptors(InterceptorRegistry registry) {
  //...
}

最後に、引数のregistryに Interceptor を追加すれば完了です。

registry.addInterceptor(myInterceptor());

コード全体は以下のようになります。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
  @Bean
  MyInterceptor myInterceptor() {
    return new MyInterceptor();
  }

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(myInterceptor());
  }
}

実行順序

Interceptor は、以下のように複数登録することが可能です。

registry.addInterceptor(myInterceptor1());
registry.addInterceptor(myInterceptor2());

基本的には登録した順に Interceptor の処理が実行されます。 もう少し詳しくすると、以下の順で実行されます。

順序クラスメソッド
1MyInterceptor1preHandle()
2MyInterceptor2preHandle()
3Controllerhoge()
4MyInterceptor2postHandle()
5MyInterceptor1postHandle()
6MyInterceptor2afterCompletion()
7MyInterceptor1afterCompletion()

実行順序を変更したい場合はorder()を使用します。 指定した数値の小さい順に Interceptor が実行されるようになります。

registry.addInterceptor(myInterceptor1()).order(2);
registry.addInterceptor(myInterceptor2()).order(1);

パスパターン

Interceptor は、パスパターンによって処理の実行を制御することができます。

特定のパスで実行したい場合は、addPathPatterns()を使用します。 以下の例は/api/以下すべてのパスでのみ実行されます。

registry.addInterceptor(myInterceptor())
        .addPathPatterns("/api/**");
パス実行可否
/api
/api/user
/api/user/0001
/user×
/user/api×

対照的な処理としてexcludePathPatterns()があります。 これは指定したパス以外で Interceptor の処理を実行することができます。 以下の例はaddPathPatterns()とは逆の結果となります。

registry.addInterceptor(myInterceptor())
        .excludePathPatterns("/api/**");
パス実行可否
/api×
/api/user×
/api/user/0001×
/user
/user/api

これら 2 つの処理は組み合わせることが可能です。 以下の例は、「/app/以下で実行するが、/api/userは対象外とする」となります。

registry.addInterceptor(myInterceptor())
        .addPathPatterns("/api/**");
        .excludePathPatterns("/api/user");