Tags: Go Golang

Go で Middleware を作り Context にデータを入れて次の処理に引き継ぐ

Go で Middleware を作り、その中で読み込んだデータを Context に入れて次の処理に引き継ぐ。

開発環境

  • go version go1.17.5 linux/amd64

本文

Go で認証必須の API を保護する Middleware を作成し、その Middleware 内で読み込んだデータを Context に入れて API 本体の処理に引き継ぐ方法についてメモする。

Middleware

今回の例では、認証状態は “auth-session” というセッションから uid が取得できるかどうかで判定する。

package middleware

import (
	"context"
	"net/http"

	"github.com/pkg/errors"
	"github.com/k1350/sololog/internal/session"
)

type AuthContext map[string]interface{}

func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
        // ここは各自の実装で認証状態が保存されているセッションを取得する
		s, err := session.Store.Get(r, "auth-session")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}

		uid := s.Values["uid"]
		if uid == nil {
            // uid が取得できなかった場合は 401 エラーを返す
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

        // API 本体の処理に引き継ぎたい値を context にセット
		data := AuthContext{
			"uid": uid.(string),
		}
		ctx := context.WithValue(r.Context(), "auth", data)
        // r.WithContext で値を引き継ぐ
		next.ServeHTTP(w, r.WithContext(ctx))
	}
}

Middleware の使用

下記のように http.HandleFunc 内で作成した Middleware で本体の関数を包む。
また Middleware から引き継いだ値は r.Context() から読み取ることができる。

package main

import (
    "fmt"
	"log"
	"net/http"

    // 作成した Middleware
	"github.com/k1350/sololog/internal/middleware"
)

func main() {
	http.HandleFunc("/", middleware.AuthMiddleware(HelloServer))
	http.ListenAndServe(":8000", nil)
}

func HelloServer(w http.ResponseWriter, r *http.Request) {
    // Middleware で Context に入れたデータを取り出す
	data := r.Context().Value("auth").(middleware.AuthContext)["uid"]
	log.Println(data)
	fmt.Fprintf(w, "Hello, world!")
}