はじめて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. プリフライトリクエストへのレスポンス
が必要ということでした。
対応内容は単純ですが意外と時間を要してしまったので、同じような状況でハマっている方の助けになれば幸いです。