gorilla/muxのCORS対応

はじめてgolangでgorilla/muxを使用したAPIを作ったのですが、CORSで諸々ハマったので記事としてまとめます。

## ことの始まり
以下のような実装のAPIを作りました。

```golang:main.go
package main

import (
	"net/http"
	"github.com/gorilla/mux"
)

func main() {
	r := mux.NewRouter()
	getTestestHandler := NewGetTestHandler()
	r.HandleFunc("/test", getTestestHandler.Get).Methods("GET")
	http.ListenAndServe(":8080", r)
}
```

```golang:get_test_handler.go
package main

import (
	"encoding/json"
	"net/http"
)

type GetTestHandler struct{}

type TestResponse struct {
	Test string
}

func NewGetTestHandler() *GetTestHandler {
	return &GetTestHandler{}
}

func (h *GetTestHandler) Get(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(TestResponse{Test: "test"})
}
```

動作確認のために`html.html`を作成してブラウザからアクセスしてみます。

```html:test.html
<html>
<script type="text/javascript">
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if (this.readyState == 4 && this.status == 200) {
            console.log(xhr.response);
        }
    }
    xhr.open('GET', "http://localhost:8080/test");
    xhr.responseType = 'blob';
    xhr.send();
</script>
</html>
```

ブラウザのデベロッパーツールを開くと以下のようなエラーが。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165658/7ffe1be7-3ccf-0711-3685-6b3d993bc3e3.png)

## 対応①
`No 'Access-Control-Allow-Origin' header is present on the requested resource.`と書いてあるので、とにかくヘッダを追加すればいいのかと思いました。
「CORS　ヘッダ」で検索したら出てくるようなヘッダを3つほど追加してみました。

```golang:get_test_handler.go
//省略

func (h *GetTestHandler) Get(w http.ResponseWriter, r *http.Request) {
	//ヘッダの追加
	w.Header().Set("Access-Control-Allow-Headers", "*")
	w.Header().Set("Access-Control-Allow-Origin", "*")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(TestResponse{Test: "test"})
}
```
そして再度確認してみると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165658/e38d3cac-4bba-d361-65c0-35eac1928cd6.png)

エラーは変わらず、よく見るとヘッダが追加されていません。

## 対応②
試しにTalendAPIでリクエストしてみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165658/8a486604-6c98-5046-38dd-8f34b13e26cf.png)
ヘッダがついている？なんで？
ということでいろいろ調べてみたところ、[プリフライトリクエスト](https://developer.mozilla.org/ja/docs/Glossary/Preflight_request)への応答が必要だと判明しました。
ということでOPTIONSリクエストに応答できるようにソースコードを変更してみました。
（ついでにhandlerの共通処理を持たせる`test_handler.go`を作成）

```golang:main.go
package main

import (
	"net/http"
	"github.com/gorilla/mux"
)

func main() {
	r := mux.NewRouter()
	getTestestHandler := NewGetTestHandler()
    //Methodsに"OPTIONS"を追加 
	r.HandleFunc("/test", TestHandler(testHandler.Get)).Methods("GET", "OPTIONS")
	http.ListenAndServe(":8080", r)
}
```

```golang:get_test_handler.go
//省略

func (h *GetTestHandler) Get(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(TestResponse{Test: "test"})
}
```

```golang:test_handler.go
package main

import (
	"net/http"
)

type TestHandle func(w http.ResponseWriter, r *http.Request)

func TestHandler(handle TestHandle) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//ヘッダの追加
		w.Header().Set("Access-Control-Allow-Headers", "*")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")

		//プリフライトリクエストへの応答
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
			return
		}

		//Handler関数の実行
		handle(w, r)
	}
}
```

ということで確認してみると。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165658/9c70ff45-9018-b74c-2724-c1be790025c0.png)

やっとできましたー

## まとめ
CORSに対応するには、
　1. ヘッダの付与
　2. プリフライトリクエストへのレスポンス
が必要ということでした。
対応内容は単純ですが意外と時間を要してしまったので、同じような状況でハマっている方の助けになれば幸いです。