1. 序章

Spring Bootアプリケーションでは、すべてのコントローラーが独自のURLマッピングを持つことができます。 これにより、1つのアプリケーションで複数の場所にWebエンドポイントを簡単に提供できます。 たとえば、APIエンドポイントを内部や外部などの論理グループにグループ化できます。

ただし、すべてのエンドポイントを共通のプレフィックスの下に配置したい場合があります。このチュートリアルでは、すべてのSpring Bootコントローラーに共通のプレフィックスを使用するさまざまな方法を見ていきます。

2. サーブレットコンテキスト

SpringアプリケーションでWeb要求を処理する主なコンポーネントは、DispatcherServletです。 このコンポーネントをカスタマイズすることで、リクエストのルーティング方法をかなり制御できます。

DispatcherServlet をカスタマイズして、すべてのアプリケーションエンドポイントを共通のURLプレフィックスで使用できるようにする2つの異なる方法を見てみましょう。

2.1. 春の豆

最初の方法は、新しいSpringBeanを導入することです。

@Configuration
public class DispatcherServletCustomConfiguration {

    @Bean
    public DispatcherServlet dispatcherServlet() {
        return new DispatcherServlet();
    }

    @Bean
    public ServletRegistrationBean dispatcherServletRegistration() {
        ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/api/");
        registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
        return registration;
    }
}

ここでは、DispatcherServletBeanをラップするServletRegistrationBeanを作成しています。 / api/の明示的なベースURLを提供していることに注意してください。 これは、すべてのエンドポイントがそのベースURLプレフィックスでアクセスされる必要があることを意味します。

2.2. アプリケーションのプロパティ

アプリケーションのプロパティを使用するだけでも同じ結果を得ることができます。 2.0.0以降のバージョンのSpringBootでは、application.propertiesファイルに次を追加します。

server.servlet.contextPath=/api

そのバージョンより前では、プロパティ名は少し異なります。

server.contextPath=/api

このアプローチの利点の1つは、通常のSpringプロパティのみを使用することです。 これは、プロファイルや外部プロパティバインディングなどの標準メカニズムを使用して、共通のプレフィックスを簡単に変更またはオーバーライドできることを意味します。

2.3. 長所と短所

これら2つのアプローチの主な利点は、主な欠点でもあります。これらは、アプリケーションのすべてのエンドポイントに影響します。

一部のアプリケーションでは、これでまったく問題ない場合があります。 ただし、一部のアプリケーションでは、OAuth交換など、サードパーティのサービスとやり取りするために標準のエンドポイントマッピングを使用する必要がある場合があります。 このような場合、このようなグローバルソリューションは適切ではない可能性があります。

3. 注釈

Springアプリケーションのすべてのコントローラーにプレフィックスを追加するもう1つの方法は、アノテーションを使用することです。 以下では、2つの異なるアプローチを見ていきます。

3.1. SpEL

最初の方法は、 Spring Expression Language (SpEL)を標準の@RequestMappingアノテーションとともに使用することです。 このアプローチでは、プレフィックスを付ける各コントローラーにプロパティを追加するだけです。

@Controller
@RequestMapping(path = "${apiPrefix}/users")
public class UserController {

} 

次に、application.propertiesでプロパティ値を指定するだけです。

apiPrefix=/api

3.2. カスタム注釈

これを実現する別の方法は、独自の注釈を作成することです。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@RequestMapping("/api/")
public @interface ApiPrefixController {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

次に、プレフィックスを付ける各コントローラーに注釈を適用するだけで済みます。

@Controller
@ApiPrefixController
public class SomeController {
    @RequestMapping("/users")
    @ReponseBody
    public String getAll(){
        // ...
    }
}

3.3. 長所と短所

これらの2つのアプローチは、前の方法の主な懸念事項に対処します。どちらも、どのコントローラーがプレフィックスを取得するかをきめ細かく制御します。 アプリケーション内のすべてのエンドポイントに影響を与えるのではなく、特定のコントローラーにのみ注釈を適用できます。

4. サーバーサイドフォワード

最後に、サーバー側の転送を使用します。 リダイレクトとは異なり、転送にはクライアントへの応答は含まれません。 これは、アプリケーションがクライアントに影響を与えることなくエンドポイント間でリクエストを渡すことができることを意味します。

開始するには、2つのエンドポイントを持つ単純なコントローラーを作成しましょう。

@Controller
class EndpointController {
    @GetMapping("/endpoint1")
    @ResponseBody
    public String endpoint1() {
        return "Hello from endpoint 1";
    }

    @GetMapping("/endpoint2")
    @ResponseBody
    public String endpoint2() {
        return "Hello from endpoint 2";
    }
}

次に、必要なプレフィックスに基づいた新しいコントローラーを作成します。

@Controller
@RequestMapping("/api/endpoint")
public class ApiPrefixController {

    @GetMapping
    public ModelAndView route(ModelMap model) {
        if(new Random().nextBoolean()) {
            return new ModelAndView("forward:/endpoint1", model);
        } 
        else {
            return new ModelAndView("forward:/endpoint2", model);
        }
    }
}

このコントローラーには、ルーターとして機能する単一のエンドポイントがあります。この場合、基本的にコインを裏返して、元のリクエストを他の2つのエンドポイントの1つに転送します。

いくつかの連続したリクエストを送信することで、機能していることを確認できます。

> curl http://localhost:8080/api/endpoint
Hello from endpoint 2
> curl http://localhost:8080/api/endpoint
Hello from endpoint 1
> curl http://localhost:8080/api/endpoint
Hello from endpoint 1
> curl http://localhost:8080/api/endpoint
Hello from endpoint 2
> curl http://localhost:8080/api/endpoint
Hello from endpoint 2

このアプローチの主な利点は、非常に強力なことです。 リクエストの転送方法を決定するロジック(URLパス、HTTPメソッド、HTTPヘッダーなど)を適用できます。

5. 結論

この記事では、Springアプリケーションのすべてのコントローラーに共通のプレフィックスを適用するいくつかの方法を学びました。 ほとんどの決定と同様に、各アプローチには、実装前に慎重に検討する必要がある長所と短所があります。

いつものように、このチュートリアルのコード例は、GitHubにあります。