使用中间件自定义适用于 Go 的 AWS SDK v2 客户端请求
警告
修改客户端请求管道可能会导致请求格式错误/无效,或者可能导致意外的应用程序错误。此功能适用于默认情况下 SDK 接口不提供的高级使用案例。
您可以通过将一个或多个中间件注册到服务操作的堆栈
| 堆栈步骤 | 描述 |
|---|---|
| 初始化 | 准备输入,并根据需要设置任何默认参数。 |
| 序列化 | 将输入序列化为适合目标传输层的协议格式。 |
| 构建 | 将其他元数据附加到序列化输入中,例如 HTTP Content-Length。 |
| 最终确定 | 最后的消息准备,包括重试和身份验证(SigV4 签名)。 |
| 反序列化 | 将协议格式的响应反序列化为结构化类型或错误。 |
给定步骤中的每个中间件都必须具有唯一标识符,该标识符由中间件的 ID 方法确定。中间件标识符可确保给定的中间件仅有一个实例注册到某个步骤中,并允许相对于该实例插入其他步骤中间件。
您可以使用一个步骤的 Insert 或 Add 方法来连接步骤中间件。您可以使用 Add,通过将 middleware.BeforeInsert 将中间件附加到步骤,方法是相对于另一个步骤中间件插入该中间件。
警告
您必须使用 Add 方法安全地插入自定义步骤中间件。使用 Insert 会在您的自定义中间件和您要插入时相对的中间件之间建立依赖关系。必须将堆栈步骤中的中间件视为不透明,以避免应用程序发生重大更改。
编写自定义中间件
每个堆栈步骤都有一个接口,您必须满足该接口的要求,才能将中间件附加到给定步骤。您可以使用提供的 函数之一来快速满足此接口的要求。下表概述了步骤、其接口以及可用来满足接口要求的帮助程序函数。StepMiddlewareFunc
以下示例演示了如何编写自定义中间件来填充 Amazon S3 GetObject API 调用的存储桶成员(如果未提供)。后续示例将引用此中间件,以演示如何将步骤中间件附加到堆栈。
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 成员添加中间件,从而将自定义步骤中间件附加到每个客户端。以下示例将 defaultBucket 中间件附加到使用您的应用程序 aws.Config 对象构造的每个客户端:
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 成员,可以将自定义步骤中间件附加到特定的客户端操作。以下示例将 defaultBucket 中间件附加到特定的 Amazon S3 GetObject 操作调用:
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 提供的元数据
适用于 Go 的 AWS SDK 提供了几个可以从所提供的上下文中检索的元数据值。这些值可用于启用更动态的中间件,该中间件可以根据正在执行的服务、操作或目标区域来修改其行为。下表提供了一些可用的键:
| 键 | 检索器 | 描述 |
|---|---|---|
| ServiceID | GetServiceID |
检索正在执行的堆栈的服务标识符。这可以与服务客户端程序包的 ServiceID 常量进行比较。 |
| OperationName | GetOperationName |
检索正在执行的堆栈的操作名称。 |
| Logger | 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)