ミドルウェアを使用した AWS SDK for Go v2 クライアントリクエストのカスタマイズ
警告
クライアントのリクエストパイプラインを変更すると、リクエストが不正または無効になったり、予期しないアプリケーションエラーが発生したりする可能性があります。この機能は、SDK インターフェイスがデフォルトで提供していない高度なユースケース向けです。
AWS SDK for Go クライアントリクエストは、サービスオペレーションの スタック
| スタックステップ | 説明 |
|---|---|
| Initialize | 入力を準備し、必要に応じてデフォルトのパラメータを設定します。 |
| Serialize | 入力を対象のトランスポート層に適したプロトコル形式にシリアル化します。 |
| Build | HTTP Content-Length などの追加のメタデータをシリアル化済みの入力にアタッチします。 |
| Finalize | 再試行や認証 (SigV4 署名) を含む最終的なメッセージの準備を行います。 |
| Deserialize | プロトコル形式のレスポンスを構造化された型またはエラーに逆シリアル化します。 |
各ステップ内のミドルウェアには、そのミドルウェアの ID メソッドによって決まる一意の ID が必要です。ミドルウェアの ID により、同じミドルウェアがステップに重複して登録されるのを防ぎ、他のミドルウェアをその相対位置に挿入できるようになっています。
ステップミドルウェアは、ステップの Insert または Add メソッドを使用してアタッチします。Add を使用すると、RelativePositionInsert を使用すると、別のミドルウェアに対して相対的な位置にミドルウェアをアタッチできます。
警告
カスタムのステップミドルウェアを安全に挿入するには、Add メソッドを使用する必要があります。Insert を使用すると、カスタムミドルウェアと、相対挿入先となる他のミドルウェアとの間に依存関係が作成されます。アプリケーションの互換性を保つため、スタックステップ内のミドルウェアはブラックボックスとして扱う必要があります。
カスタムミドルウェアの記述
各スタックステップには対応するインターフェイスがあり、ミドルウェアをアタッチするにはそのインターフェイスを実装する必要があります。提供されている 関数を使用すれば、このインターフェイスをすばやく実装できます。次の表では、各ステップ、そのインターフェイス、およびインターフェイスを実装するためのヘルパー関数の概要を示しています。StepMiddlewareFunc
| Step | インターフェイス | ヘルパー関数 |
|---|---|---|
| Initialize | InitializeMiddleware |
InitializeMiddlewareFunc |
| Build | BuildMiddleware |
BuildMiddlewareFunc |
| Serialize | SerializeMiddleware |
SerializeMiddlewareFunc |
| Finalize | FinalizeMiddleware |
FinalizeMiddlewareFunc |
| Deserialize | DeserializeMiddleware |
DeserializeMiddlewareFunc |
次の例では、Amazon S3 の GetObject API コールで Bucket メンバーが指定されていない場合に、その値を設定するカスタムミドルウェアの記述方法を示しています。このミドルウェアは、後続の例でスタックへのステップミドルウェアのアタッチ方法を示すために使用します。
import "github.com/aws/smithy-go/aws" import "github.com/aws/smithy-go/middleware" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... var defaultBucket = middleware.InitializeMiddlewareFunc("DefaultBucket", func( ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler, ) ( out middleware.InitializeOutput, metadata middleware.Metadata, err error, ) { // Type switch to check if the input is s3.GetObjectInput, if so and the bucket is not set, populate it with // our default. switch v := in.Parameters.(type) { case *s3.GetObjectInput: if v.Bucket == nil { v.Bucket = aws.String("amzn-s3-demo-bucket") } } // Middleware must call the next middleware to be executed in order to continue execution of the stack. // If an error occurs, you can return to prevent further execution. return next.HandleInitialize(ctx, in) })
すべてのクライアントへのミドルウェアのアタッチ
カスタムステップミドルウェアは、aws.ConfigAPIOptions メンバーを使用して追加することで、すべてのクライアントにアタッチできます。次の例では、アプリケーションの aws.Config オブジェクトを使用して作成されるすべてのクライアントに defaultBucket ミドルウェアをアタッチしています。
import "context" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/smithy-go/middleware" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { // handle error } cfg.APIOptions = append(cfg.APIOptions, func(stack *middleware.Stack) error { // Attach the custom middleware to the beginning of the Initialize step return stack.Initialize.Add(defaultBucket, middleware.Before) }) client := s3.NewFromConfig(cfg)
特定のオペレーションへのミドルウェアのアタッチ
カスタムのステップミドルウェアは、オペレーションの可変長引数リストを使用して、クライアントの APIOptions メンバーを変更することで、特定のクライアントオペレーションにアタッチできます。次の例では、Amazon S3 の特定の GetObject オペレーション呼び出しに defaultBucket ミドルウェアをアタッチしています。
import "context" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/smithy-go/middleware" // ... // registerDefaultBucketMiddleware registers the defaultBucket middleware with the provided stack. func registerDefaultBucketMiddleware(stack *middleware.Stack) error { // Attach the custom middleware to the beginning of the Initialize step return stack.Initialize.Add(defaultBucket, middleware.Before) } // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { // handle error } client := s3.NewFromConfig(cfg) object, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ Key: aws.String("my-key"), }, func(options *s3.Options) { // Register the defaultBucketMiddleware for this operation only options.APIOptions = append(options.APIOptions, registerDefaultBucketMiddleware) })
スタック下層へのメタデータの受け渡し
状況によっては、複数のミドルウェアが情報や状態を共有して連携する必要がある場合があります。このようなメタデータの受け渡しには、context.Contextmiddleware.WithStackValue は指定されたキーと値のペアを指定されたコンテキストにアタッチし、そのスコープを現在実行中のスタックに安全に限定します。このスタックスコープ付きの値は、対応するキーを middleware.GetStackValuecontext.Context を使用してスタック内で情報を受け渡す方法を示しています。
import "context" import "github.com/aws/smithy-go/middleware" // ... type customKey struct {} func GetCustomKey(ctx context.Context) (v string) { v, _ = middleware.GetStackValue(ctx, customKey{}).(string) return v } func SetCustomKey(ctx context.Context, value string) context.Context { return middleware.WithStackValue(ctx, customKey{}, value) } // ... var customInitalize = middleware.InitializeMiddlewareFunc("customInitialize", func( ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler, ) ( out middleware.InitializeOutput, metadata middleware.Metadata, err error, ) { ctx = SetCustomKey(ctx, "my-custom-value") return next.HandleInitialize(ctx, in) }) var customBuild = middleware.BuildMiddlewareFunc("customBuild", func( ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler, ) ( out middleware.BuildOutput, metadata middleware.Metadata, err error, ) { customValue := GetCustomKey(ctx) // use customValue return next.HandleBuild(ctx, in) })
SDK が提供するメタデータ
AWS SDK for Go では、指定されたコンテキストから取得できるメタデータ値がいくつか用意されています。これらの値を使用すると、実行中のサービスやオペレーション、対象リージョンに応じて動作を変更する、より動的なミドルウェアを記述できます。使用可能ないくつかのキーを次の表に示します。
| キー | リトリーバー | 説明 |
|---|---|---|
| ServiceID | GetServiceID |
実行中のスタックに対応するサービス ID を取得します。この ID は、サービスクライアントパッケージの ServiceID 定数と比較できます。 |
| OperationName | GetOperationName |
実行中のスタックに対応するオペレーション名を取得します。 |
| ロガー | GetLogger |
ミドルウェアからのメッセージのログ記録に使用できるロガーを取得します。 |
スタック上層へのメタデータの受け渡し
middleware.MetadataResultMetadata 構造体メンバーを介して、オペレーションの出力型から呼び出し元アプリケーションが参照できます。
次の例では、オペレーション出力の一部として返されるメタデータをカスタムミドルウェアがどのように追加するかを示しています。
import "context" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/smithy-go/middleware" // ... type customKey struct{} func GetCustomKey(metadata middleware.Metadata) (v string) { v, _ = metadata.Get(customKey{}).(string) return v } func SetCustomKey(metadata *middleware.Metadata, value string) { metadata.Set(customKey{}, value) } // ... var customInitalize = middleware.InitializeMiddlewareFunc("customInitialize", func ( ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler, ) ( out middleware.InitializeOutput, metadata middleware.Metadata, err error, ) { out, metadata, err = next.HandleInitialize(ctx, in) if err != nil { return out, metadata, err } SetCustomKey(&metadata, "my-custom-value") return out, metadata, nil }) // ... client := s3.NewFromConfig(cfg, func (options *s3.Options) { options.APIOptions = append(options.APIOptions, func(stack *middleware.Stack) error { return stack.Initialize.Add(customInitalize, middleware.After) }) }) out, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ // input parameters }) if err != nil { // handle error } customValue := GetCustomKey(out.ResponseMetadata)