ミドルウェアとは何か?
**ミドルウェア(Middleware)**は、高階関数として機能する設計パターンであり、ある関数を受け取り、機能を追加した新しい関数を返却します。このパターンの本質的な概念は、関数を階層的に組み合わせることで、動的に処理機能を拡張することにあります。Go言語において、ミドルウェアはログ収集、認証処理、エラー管理、データ変換などの用途で広く利用されます。
// ミドルウェア型定義:ハンドラを受け取り、強化されたハンドラを返す高階関数
type Middleware func(Handler) Handler
ミドルウェアの必要性
実際の開発では次のような要件が頻繁に発生します:
監査ログ:関数実行前後にパラメータや実行時間を記録 アクセス制御:ユーザの権限に基づきリクエストの許可判断 例外管理:実行中のエラーをキャッチして統合的に処理
ミドルウェアを使用しない場合、コードは以下のようになります:
func (svc *MyService) SampleOperation(ctx context.Context, input string) error {
RecordLog(ctx) // ログ記録
if authErr := ValidatePermission(ctx); authErr != nil { // 権限確認
return authErr
}
return svc.ExecuteCoreLogic(ctx, input) // 核心ロジック
}
このように記述することで、ビジネスロジックと補助的な処理が混在し、保守性が低下します。
ミドルウェアを導入することで、これらの関心事を分離し、コードを簡潔かつ明確にすることができます:
func (svc *MyService) SampleOperation(ctx context.Context, input string) error {
return svc.ExecuteCoreLogic(ctx, input) // ビジネスロジックに集中
}
ミドルウェアによる機能注入により、業務コードは単一の責任を持つようになり、可読性と保守性が向上します。
ミドルウェアの動作原理
以下3つのステップでその動作を説明します:
ミドルウェアの実装 業務ハンドラの実装 複数ミドルウェアの組み合わせとハンドラのラップ
3.1 ミドルウェアの実装
前述の抽象定義に基づき、具体的なミドルウェアを実装します。
// ミドルウェア型宣言
type Middleware func(Handler) Handler
// ロギング機能を提供するミドルウェア
func LoggingMiddleware(next Handler) Handler {
return func(payload string) string {
fmt.Println("開始時刻:", time.Now().Format("15:04:05"))
outcome := next(payload) // 次の処理を呼び出し(クロージャーでnextを保持)
fmt.Println("終了時刻:", time.Now().Format("15:04:05"))
return outcome
}
}
// 認証機能を提供するミドルウェア
func AuthenticationMiddleware(next Handler) Handler {
return func(payload string) string {
fmt.Println(" 認証プロセス開始")
if payload == "不正ユーザー" {
return "認証失敗"
}
outcome := next(payload)
fmt.Println(" 認証プロセス完了")
return outcome
}
}
3.2 ハンドラの実装
// 実際の業務処理を担当する関数
func CoreBusinessHandler(payload string) string {
fmt.Println(" 処理対象: " + payload + " - 実行完了")
return payload
}
3.3 ミドルウェアの連鎖とハンドラのラップ
複数のミドルウェアを同時に適用する必要がある場合、それらを適切な順序で結合する必要があります。
結合はチェーン構造(Chain)を通じて行われます。以下のコードはChain関数の実装方法を示しています:
func BuildChain(middlewareList ...Middleware) Middleware {
return func(target Handler) Handler {
// 後方から前方へ向けてミドルウェアをラップ
for index := len(middlewareList) - 1; index >= 0; index-- {
target = middlewareList[index](target)
}
return target
}
}
// 1. ミドルウェアを連鎖させる
composedMiddleware := BuildChain(LoggingMiddleware, AuthenticationMiddleware)
// 2. ハンドラをラップ
processedHandler := composedMiddleware(CoreBusinessHandler)
ミドルウェアの実行プロセスは**「製造ラインでの加工」**に例えることができます:元のデータが各ミドルウェアを通過しながら段階的に処理され、最終的に完成した結果が返されます。
開始時刻: 18:21:04 認証プロセス開始 処理対象: ファイル作成 - 実行完了 認証プロセス完了 終了時刻: 18:21:04
完全な実装例
前述のコードフローを完全に示すために、以下の完全なサンプルを参照してください:
package main
import (
"fmt"
"time"
)
// ミドルウェア型定義:ハンドラを入力として受け取り、変換されたハンドラを出力
type Middleware func(Handler) Handler
// ログ記録ミドルウェアの実装
func LoggingMiddleware(next Handler) Handler {
return func(payload string) string {
fmt.Println("開始時刻:", time.Now().Format("15:04:05"))
outcome := next(payload) // 次の処理を呼び出し(クロージャーでnextを保持)
fmt.Println("終了時刻:", time.Now().Format("15:04:05"))
return outcome
}
}
// 認証ミドルウェアの実装
func AuthenticationMiddleware(next Handler) Handler {
return func(payload string) string {
fmt.Println(" 認証プロセス開始")
if payload == "不正ユーザー" {
return "認証失敗"
}
outcome := next(payload)
fmt.Println(" 認証プロセス完了")
return outcome
}
}
// 複数ミドルウェアを連鎖させる関数
// 入力:n個のミドルウェア(高階関数)
// 出力:1つの合成ミドルウェア(高階関数)
// 機能:n個のミドルウェアを階層的にラップ
func BuildChain(middlewareList ...Middleware) Middleware {
return func(target Handler) Handler {
// 後方から前方へ向けてミドルウェアを適用
for index := len(middlewareList) - 1; index >= 0; index-- {
target = middlewareList[index](target)
}
return target
}
}
// ハンドラ型定義:HTTPリクエストに対する実際の処理ロジックに対応
type Handler func(string) string
// 核心業務処理関数
func CoreBusinessHandler(payload string) string {
fmt.Println(" 処理対象: " + payload + " - 実行完了")
return payload
}
// 実行例
func main() {
composedMiddleware := BuildChain(LoggingMiddleware, AuthenticationMiddleware)
processedHandler := composedMiddleware(CoreBusinessHandler)
processedHandler("ファイル作成")
}