Developersをフォローする

【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ストリームの変換に対応していないからのようです。

PSR-7: HTTP message interfaces - PHP-FIG
We're a group of established PHP projects whose goal is to talk about commonalities between our projects and find ways w...

    要求および応答インターフェースとは異なり、StreamInterface不変性をモデル化しません。

    実際のPHPストリームがラップされている状況では、リソースと対話するコードがその状態(カーソル位置、コンテンツなどを含む)を変更する可能性があるため、不変性を強制することは不可能です。

    実装では、サーバー側の要求とクライアント側の応答に読み取り専用ストリームを使用することをお勧めします。

なので、今回はダウンロードURLをAWSのCroudFrontで発行し

そちらのURLを返却、リダイレクトすることで対応しました。

(正直streamの概念だったり、HTTP通信時のメッセージの変換規則?などよくわかりませんでした。。)

ただ、外部APIを多様するアプリではこのように呼び出しを内部APIに集約したいという要望はあるかと思うので、

外部APIの戻り値の設計などには気を配っていきたいと思います。