はじめてgolangでgorilla/muxを使用したAPIを作ったのですが、CORSで諸々ハマったので記事としてまとめます。
ことの始まり
以下のような実装のAPIを作りました。
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)
}
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>
<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>
対応①
No 'Access-Control-Allow-Origin' header is present on the requested resource.と書いてあるので、とにかくヘッダを追加すればいいのかと思いました。
「CORS ヘッダ」で検索したら出てくるようなヘッダを3つほど追加してみました。
//省略
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"})
}
そして再度確認してみると、
エラーは変わらず、よく見るとヘッダが追加されていません。
対応②
試しにTalendAPIでリクエストしてみると

ヘッダがついている?なんで?
ということでいろいろ調べてみたところ、プリフライトリクエストへの応答が必要だと判明しました。
ということでOPTIONSリクエストに応答できるようにソースコードを変更してみました。
(ついでにhandlerの共通処理を持たせるtest_handler.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)
}
//省略
func (h *GetTestHandler) Get(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(TestResponse{Test: "test"})
}
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)
	}
}
やっとできましたー
まとめ
CORSに対応するには、
 1. ヘッダの付与
 2. プリフライトリクエストへのレスポンス
が必要ということでした。
対応内容は単純ですが意外と時間を要してしまったので、同じような状況でハマっている方の助けになれば幸いです。


