【Spring Boot】Interceptorによる共通処理
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 の場合は、関係のないものなのでmodelAndView
はnull
となります。
コントローラーで例外がスローされた場合、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 の処理が実行されます。 もう少し詳しくすると、以下の順で実行されます。
順序 | クラス | メソッド |
---|---|---|
1 | MyInterceptor1 | preHandle() |
2 | MyInterceptor2 | preHandle() |
3 | Controller | hoge() |
4 | MyInterceptor2 | postHandle() |
5 | MyInterceptor1 | postHandle() |
6 | MyInterceptor2 | afterCompletion() |
7 | MyInterceptor1 | afterCompletion() |
実行順序を変更したい場合は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");