Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Laravel で API バージョニングを実装するなら

914 views

Published on

2018-09-26 開催の「第130回 PHP勉強会@東京」におけるLT資料です

https://phpstudy.doorkeeper.jp/events/79982

Published in: Software
  • Be the first to comment

Laravel で API バージョニングを実装するなら

  1. 1. API バージョニング を Laravel で実装するなら 第130回 PHP勉強会@東京
  2. 2. 岡田 正平(おかだ しょうへい)@okashoi • 株式会社ウィルゲート 2015年新卒入社 • 開発室 ソリューションユニット 所属 • PHP, Laravel, Vue.js 2 自己紹介 Slides:
  3. 3. クライアントに対し、古い形式でのアクセス方法を提供しつづける 例)Google Maps Embed API https://www.google.com/maps/embed/v1/place?q=...&key=... 新しいアクセス方法・レスポンス形式などは v2, v3… としていく ※他にもメディアタイプでバージョンを指定する方法などがある 3 API バージョニング
  4. 4. クライアントに対し、古い形式でのアクセス方法を提供しつづける 例)Google Maps Embed API https://www.google.com/maps/embed/v1/place?q=...&key=... 新しいアクセス方法・レスポンス形式などは v2, v3… としていく ※他にもメディアタイプでバージョンを指定する方法などがある 4 API バージョニング このスライドではいったん この方法で考えます
  5. 5. 愚直に考えるとこう routes/api.php 5 Laravel でどう実装する?① Route::get('v1/posts', 'V1/PostController@index'); Route::get('v2/posts', 'V2/PostController@index');
  6. 6. 愚直に考えるとこう routes/api.php 6 Laravel でどう実装する?① Route::get('v1/posts', 'V1/PostController@index'); Route::get('v2/posts', 'V2/PostController@index'); バージョンごとに 異なる Controller 明示的に指定
  7. 7. たしかに実現できてるけど…… • 新しいバージョンが増えるたびに、全てを追記するの? ➢ バージョンを扱う部分は仕組み化したい • コントローラを分割してしまうとコピペが横行するおそれアリ ➢ コントローラ内の条件分岐にしたい 7 Laravel でどう実装する?①
  8. 8. 8 Laravel でどう実装する?① 参考)http://kenn.hatenablog.com/entry/2014/03/06/105249
  9. 9. 改善案:ルーティングパラメータとして渡す 9 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php
  10. 10. 改善案:ルーティングパラメータとして渡す 10 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php
  11. 11. 改善案:ルーティングパラメータとして渡す 11 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php パターン指定による制限
  12. 12. 改善案:ルーティングパラメータとして渡す 12 Laravel でどう実装する?② Route::get('{version}/posts', 'PostController@index') ->where('version', 'v[1-9][0-9]*'); routes/api.php public function index(string $versionString, Request $request) { // 'v1' => 1 のように int 値に変換 $version = (int)substr($versionString, 1); if ($version === 1) { // v1 の処理 } // ... 以下略 } apps/MyApp/Application/Http/Controllers/PostConroller.php
  13. 13. ①の問題点は解決できたものの、欲を言うなら • 毎 action で ←これやるの? ➢ 勝手に変換されていて欲しい • による制限が に対してできない ➢ パターン指定は一箇所だけにしたい 13 Laravel でどう実装する?② $version = (int)substr($versionString, 1); ->where() Route::group() // できそうでできない Route::group(['prefix' => '{version}'], function () { Route::get('posts', 'PostController@index'); Route::get('users', 'UserController@index'); })->where('version', 'v[1-9][0-9]*');
  14. 14. アイディア:Explicit Route Model Binding を使う 15 Laravel でどう実装する?③ カスタマイズロジックを用いれば Eloquent ORM 以外も Bind できる
  15. 15. 1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに pattern も)登録 3. Controller で利用する 16 Laravel でどう実装する?③
  16. 16. 1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに pattern も)登録 3. Controller で利用する 17 Laravel でどう実装する?③ class ApiVersion { /** * @var int */ protected $value; /** * @param string $versionString */ public function __construct(string $versionString) { $this->value = (int)substr($versionString, 1); } /** * @return int */ public function value(): int { return $this->value; } /** * @param int $number * @return bool */ public function is(int $number): bool { return $this->value === $number; } }
  17. 17. 1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに pattern も)登録 3. Controller で利用する 18 Laravel でどう実装する?③ public function boot(): void { Route::pattern('version', 'v[1-9][0-9]*'); parent::boot(); Route::bind('version', function ($versionString, $route) { return new ApiVersionValue($versionString); }); } tips: は より前 は より後 でないと正しく動かない Route::pattern() parent::boot() Route::bind() parent::boot()
  18. 18. 1. API バージョンを表現するクラス (Value Object)を作る 2. RouteServiceProvider で Binding を(ついでに pattern も)登録 3. Controller で利用する 19 Laravel でどう実装する?③ Route::get('{version}/posts', 'PostController@index'); public function index(ApiVersion $version, R... { // 特定のバージョンかどうか調べるには // $version->is() を使用 if ($version->is(1)) { // v1 の処理 } // バージョンの値を得たければ // $version->value() を使用 if ($version->value() <= 3) { // v2, v3 の処理 } // 以下略 } ↓
  19. 19. • Explicit Route Model Binding は Model 以外にも使い道がある! • API バージョンを Value Object にするは良い工夫だと思う(自画自賛) • Application Layer の Value Object になる(特殊な例?) • (余談)そもそも API バージョニングって難しい 20 所感など
  20. 20. • 本当にその API にバージョンニングいる? • SSKDs (Small Set of Known Developers) 向けならいらない? • c.f.) LSUDs (Large Set of Unknown Developers) • 後から足すことになるくらいなら、当面 v1 であってもひとまず…… • どのタイミングでバージョンを変える? • 後方互換が保てなくなったら?時間(1 年ごとなど)で区切る? • エンドポイント単位で更新?API 全体で更新? • バージョン指定なしを許容するか?許容する場合の挙動はどうするか? 21 余談:そもそも API バージョニング難しい問題 などなど……

×