担当している案件で、Ajaxで外部APIを叩いているコードを内部のAPIから叩くようにしたいとの要望がありました。
図に表すと以下のような修正です。
こちらの修正を通して、以下のポイントについてまとめてみました。
- Ajax通信とHTTP通信の違い
- LaravelのHTTP通信の書き方
- LaravelのHTTP通信でできないこと・代替案
Ajax通信とHTTP通信の違い
Ajax通信…「Asynchronous JavaScript + XML」
JavaSciptを使った非同期処理(検索サジェストなど)
HTTP通信 … 同期処理(フォーム送信など)
今回は非同期処理は必須ではなかったため、HTTP通信への書き換えが可能です。
LaravelのHTTP通信の書き方
まずはAjax側の修正です。
PDFファイルを作成するAPIへPOSTリクエストを投げて、レスポンスのファイルIDをDBに登録しています。
こちらはURLの向き先が外部URLから内部へ変わっただけです。
// 修正前 アプリAフロントエンド
await axios.post("${this.apiUrl}/api/v1/make-pdf/pdf", this.parameter).then(response => {
if (response.data.success) {
// ファイル作成結果をDBに格納
console.log(response)
this.jobInsert(response.data.file_id)
}
})
// 修正後 アプリAフロントエンド
await axios.post("/api/createFile", this.parameter).then(response => {
if (response.data.success) {
// ファイル作成結果をDBに格納
console.log(response)
this.jobInsert(response.data.file_id)
}
})
続いて、アプリAのバックエンド側です。
GuzzleHttpを使用して、アプリBのバックエンドにHTTP通信でリクエストを送信します。
// 修正後 アプリAバックエンド
use GuzzleHttp\Client;
public function createFile(Request $request)
{
$client = new GuzzleHttp\Client();
$response = $client->request('POST', 'http://appB/api/v1/make-pdf/pdf', [
['Authorization' => 'Bearer '.$token],'json'=> $request->all()]
]);
return $response->getBody();
}
また、postのパラメータとして’json’=> $request->all()で値を受け渡していますが、
元ネタがajax通信なのでjson形式で渡しています。
もしformをhttp通信に書き換えたいときは、jsonの部分を’form_params’ =>$request
とすることで対応できます。
最後に、アプリBのバックエンド側のコードです。ファイルを作成して、ファイルIDを返却しています。こちらは修正前後でコードの変更はありません。
//修正前、修正後共通 アプリBバックエンド
public function makePdf(Request $request)
{
// (中略)ファイル作成処理
$result = ["file_id" => $file_id]
return new JsonResponse($result);
}
以上で、ajaxからHTTP通信への書き換えが完了しました。
LaravelのHTTP通信でできないこと・代替案
前述のようにresponseが単純なjson型であれば特に問題なかったのですが、
書き換えがうまくいかないパターンがありました。
LaravelのstreamDownload()など、PHPストリームを使用するパターンです。
// アプリBのバックエンド側の処理
return response()->streamDownload(function () {
echo GitHub::api('repo')
->contents()
->readme('laravel', 'laravel')['contents'];
}, 'laravel-readme.md');
こちらをguzzleHttpで置き換えてみたところ、アプリAのフロントエンドで受け取ったresponseが文字化けしてしまっていて
正常なダウンロードファイルを作成できませんでした。
原因は、guzzleHttpがHTTPメッセージのインターフェースとしてPSR-7を使用しているのですが
PSR-7がPHPストリームの変換に対応していないからのようです。
要求および応答インターフェースとは異なり、StreamInterface不変性をモデル化しません。
実際のPHPストリームがラップされている状況では、リソースと対話するコードがその状態(カーソル位置、コンテンツなどを含む)を変更する可能性があるため、不変性を強制することは不可能です。
実装では、サーバー側の要求とクライアント側の応答に読み取り専用ストリームを使用することをお勧めします。
なので、今回はダウンロードURLをAWSのCroudFrontで発行し
そちらのURLを返却、リダイレクトすることで対応しました。
(正直streamの概念だったり、HTTP通信時のメッセージの変換規則?などよくわかりませんでした。。)
ただ、外部APIを多様するアプリではこのように呼び出しを内部APIに集約したいという要望はあるかと思うので、
外部APIの戻り値の設計などには気を配っていきたいと思います。