Spring Cloud Gatewayで作るTakanawa Gateway

# 概要

Spring Cloud Gatewayの紹介をしようと思って書いてたら高輪ゲートウェイ開通の記事が目に入って勢いでタイトルを決めました。

品川ー田町間に新しく出来たTakanawa GatewayをSpring Cloud GatewayのRoute Predicate Factoriesを使って実装していきます。

# Spring Cloud Gatewayとは

https://spring.io/projects/spring-cloud-gateway

Spring Projectが提供しているAPI Gatewayになります。application.yamlを設定するだけでわりと自由なルーティングが実現できたり、通過するリクエストのヘッダーをカスタマイズしたり、サーキットブレーカーを利用できたりと色々な機能が提供できます。

# プロジェクトの作成

[Spring Initializr](https://start.spring.io/)から作ります。
Dependenciesに`Gateway`を入れる以外はお好きな設定をお選びください。
<img width="903" alt="スクリーンショット 2020-03-18 22.10.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62495/380a2809-7009-7251-b662-75b253c5b637.png">

# Routingの実装

プロジェクトを作成してダウンロードしたら、`src/main/resources/application.yaml`ファイルを作成し、Takanawa Gatewayにふさわしいルーティング設定を入れていきます。

### 田町駅へルーティングする

まずは田町へのルートを確保します。Spring Cloud Gatewayでは`predicates`という項目にルーティングする条件を書いていきます。条件なしは作れないので、ここでは**Method Route Predicate Factor**を使いGETメソッドを対象として田町へルーティングします。

```yaml:application.yaml
spring:
  cloud:
    gateway:
      routes:
        - id: tamachi
          uri: http://tamachi:8081
          predicates:
          - Method=GET
```

`id`と`uri`はご自身の環境に合わせたものをお選びください。この例では盛り上げるためにlocalhostのHostsを各種書き換えています。

この状態でアプリケーションを起動し、呼び出してあげます。

```bash
$ curl http://takanawa-gateway:8080/
Tamachi Station.⏎
```

これで田町駅と繋がることができました。

### 駅が開業する2020/3/14以降しか通さない

高輪ゲートウェイが開業したのは2020/03/14です。なので、それより前の時間帯で駅が使えてはいけません。
**After Route Predicate Factory**を使って制限をかけます。

```yaml:application.yaml
spring:
  cloud:
    gateway:
      routes:
        - id: tamachi
          uri: http://tamachi:8081
          predicates:
          - Method=GET
          - After=2020-03-14T00:00:00.000+09:00[Asia/Tokyo]
```

これで、3/14以前のリクエストは弾くことができるようになります。この記事を書いている時間ではすでに高輪ゲートウェイが開業してしまっているので、システム日付をいじって3/13にしてからTakanawaGatewayを呼び出してみます。

```bash
$ curl http://takanawa-gateway:8080/
{"timestamp":"2020-03-13T13:31:36.812+0000","path":"/","status":404,"error":"Not Found","message":null,"requestId":"05652ab6-1"}⏎
```

日付を戻して3/14以降にすると通過することが出来ます。

```bash
$ date;curl http://takanawa-gateway:8080/
2020年 3月18日 水曜日 22時33分01秒 JST
Tamachi Station.⏎
```

### JR山手線しか通さないようにする

高輪ゲートウェイは山手線の駅なので、山手線以外の路線から使えてはいけません。
そこで、`JR-Yamanote-Line`というHTTPヘッダーが含まれていなければTakanawa Gatewayを通過させないようにします。
HTTPヘッダーによる制御は**Header Route Predicate Factory**にて実現できます。

```yaml:application.yaml
spring:
  cloud:
    gateway:
      routes:
        - id: tamachi
          uri: http://tamachi:8081
          predicates:
          - Method=GET
          - After=2020-03-14T00:00:00.000+09:00[Asia/Tokyo]
          - Header=JR-Yamanote-Line
```

これにより、山手線占用のヘッダーがない場合アクセスは弾かれてしまいます。

```bash
# JR-Yamanote-Lineヘッダーが無い
$ curl http://takanawa-gateway:8080/
{"timestamp":"2020-03-18T13:37:22.660+0000","path":"/","status":404,"error":"Not Found","message":null,"requestId":"83ea59d6-1"}⏎

# JR-Yamanote-Lineヘッダーがある(値問わず)
$ curl -H "JR-Yamanote-Line:true" http://takanawa-gateway:8080/
Tamachi Station.⏎
```

### 品川駅にも行けるようにする

山手線に問わず、終点以外のほとんどの駅には上りと下りがあります。今は田町にしか行けない片道状態ですが、当然品川にも行けるようにしてあげないといけません。
そこで、`Host`ヘッダーを見てルーティング先を品川か田町か決めてあげます。
ここでは**Host Route Predicate Factory**を利用します。

```yaml:application.yaml
spring:
  cloud:
    gateway:
      routes:
        - id: tamachi
          uri: http://tamachi:8081
          predicates:
          - Method=GET
          - After=2020-03-14T00:00:00.000+09:00[Asia/Tokyo]
          - Header=JR-Yamanote-Line
          - Host=tamachi
        - id: shinagawa
          uri: http://shinagawa:8082
          predicates:
            - Method=GET
            - After=2020-03-14T00:00:00.000+09:00[Asia/Tokyo]
            - Header=JR-Yamanote-Line
            - Host=shinagawa,**
```

これで品川駅にも行けるようになりました！今回はワイルドカードも入れているので、田町以外のHostヘッダーの場合は行き先がすべて品川になります。

```bash
$ curl -H "JR-Yamanote-Line:true" -H "Host:tamachi" http://takanawa-gateway:8080/
Tamachi Station.⏎                                                                                                                                                                    $ curl -H "JR-Yamanote-Line:true" -H "Host:shinagawa" http://takanawa-gateway:8080/
Shinagawa Station.⏎                                                                                                                                                                  $ curl -H "JR-Yamanote-Line:true"  http://takanawa-gateway:8080/
Shinagawa Station.⏎
```


# まとめ

Spring Cloud GatwayのRoute Predicate Factoriesを使った簡単なルーティング例を上げてみました。紹介した以外にも様々な機能を持っており、自前の認証ヘッダーを挟むためにGlobal Filterを実装してあげるなど、独自の実装を求められる場面などでも活躍します。興味を持った人は是非使ってみてください。


**そして、高輪ゲートウェイ開業おめでとうございます！！**


# リファレンス

https://cloud.spring.io/spring-cloud-gateway/reference/html/