【axios+SAM+API Gateway】localhostからapiを叩けるようになるために苦労した話 （2/3）ローカルから先程作成した`API Gateway`の`GET`メソッドを叩く

# はじめに
本稿は下記流れに沿って、axios+SAM+API Gatewayを利用して、`GET`及び`POST`メソッドのAPIを叩けるところまでを目標としています。

1. `SAM`を利用して、`API Gateway`及び`lambda`を構築
1. ローカルから先程作成した`API Gateway`の`GET`メソッドを叩く
1. ローカルから先程作成した`API Gateway`の`POST`メソッドを叩く


[前回](https://qiita.com/soana/items/611dceeeed5521b6d896)は`SAM`を利用して、`API Gateway`及び`lambda`を構築し、作成したAPIに対して`curl`コマンドを利用し叩けるところまでを検証しました。
今回は、「ローカルから先程作成した`API Gateway`の`GET`メソッドを叩く」ということを試していきたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586524/6b17e317-472c-b982-2548-a4b9ad2c0332.png)

# 2. ローカルから先程作成した`API Gateway`の`GET`メソッドを叩く
localhostを起動し、先程のURLを叩いてみます。
デモ用のソースは以下としました(実処理部分のみ)。

```
import React, {Component} from 'react';
// import ReactDOM from 'react-dom';
import axios from 'axios';

class App extends Component {
    constructor(props){
        super(props);
        this.state = {
            'appMessage': 'hello,app'
        };
        this.getApi = this.getApi.bind(this);
    }

    instance = axios.create({
        baseURL: 'https://{メソッドのURL}//Prod'
    });
    
    getApi(){
        this.instance.get('/hello')
            .then((response) => {
                this.setState({
                    'appMessage': response.data['message']
                })
            })
            .catch(() => {
                this.setState({
                    'appMessage': 'faild get message from button'
                })                
            })
    }

    render(){
        return(
            <div>
                <div>{this.state.appMessage}</div>
                <input type='button' onClick={this.getApi} value="button" />
            </div>
        )
    }
}

export default App;
```

これを実行したところ以下のようなエラーが出ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586524/d7131b5b-b7db-4c3b-6005-df166fef63ad.png)

```
Access to XMLHttpRequest at 'https://{メソッドのURL}//Prod/hello' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
```
どうやらCORSに関する実装がちゃんとされてないことが原因で怒られたようです（参考にしたAWS公式ドキュメントは[こちら](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/）how-to-cors.html)）。
なのでresponseに以下のheaderを含むようにします。

```
// app.py
    return {
        "statusCode": 200,
        'headers': {
            'Access-Control-Allow-Headers': 'Content-Type',
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
        },        
        "body": json.dumps({
            "message": "hello world",
            # "location": ip.text.replace("\n", "")
        }),
    }
```
これでデプロイ後再度実行すると無事apiが叩けました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586524/1745cc84-1552-fd2d-6b43-aa656576151c.png)

試しにresponse部分を確認すると、先程追加したheaderが含まれているのを確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586524/5c48641c-5275-7db7-2813-e93885db48c6.png)

## 原因部分をもう少し詳しく
（※こちらの記事が大変参考になりました！[なんとなく CORS がわかる...はもう終わりにする。](https://qiita.com/att55/items/2154a8aad8bf1409db2b)）

先述したとおり、今回の原因はCORSに関する実装がちゃんとされてなかったことです。
CORSとは、「Cross-Origin Resource Sharing」の略で、別のオリジン間でリソースを共有するためには、ちゃんと許可してあげようねというものだそうです。今回私は、

* 叩き元：localhost
* 叩き先：API Gateway

としたので、これらは当然ドメインが異なります。なのでAPIからのresponseにリソースの共有を許可する`'Access-Control-Allow-Origin': '*'`をheaderに追加する必要があったということですね。

# 終わりに
今回は自分で作成したAPIに対し、アプリを介して叩いてみるということを検証しました。これまでCORSは言葉を知っている程度だったので今回みたいなことは考えたことがなかったです。そのため、「なんで`curl`で叩けたのに、アプリ経由だと叩けないの…？」とだいぶハマりました。
今回の経験を経て、「じゃあ`POST`も同様のことを気をつける必要があるんだな！」と気をつけてたら、また別のところでハマりました…
それは次回で説明できればと思います。