【API Gateway】バイナリペイロードを適切に扱う

# はじめに
API Gatewayで扱うリクエスト、レスポンスに含まれるペイロードはテキストペイロードとバイナリペイロードに区分される。
| ペイロード | 説明 |
|:-:|:-:|
|テキスト|UTF-8エンコードされた JSON 文字列|
|バイナリ|テキストペイロード以外のペイロード|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/7482c394-14ef-0951-549b-b77d379f76b4.png)
バイナリペイロードを扱うためには、Lambdaプロキシ統合の利用有無によって適切にAPI Gatewayを構成する必要がある。

# Lambdaプロキシを利用する場合
必要な設定は以下の通りです。
- レスポンスのbase64エンコード
    Lambdaプロキシの利用時にバイナリペイロードを処理するためには、Lambdaからのレスポンスをbase64でエンコードする必要がある。
- binaryMediaTypes
    binaryMediaTypesbinaryを設定し、APIがバイナリデータとして扱うコンテンツタイプを定義する必要がある。

    |バイナリメディアタイプ例|説明|
    |:-:|:-:|
    |image/jpeg|JPEG画像|
    |image/png|PNG画像|
    |application/pdf|PDFドキュメント|
    |audio/mpeg|MP3音声ファイル|
    |video/mp4|MP4動画ファイル|
    |text/csv|CSV|
    |application/msword|Word|
    |application/gzip|GZip|
    |application/json|JSON|
    |application/vnd.ms-powerpoint|PowerPoint|
    |application/vnd.openxmlformats-officedocument.spreadsheetml.sheet|Excel（.xlsx）|
    |application/vnd.ms-excel|Excel（.xls）|
    |application/zip|ZIP|
    |application/octet-stream|任意のバイナリデータ|
    |\*/\*|すべてのコンテンツタイプ|

### S3のデータを取得する例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/ba6d051a-2a2a-f47c-d636-285cbb34803a.png)

- IAMロールを以下の通り作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/ccb46192-13e6-9e36-e846-75c085bcbe41.png)

- LambdaをNode.jsで作成
IAMロールは先程作成したものを割り当てる。
Excelを返却する際のコードは以下の通り。
```
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"

const client = new S3Client({region: 'ap-northeast-1'});

export const handler = async (event) => {
  const bucket = event.path.split('/')[2]
  const key = event.path.split('/')[3]
  const input = {
    "Bucket" : bucket,
    "Key" : key
    }
  try {
    const command = new GetObjectCommand(input)
    const response = await client.send(command);
    var str = await response.Body.transformToByteArray();
  } catch (err) {
    console.error(err);
  }
  const base64body = Buffer.from(str).toString('base64');
  return {
    'headers': { "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
    'statusCode': 200,
    'body': base64body,
    'isBase64Encoded': true
    }
}
```

- API Gatewayを作成
    - リソースを以下の通り作成
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/3feb32d9-655c-d5b6-6c29-71c634a9ae1f.png)
    - {item}リクエストにGETメソッドを以下の通り作成
    作成したLambdaのARNを指定する
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/ed02f8c9-4847-e778-1731-b38c7aba1411.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/f6304e8c-748d-0b75-4126-ae4547dabbe3.png)

    - バイナリサポートの有効化
        - APIの設定
            - 「メディアタイプを管理」を押下
                ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/16aab72e-9ce0-d108-f47f-6b6acd270897.png)
            - ダウンロード対象のデータに対応するバイナリメディアタイプを指定して保存。
                エクセル（.exls）を扱う場合は以下の通りに指定。
                ```
                application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
                ```
                ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/3bdb71cc-7d05-4c4d-376b-0e18c38bd761.png)

- 動作確認
    POSTMANにてAPIへのGETリクエストを行い、S3のファイルをダウンロードできることを確認する。
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/4cc4b18e-5ad8-9ebf-60b1-01d2b4ebe702.png)

# Lambdaプロキシを利用しない場合
- RestApiリソースのbinaryMediaTypesリストにメディアタイプを追加
- IntegrationおよびIntegrationResponseリソースのcontentHandlingプロパティを設定することもでる
    - contentHandlingの値は、CONVERT_TO_BINARY、CONVERT_TO_TEXT、またはundefinedを指定する
    - contentHandlingの値と、レスポンスのContent-Typeヘッダーまたは受信リクエストのAcceptヘッダーがbinaryMediaTypesリストのエントリと一致するかどうかに応じて、API Gatewayは生のバイナリをbase64エンコード文字列としてエンコードしたり、base64エンコード文字列を生のバイトにデコードしたり、本文を変更せずに渡したりすことができる

### S3のデータを取得する例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/c0f87501-9a0e-eae8-5af7-3067fa49788b.png)
#### 
- メソッド実行に用いるIAMロールを以下の通り作成
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/7adbe481-95c6-5950-a12b-629831ec1890.png)
- API Gateway の作成
    - リソースを以下の通り作成
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/3feb32d9-655c-d5b6-6c29-71c634a9ae1f.png)

    - {item}リクエストにGETメソッドを以下の通り作成
        実行ロールは先程作成したIAMロールを指定する
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/276299c0-760d-deb2-015c-a11eb7fd535a.png)
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/f6304e8c-748d-0b75-4126-ae4547dabbe3.png)

    - バイナリサポートの有効化
        - APIの設定
            - 「メディアタイプを管理」を押下
            ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/16aab72e-9ce0-d108-f47f-6b6acd270897.png)
            - ダウンロード対象のデータに対応するバイナリメディアタイプを指定して保存。
                エクセル（.exls）を扱う場合は以下の通りに指定。
                ```
                application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
                ```
                ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/3bdb71cc-7d05-4c4d-376b-0e18c38bd761.png)
    
        - メソッドリクエスト
            HTTPリクエストヘッダーにAcceptを追加
            ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/c9f6e7cd-73c9-71c3-ca11-b8cf52c57517.png)
            
            :::note info
            Acceptヘッダーでクライアントがサーバーに対して、どのメディアタイプを受け入れることができるかを指定する
        - 統合リクエスト
            統合レスポンスのHTTPヘッダーにメソッドリクエストのAcceptヘッダをマッピング
            ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/8c92f849-79bc-399d-58eb-7cdefa3c75c0.png)
    
        - 統合レスポンス
            メソッドレスポンスのHTTPヘッダーに統合レスポンスのContent-Typeヘッダをマッピング
            ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/463ee97a-a39f-34cb-e605-c0cb9849d36b.png)
            
            :::note info
            Content-Typeヘッダーでレスポンスに含まれるデータのメディアタイプを指定する
        - メソッドレスポンス
            レスポンスヘッダーにContent-Typeヘッダーを追加
            ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/9b42d5f5-26e0-a7c1-5d47-63bd36ed1c1f.png)

- 動作確認
    POSTMANにてAPIへのGETリクエストを行い、S3のファイルをダウンロードできることを確認する。
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/74a38fbf-2b97-37e7-de2f-269929d1b9d8.png)
    
    :::note warn
    Acceptヘッダにて、エクセルのメディアタイプ「application/vnd.openxmlformats-officedocument.spreadsheetml.sheet」を指定しないとローカルでダウンロードされたファイルを正常に開けないので注意