Amazon API GatewayでサクッとCORS対応する

# はじめに
Amazon API GatewayとS3の静的Webサイトホスティングを組み合わせると、サーバレスで何でもできるようになるのだけど、当然のことながらオリジンが変わってしまうので、CORSの設定が必要。
マネコンからは一発でCORSの設定を入れることができるものの、IaCと組み合わせてると、マネコンからの変更は色々と不都合があるので、ここはTerraform一撃でCORSの設定を入れられるようにしてしまおう。

CORSって何？という人は[クラメソ先生](https://dev.classmethod.jp/articles/about-cors/)に教えてもらおう！

# 簡単なリクエスト編(GETメソッド)
GETについては、レスポンスのheadersでCORSを許容することを示せば良い。

具体的には、```Access-Control-Allow-Headers```、```Access-Control-Allow-Origin```、```Access-Control-Allow-Methods```を返す。

以下はPython＋API Gatewayの統合レスポンスで返す場合の例。

```python
    return {
        'isBase64Encoded': False,
        'statusCode': status_code,
        'headers': {
            "Access-Control-Allow-Headers" : "*",
            "Access-Control-Allow-Origin": "[許容するオリジン]",
            "Access-Control-Allow-Methods": "GET"
        },
        'body': json.dumps(body)
    }
```

# プリフライトリクエスト編(OPTIONメソッド)
プリフライトリクエストについては、Lambdaで実装しても良いが、いちいちLambdaなんて作っていられないので、API Gatewayの統合でMockを使うことにする。

まずは、API GatewayのRESTAPIと、リソースを作成し、

```HCL
resource "aws_api_gateway_rest_api" "test" {
  name        = local.api_gateway_name
  description = "テスト用API Gateway"
}

resource "aws_api_gateway_resource" "test" {
  rest_api_id = aws_api_gateway_rest_api.test.id
  parent_id   = aws_api_gateway_rest_api.test.root_resource_id
  path_part   = "test"
}
```

OPTIONメソッドを定義する。

```HCL
resource "aws_api_gateway_method" "test_options" {
  rest_api_id   = aws_api_gateway_rest_api.test.id
  resource_id   = aws_api_gateway_resource.test.id
  http_method   = "OPTIONS"
  authorization = "NONE"
}
```

testのOPTIONメソッドに対する統合レスポンスを作りつつ、メソッドレスポンスでCORSのヘッダを透過するよう設定し、

```HCL
resource "aws_api_gateway_method_response" "test_options_200" {
  rest_api_id = aws_api_gateway_rest_api.test.id
  resource_id = aws_api_gateway_resource.test.id
  http_method = aws_api_gateway_method.test_options.http_method
  status_code = "200"

  response_models = {
    "application/json" = "Empty"
  }

  response_parameters = {
    "method.response.header.Access-Control-Allow-Headers" = true,
    "method.response.header.Access-Control-Allow-Methods" = true,
    "method.response.header.Access-Control-Allow-Origin"  = true
  }
}

resource "aws_api_gateway_integration_response" "test_options" {
  rest_api_id = aws_api_gateway_rest_api.test.id
  resource_id = aws_api_gateway_resource.test.id
  http_method = aws_api_gateway_method.test_options.http_method
  status_code = aws_api_gateway_method_response.test_options_200.status_code

  response_parameters = {
    "method.response.header.Access-Control-Allow-Headers" = "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
    "method.response.header.Access-Control-Allow-Methods" = "'GET,OPTIONS,POST,PUT'",
    "method.response.header.Access-Control-Allow-Origin"  = "'[許容するオリジン]'"
  }
}
```

testパスのOPTIONSメソッドに対して200応答をするモックを作成する。

```HCL
resource "aws_api_gateway_integration" "test_options_mock" {
  rest_api_id = aws_api_gateway_rest_api.test.id
  resource_id = aws_api_gateway_resource.test.id
  http_method = aws_api_gateway_method.test_options.http_method
  type        = "MOCK"

  request_templates = {
    "application/json" = <<EOF
{
  "statusCode": 200
}
EOF
  }
}
```

これでOPTIONリクエストに良い感じにCORS許容のレスポンスをしてくれるモックが作れた！
あとは好きなようにPOSTなりPUTのメソッドでCORSな更新系リクエストを作れるぞ！