Symfony Advent Calendar 2018 3日目の記事です。
昨日は@77webさんのSymfony4のautowiringで狙ったinterfaceを確実に注入させる方法でした。
はじめに
一人でそこまで大規模ではないアプリケーションを作る事がおおいのですが、Symfonyで簡単なAPIを実装したくなる場合にどう実装したらいいのか?
結構悩むことが多いです。
要件的には以下のような感じです。
- JSONでレスポンスする
- POST時のrequestのbodyに
{"name":"xxx","description":"yyy"}
な形で入ってきたものを受け取る(bodyParser的なものが必要) - ControllerでAnnotationを使いAPIかどうかを指定したい
2つの方法
SymfonyでAPIな実装(jsonをレスポンスするような実装)をする場合に僕が思いつく方法が2つあります。
- JsonResponse()オブジェクトを返す
- FOSRestBundleを使う方法
JsonResponse()オブジェクトを返す
これは非常にシンプルな方法でコントローラで以下のように書くだけです。
<?php
class MainController
{
public function index()
{
return new JsonResponse(['message' => 'hello, world');
}
}
確かにResponseをJSONで返すだけならこの方法で十分です。
しかし、POST時のデータの受取まで考えると、ちょっと微妙ですね。
FOSRestBundleを使う
SymfonyにはFOSRestBundleという素晴らしいBundleがあります。
https://github.com/FriendsOfSymfony/FOSRestBundle
ただ、今回の簡単なAPI実装ではちょっと機能が多すぎるのとAnnotationを活用出来ないのであまり積極的に使う気になれません。
SimpleApiBundle
最初に提示した3つの要件を満たすために作ったBundleです。
一番最初に求めたものは「@API
アノテーションを付けたら、jsonレスポンスする」ということでした。
@API
を外して@Template
に付け替えたら簡単にjsonからhtmlに切り替えられるみたいな。
すべてjsonで返すわけではなくて、一部はTwig使ってHTMLを返したいという要件がよくあって、それを簡単に実現したいのです。
使い方
README.mdを見ていただければすぐに分かると思いますが、単純にjsonのレスポンスを返すだけなら以下のとおりです。
// UserController.php
/**
* @Route("/user/{id}")
* @Api()
*/
public function me($id): array
{
$user = $this->userRepository->find($id);
return [
'id' => $user->getId(),
'name' => $user->getUsername(),
'avatar' => $user->getAvatar(),
];
}
201レスポンス
annotationの引数statusCode
にステータスコードを指定するだけです。
/**
* @Route("/user/post", methods={"POST"})
* @Api(statusCode=201)
*/
public function post(Request $request): array
{
// TODO save logic.
return [
'status' => 'ok',
];
}
ステータスコードの問題点
現状のSimpleApiBundleではannotationで宣言的に書かれているstatusCode
ですが、動的に変化させたいケースもあります。
例えばバリデーションのチェックで失敗した場合は400でレスポンスコード返したいとか、実行時に決まるステータスコードのどう扱うべきか。
現状ではStatusCodeを変更する方法は一つしかなくてExceptionを発生させることです。
Exceptionのcodeが0以外は、ResponseオブジェクトのstatusCodeに反映するように実装しています。
POSTやGETを同じコントローラのアクションに記述するのは、アクションに複数の責務をもたせており、分離したほうがいいわけで、そう考えると、StatusCodeを変更するというのは、基本的にエラーが発生する場合のみであるべきなのではないでしょうか?
ということで現状は宣言的にStatusCodeを設定できるぐらいで十分な気もしてきました。
## 最後に
Bundleを自分で作ると、Symfonyをより深く学ぶ事が出来ます。
SimpleApiBundleはかなりFOSRestBundleを参考にしていますし、僕はFOSRestBundleから多くのことを学びました。
年末年始にお時間のある方は小さなBundle作ってみてはいかがでしょか?