LoginSignup
3
3

More than 3 years have passed since last update.

gorilla/muxのCORS対応

Last updated at Posted at 2021-03-13

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

ことの始まり

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

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)
}
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を作成してブラウザからアクセスしてみます。

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

対応①

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

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

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

対応②

試しにTalendAPIでリクエストしてみると
image.png
ヘッダがついている?なんで?
ということでいろいろ調べてみたところ、プリフライトリクエストへの応答が必要だと判明しました。
ということでOPTIONSリクエストに応答できるようにソースコードを変更してみました。
(ついでにhandlerの共通処理を持たせるtest_handler.goを作成)

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)
}
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"})
}
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

やっとできましたー

まとめ

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

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3