1
0

More than 1 year has passed since last update.

Go(echo)で簡易的なCookieを実装してみた

Last updated at Posted at 2023-08-19

はじめに

以前、Go(echo)とMySQLでAPIサーバーを構築してみました。それの続きとして今回は、Go(echo)を使ったCookieの実装をしてみました。
サーバー構築とかバックエンドとか全然やったことなかったので、無知が故に沼った箇所が多々ありました。自分への戒めとして、それらも記述したいと思います。

実装した処理の流れ

  • フロント → localhost:3000
  • バック → localhost:8080
  • localhost:3000→localhost:8080/setSessionに、データをPOSTメソッドで送る+Cookieの作成
  • localhost:3000→localhost:8080/readSessionに、GETメソッドでブラウザ側にCookieがあるかを確認

たったこれだけ。

GoとCookie

まずはCookieとは何かを知る必要があり、さらにそれをどのようにGoで実装するのかを知る必要がありました。こちらの記事が細かく説明してくれていたので、参考にしました。

環境構築(Goのインストール以降の話)

以下のディレクトリ構成で作りました。

./practice_cookie
    ├─ cookie.go
    ├─ fileserver.go
    ├─ index.html
    ├─ go.mod
    └─ go.sum

必要なフレームワークをインストールします。

  1. go.modの作成
    • ターミナルでgo mod init mainを実行して、go.modファイルを作成
    • go.modと同じディレクトリで、go get github.com/labstack/echo/v4github.com/labstack/echo/v4/middlewareを実行
  2. go.sumの作成
    • go.modと同じディレクトリで、go mod tidyを実行

echoのインストールで1つ気をつけてほしいポイントがありまして。GoでCookieを実装する方法を調べると、色々と記事が出てきますが、多くの記事で使われているechoフレームワークにはechoecho/v4の2つがあります(middlewareも同様)。これらが混同してしまうと、実行時にエラーが発生するため、echoかecho/v4かのどちらかに統一してください。今回のこの記事ではecho/v4にしました。
僕は2つのフレームワークが混在してしまって、ここでちょっと沼りました。

ソースコード

cookie.go

cookie.go
package main

import (
	"fmt"
	"net/http"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

// 受け取るデータ
type info struct {
	Id string `json:"id"`
	Password string `json:"password"`
}

// Cookieの作成
func setCookie(c echo.Context) error {
	post := new(info)
	err := c.Bind(&post)
	if err != nil {
		fmt.Println(err)
		return c.String(http.StatusCreated, "Missed setCookie!")
	}

	cookie := &http.Cookie{
		Name: "cookie",
		Value: post.id,
		MaxAge: 10, // 10secs
		Path: "/",
	}
	
	c.SetCookie(cookie)
	return c.String(http.StatusCreated, "Success setCookie!")
}

// Cookieの確認
func readCookie(c echo.Context) error {
	cookie, err := c.Cookie("cookie")
	if err != nil {
		return c.String(http.StatusCreated, "Missed readCookie!")
	}
	
    // Cookie情報がちゃんとあるか確認
	// fmt.Println(cookie.Name)
	// fmt.Println(cookie.Value)

	return c.String(http.StatusCreated, "Success readCookie!")
}

func main() {
	e := echo.New()

	// ミドルウェアの設定
	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowCredentials: true,
		AllowOrigins: []string{"http://localhost:3000"},
		AllowMethods: []string{
			http.MethodPost,
			http.MethodGet,
		},
	}))

	// ハンドラーの設定
	e.POST("/setCookie", setCookie)
	e.GET("/readCookie", readCookie)

    // サーバー起動
	e.Logger.Fatal(e.Start(":8080"))
}

コードの内容をざっくりと説明すると、

  • setCookieで、フロント側から送られるjson形式のデータを受け取り、それをもとにCookie情報を作成します
  • readCookieで、ブラウザ側にCookie情報があるかを確認します
  • mainで、Cookieのやり取りをする設定を色々やってます

ここで、Cookieのやりとりをする際に、めちゃくちゃ大事になってくる内容が、main関数の中に記述しているmiddleware.CORSWithConfigです。ここはかなり沼りました..。
こちらの記事のおかげで、無事解決できました。端的に話すと、Cookieのやりとりをするためには、バック側でAccess-Control-Allow-Origincredentialsの設定が必須であるということです。この設定が無くてもフロント側とデータのやりとりはできるのですが、Cookieだけはやりとりができない、という事態になります。

データは送れているっぽいのに、Cookieは送れてなさそう..?ってことは、通信は問題ないからCookieを新規作成するコードが間違ってるのか!と勘違いしてしまいました。

fileserver.go

fileserver.go
package main

import (
	"net/http"
)

func main() {
	mux := http.NewServeMux()

	fileServer := http.FileServer(http.Dir("."))
	mux.Handle("/", http.StripPrefix("/", fileServer))
	server := http.Server{
		Addr: ":3000",
		Handler: mux,
	}

	server.ListenAndServe()
}

フロント側で使用する静的なファイル(今回はindex.html)を持ってもらうだけのサーバーです。同じディレクトリに静的なファイルがあるため、引数が./になっていますが、ここでパス名を指定したら、他の静的ファイルが存在するディレクトリを指定できるらしいです。

index.html

index.html
<html>
    <body>
        <h1>入力フォーム</h1>
        <input type="text" id="id" placeholder="id"><br>
        <input type="password" id="password" placeholder="password"><br>
        <input type="button" value="Cookieを新規作成" onclick="setCookie()">
        <input type="button" value="Cookieの確認" onclick="readCookie()">
    </body>
</html>

<script>
    const baseURL = "http://localhost:8080"
    
    async function setCookie() {
        var id = document.getElementById("id").value;
        var password = document.getElementById("password").value;
        console.log(id, password);
        await fetch(baseURL + "/setCookie", {
            method: "POST",
            mode: "cors",
            credentials: "include",
            headers: {
                "Content-Type": "application/json",
            },
            body: JSON.stringify({
                "id": id,
                "password": password,
            })
        })
        .then(response => console.log(response.text()));
    }
    
    async function readCookie() {
        await fetch(baseURL + "/readCookie", {
            method: "GET",
            mode: "cors",
            credentials: "include",
        })
        .then(response => console.log(response.text()));
    }
</script>

JavaScriptのfetchを使って、バック側とやりとりをしています。ここにも沼ポイントがありました。
先ほど参考にした記事に、Cookieのやりとり時にフロント側でやるべき設定も記載されています。フロント側では、modecredentialsの設定が必須です。これらが無いとエラーになります。また、今回POSTメソッドにて送るデータはjson形式になっているため、headersの設定も必要です。

個人的に、このフロント側での必須な設定が一番沼でした。Cookieのやりとりは全部バックで設定するものだと勝手に思っていたので、フロント側でも考慮するなんてこれっぽっちも思っていませんでした。良い勉強になりました。

その他、細かいフロント側の話(async/awaitやpromise)に関する知識は、こちらの記事がわかりやすかったです。

実行確認

2つのターミナルで、今回の作業ディレクトリまで移動して、それぞれのターミナルでgo run cookie.gogo run fileserver.goを実行します。cookie.goの方は大きな文字でEchoと表示され、fileserver.goの方は何も表示されませんが、それで合ってます。
Chromeでhttp://localhost:3000にアクセスすると、以下のような画面になっているはずです。その画面上でF12を使って、DevToolsを確認できるようにします。
image.png
テキトーにidとpasswordを入力して、まずはCookieを新規作成ボタンをクリック。
image.png

すると上画像のように、DevToolsで入力したidとpasswordが確認できると思います。同時に、promiseも表示されるので、クリックして確認してみると、"Success setCookie!"が確認でき、見事Cookieの作成ができました。


Cookieがブラウザ上に本当にあるかを確認するためには、DevToolsのApplicationでCookieとあるので、そこの中にあるhttp://localhost:3000を見てみます。
スクリーンショット 2023-08-19 14.29.49.png
すると、cookieというNameでpasswordというValueがありました。これらのCookieの内容は、cookie.goで設定しています。今回のCookieの設定では、10秒だけ保持されるような設定にしているため、10秒経つとCookie情報は自動的に無くなります(設定した時間が経過してもDevTools上には表示されていますが、ブラウザを再読み込みすると消えます)。
image.png


最後に、Cookie情報がきちんと読み取れているかを確認します。
Cookieを新規作成してから、設定した時間内で、今度はCookieの確認ボタンをクリックしてみます。スクリーンショット 2023-08-19 14.37.29.png
こんな感じで、設定した時間だけCookieが有効になっていることが確認できました。

終わりに

Cookieの実装が、思っていた数倍難しかったです。コーディングの複雑さとかは全然感じませんでしたが、記事中にもちょこちょこ書いていたとおり、Cookieのやりとりに必要な設定周りで苦戦しました。一方で、今回の苦戦した箇所は非常に良い経験になったと思います。

1
0
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
1
0