Go言語(Go-Json-Rest)でAPIサーバーを立てるときのCORS設定 (Basic認証機能付きも)


Go楽しいですね。[昨日のJSおじさん](https://atnd.org/events/67066)も楽しかったw 

[Go言語でJSONを返すシンプルなAPIサーバーを作るメモ](http://qiita.com/n0bisuke/items/62d11e4f17a9b5d483c0)とか書きましたが、[Go-Json-Rest](https://github.com/ant0ine/go-json-rest)を使ってAPIサーバーを作っています。

## APIサーバー立てるとき最近は気をつけたいCORS

こういうメッセージがajax通信したときのエラーでよく見るようになりました。

```error_message
XMLHttpRequest cannot load http://localhost:8080/countries?ie=1435165847. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://n0bisuke-centos.local' is therefore not allowed access. The response had HTTP status code 403.
```

![](http://i.gyazo.com/1c0eae528b115f98e96547dedae38343.png)

これってそんなに前からあったわけじゃないですけど、最近だと大抵のブラウザがクロスオリジンへのアクセスを拒否しちゃうやつです。サーバー側でヘッダー情報に設定してあげてないといけません。

オリジンは http://localhost:3000 とかの場合

**`http://`がプロトコル、 `localhost`がドメイン、 `:3000`**がポート番号みたいな分け方になりますが、これら全てを含めたものをオリジン(Origin)と呼ぶみたいです。

## Go-Json-Restでやってみる。

このライブラリはかなり多機能で、今の所、詰まっても大体解決してくれてます。
[ここ](https://github.com/ant0ine/go-json-rest#cors)に情報がありました。

サンプルコードです。

```cors.go
package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "log"
    "net/http"
)

func main() {
    api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)
    api.Use(&rest.CorsMiddleware{
        RejectNonCorsRequests: false,
        OriginValidator: func(origin string, request *rest.Request) bool {
            return origin == "http://my.other.host"
        },
        AllowedMethods: []string{"GET", "POST", "PUT"},
        AllowedHeaders: []string{
            "Accept", "Content-Type", "X-Custom-Header", "Origin"},
        AccessControlAllowCredentials: true,
        AccessControlMaxAge:           3600,
    })
    router, err := rest.MakeRouter(
        rest.Get("/countries", GetAllCountries),
    )
    if err != nil {
        log.Fatal(err)
    }
    api.SetApp(router)
    log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}

type Country struct {
    Code string
    Name string
}

func GetAllCountries(w rest.ResponseWriter, r *rest.Request) {
    w.WriteJson(
        []Country{
            Country{
                Code: "FR",
                Name: "France",
            },
            Country{
                Code: "US",
                Name: "United States",
            },
        },
    )
}
```

このコードの中で設定はこの部分が肝みたいですね。

```cors.go
/*省略*/
api.Use(&rest.CorsMiddleware{
        RejectNonCorsRequests: false,
        OriginValidator: func(origin string, request *rest.Request) bool {
            return origin == "http://my.other.host"
        },
        AllowedMethods: []string{"GET", "POST", "PUT"},
        AllowedHeaders: []string{
            "Accept", "Content-Type", "X-Custom-Header", "Origin"},
        AccessControlAllowCredentials: true,
        AccessControlMaxAge:           3600,
    })

/*省略*/
```

## 全て許可の場合

* `OriginValidator`

ここの関数の返り値を`true`にすれば全て許可出来ると思います。

```cors.go
/*省略*/
api.Use(&rest.CorsMiddleware{
        OriginValidator: func(origin string, request *rest.Request) bool {
            return true
        },
    })
/*省略*/
```

PHPだったらこんな感じに書くやつです。

```cors.php
header("Access-Control-Allow-Origin: *");
```

## 例えば、`http://localhost:3000`とかgulpとかでフロント開発してるエンジニアがいる場合

* `OriginValidator`

ここに許可するOriginを指定します。

```cors.go
/*省略*/
api.Use(&rest.CorsMiddleware{
        OriginValidator: func(origin string, request *rest.Request) bool {
            return origin == "http://localhost:3000"
        },
    })

/*省略*/
```

## Basic認証付き


### Basic認証機能をつける

簡単です。
こんな感じで記述します。

```cors.go
/*省略*/

api.Use(&rest.AuthBasicMiddleware{
       Realm: "message!!!!",
       Authenticator: func(userId string, password string) bool {
           if userId == "userid" && password == "password" {
               return true
           }
           return false
       },
    })
    
/*省略*/
```

![](http://i.gyazo.com/d66edfb62068ba91fbde82bc58ff1a97.png)


### Basic認証機能付きサーバーのCORSの設定

ちなみに、Chromeで試してます。

* `AllowedMethods`

設定しないといけないみたい。API開発で`PUT`や`DELETE`って使ったことほぼ無いですし、使わない場合は`GET`と`POST`だけでいいと思います。

* `AllowedHeaders`

`Accept`と`Authorization`をつけましょう。詳細は...(調べておこう...)

```cors.go
    api.Use(&rest.CorsMiddleware{
        OriginValidator: func(origin string, request *rest.Request) bool {
            return origin == "http://localhost"
        },
        AllowedMethods: []string{"GET", "POST"},
        AllowedHeaders: []string{"Accept","Authorization"},
    })
```

###おまけ: $.ajax()からBasic認証付きアクセス

※jquery1.11です。

```api.js
$(function(){
  var host = 'localhost:8080';
  var rand_param = '?ie=' + parseInt((new Date())/1000, 10);
  var endpoint = '/countries';
  var url = 'http://'+host+endpoint+rand_param;

  function make_base_auth(user, password) {
    var tok = user + ':' + password;
    var hash = btoa(tok);
    return "Basic " + hash;
  }

  $.ajax({
      url: url,
      beforeSend: function (xhr){
        xhr.setRequestHeader('Authorization', make_base_auth('userud', 'password'));
      },
  }).done(function(data){
      console.log(data);
  }).fail(function(data){
      console.log(data);
  });

});

```

こんな感じでデータ取得できます！

![](http://i.gyazo.com/de17e883ed38fe4c2291e09dc6b7e72e.png)

go-json-restのCORSなドキュメントは[こちら](https://godoc.org/github.com/ant0ine/go-json-rest/rest#CorsMiddleware)です。

##まとめ

CORS意識していこう。
Go楽しい。

参考: [GolangでXMLHttpRequestLevel2+CORSのプリフライトが通るサーバーを立てる(BasicAuth付き)](http://qiita.com/futosu/items/b49f7d9e28101daaa99e)