【Laravel】HTTP通信を使った外部APIの呼び出し方

   担当している案件で、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ストリームの変換に対応していないからのようです。
 https://www.php-fig.org/psr/psr-7/

    要求および応答インターフェースとは異なり、StreamInterface不変性をモデル化しません。
    実際のPHPストリームがラップされている状況では、リソースと対話するコードがその状態(カーソル位置、コンテンツなどを含む)を変更する可能性があるため、不変性を強制することは不可能です。
    実装では、サーバー側の要求とクライアント側の応答に読み取り専用ストリームを使用することをお勧めします。
なので、今回はダウンロードURLをAWSのCroudFrontで発行し
そちらのURLを返却、リダイレクトすることで対応しました。
 
(正直streamの概念だったり、HTTP通信時のメッセージの変換規則?などよくわかりませんでした。。)

ただ、外部APIを多様するアプリではこのように呼び出しを内部APIに集約したいという要望はあるかと思うので、
外部APIの戻り値の設計などには気を配っていきたいと思います。

前へ

Googleに学ぶコードレビューのポイント

次へ

A5:SQL Mk-2(A5M2)よく使う・たまに使う・そのうち使う便利機能