LoginSignup
1
0

More than 1 year has passed since last update.

【SpringBoot】プログラムの中からAPIエンドポイントの情報を取得する

Last updated at Posted at 2022-09-14

はじめに

対象の方

  • SpringBoot(SpringMVC/spring-boot-starter-web)を使ってる人
  • アプリで使用可能な API エンドポイントの情報を取りたい人

サンプルは Kotlin で記述していますが、Java でも同等のことが実現可能かと思います。

忙しい人向け

RequestMappingHandlerMapping の Bean を引き込んで getHandlerMethods() を叩けば取れます。

取得方法

具体的にどのようなことをすればタイトルのようなことが実現できるか見ていきます。

前準備

RequestMappingHandlerMappingクラスのインスタンスを DI などでコンテナから取得します。

このクラスは Spring のコンテナに Bean として登録されていますので、
コンストラクタインジェクション等で容易にインスタンスを確保することが出来ます。

こんなかんじ。
image.png

エンドポイント一覧の取得

RequestMappingHandlerMapping#getHandlerMethodsを呼び出すことで、API エンドポイントの一覧を取得できます。

image.png

おまけ

戻り値

getHandlerMethods()の戻り値はRequestMappingInfoクラスとHandlerMethodクラスの Map です。
それぞれどのような情報を持っているかを見ていきます。

HandlerMethod

呼び出し先の Controller クラスに定義されている関数の情報が収められています。
下記のデバッグウィンドウに表示されている通り、関数の参照(リフレクションで取得できる Method クラス)や関数が定義されている Controller クラスの情報等が取得できます。
image.png

RequestMappingInfo

エンドポイントのパス情報と対応している HTTP メソッドの種別などが収められています。
下記のデバッグウィンドウに表示されている通り、RequestMapping アノテーションに設定されているパス(/api/user)と、fetchUser()に設定されている HTTP メソッド(GET、HEAD)等が取得できます。
image.png

Key と Value の組み合わせで表現されるもの

「この HTTP メソッドとパスを持つリクエストは、この関数で処理を行う」…を表現します。
RequestMappingHandlerMapping クラスの名前の通り、リクエストとのマッピングですね。
下記の関数定義部分とデバッグウィンドウを見比べてみてください。Controller に定義されている関数およびそれらに付与されているアノテーションの組み合わせが Key-Value で表現されているのがわかります。
image.png

実用例

デバッグ目的のほかに、実際に使えそうな例を考えてみました。
CORSの設定をイイカンジに行ってくれるFilterを書くのに使えそう…?

  /**
   * [HttpServletRequest.getRequestURI]と一致するエンドポイントを列挙し、そのエンドポイントが求めているHTTPメソッドの情報を
   * "Access-Control-Allow-Methods"ヘッダとして[response]に設定する。
   * 同一のパスを持つがHTTPメソッドごとに関数が分かれているようなケースにおいては、それらの関数が要求するHTTPメソッドすべてを収集し、
   * "Access-Control-Allow-Methods"の設定値とする。
   * また、Preflight requestに対応するため、上記で検出したHTTPメソッド一覧に加えて[RequestMethod.OPTIONS]を無条件で追加する。
   *
   * @param request 受信したリクエスト
   * @param response ヘッダ設定対象のレスポンス
   * @see findMappingInfosByPath
   */
  private fun applyAllowMethods(request: HttpServletRequest, response: HttpServletResponse) {
    val supportRequestMethods = findMappingInfosByPath(request).flatMap { it.methodsCondition.methods }
    val allowMethodsText = (supportRequestMethods + listOf(RequestMethod.OPTIONS))
      .toSet()
      .joinToString(", ") { it.name }
    response.setHeader("Access-Control-Allow-Methods", allowMethodsText)
  }

  /**
   * [HttpServletRequest.getRequestURI]と一致([RequestMappingInfo.getDirectPaths]に含まれるかどうかで判断)するパスを持つ
   * エンドポイントのマッピング情報を検索するシーケンスを作成する。
   *
   * @param request 受信したリクエスト
   * @return 見つかったマッピング情報を返すシーケンス
   */
  private fun findMappingInfosByPath(request: HttpServletRequest): Sequence<RequestMappingInfo> {
    return requestMappingHandlerMapping.handlerMethods.keys.asSequence().filter { info ->
      info.directPaths.contains(request.requestURI)
    }
  }

全体像は下記のGistに置いてあります。
https://gist.github.com/samunohito/6000650cffeaf5a317400df1e76a6d62

参考

1
0
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
1
0