Personalización de las solicitudes de cliente de AWS SDK para Go v2 con middleware
aviso
La modificación de la canalización de solicitudes de cliente puede generar solicitudes no válidas o con formato incorrecto, o puede provocar errores de aplicación inesperados. Esta funcionalidad está pensada para casos de uso avanzados que la interfaz del SDK no proporciona de forma predeterminada.
Puede personalizar las solicitudes de cliente de AWS SDK para Go mediante el registro de middleware (uno o más) en la pila
| Paso de la pila | Descripción |
|---|---|
| Initialize | Prepara la entrada y establece los parámetros predeterminados según sea necesario. |
| Serialize | Serializa la entrada en un formato de protocolo adecuado para la capa de transporte de destino. |
| Build | Asocie metadatos adicionales a la entrada serializada, como la longitud del contenido HTTP. |
| Finalize | Preparación del mensaje final, incluidos los reintentos y la autenticación (firma de SiGv4). |
| Deserialize | Deserialice las respuestas del formato de protocolo en un error o tipo estructurado. |
Cada middleware de un paso determinado debe tener un identificador único, que viene determinado por el método ID del middleware. Los identificadores de middleware garantizan que solo se registre una instancia de un middleware determinado en un paso y permiten insertar otro middleware de paso en relación con este.
Para asociar middleware de paso, use los métodos Insert o Add de un paso. Use Add para asociar middleware al principio de un paso al especificar middleware.BeforeInsert para asociar middleware a un paso con la inserción del middleware relativo a otro middleware de paso.
aviso
Debe utilizar el método Add para insertar middleware de paso personalizado de forma segura. El uso de Insert crea una dependencia entre su middleware personalizado y aquel relacionado que inserta. El middleware incluido en un paso de pila debe considerarse opaco para evitar que se produzcan cambios importantes en la aplicación.
Escritura de un middleware personalizado
Cada paso de pila tiene una interfaz que debe cumplirse para poder asociar un middleware a un paso determinado. Puede utilizar una de las funciones proporcionadas para cumplir rápidamente con esta interfaz. En la tabla siguiente se describen los pasos, su interfaz y la función auxiliar que se puede utilizar para cumplir con la interfaz. StepMiddlewareFunc
| Paso | Interfaz | Función Helper |
|---|---|---|
| Initialize | InitializeMiddleware |
InitializeMiddlewareFunc |
| Build | BuildMiddleware |
BuildMiddlewareFunc |
| Serialize | SerializeMiddleware |
SerializeMiddlewareFunc |
| Finalize | FinalizeMiddleware |
FinalizeMiddlewareFunc |
| Deserialize | DeserializeMiddleware |
DeserializeMiddlewareFunc |
En los ejemplos siguientes se muestra cómo puede escribir un middleware personalizado para rellenar el miembro Bucket de las llamadas a la API GetObject de Amazon S3 en caso de que no se proporcione uno. Se hará referencia a este middleware en ejemplos posteriores para mostrar cómo asociar middleware de un paso a la pila.
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) })
Cómo asociar middleware a todos los clientes
Para asociar su middleware de paso personalizado a cada uno de los clientes, añada el middleware mediante el miembro APIOptions del tipo aws.ConfigdefaultBucket a todos los clientes creados con el objeto aws.Config de sus aplicaciones:
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)
Cómo asociar middleware a una operación específica
Para asociar su middleware de paso personalizado a una operación de cliente específica, modifique el miembro APIOptions del cliente con la lista de argumentos variádicos de una operación. En los ejemplos siguientes se asocia el middleware defaultBucket a la invocación de una operación GetObject de Amazon S3 específica:
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) })
Transferencia de metadatos descendente en la pila
En determinadas situaciones, puede que necesite utilizar dos o más middlewares a la par mediante el intercambio de información o del estado. Puede usar context.Contextmiddleware.WithStackValue asocia el par clave-valor determinado al contexto proporcionado y limita de forma segura el alcance a la pila en ejecución. Estos valores para la pila se pueden recuperar de un contexto mediante middleware.GetStackValuecontext.Context para realizar una transferencia de información descendente en la pila.
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) })
Metadatos proporcionados por el SDK
AWS SDK para Go proporciona varios valores de metadatos que se pueden recuperar del contexto proporcionado. Estos valores pueden utilizarse para habilitar un middleware más dinámico que modifique su comportamiento en función de la región de destino, la operación o el servicio en ejecución. En la tabla siguiente se proporcionan algunas de las claves disponibles:
| Clave | Recuperador | Descripción |
|---|---|---|
| ServiceID | GetServiceID |
Recupera el identificador de servicio para la pila en ejecución. Esto puede compararse con la constante ServiceID del paquete de clientes de servicio. |
| OperationName | GetOperationName |
Recupera el nombre de la operación para la pila en ejecución. |
| Logger | GetLogger |
Recupera el registrador que se puede usar para registrar mensajes del middleware. |
Transferencia de metadatos ascendente en la pila
Puede realizar una transferencia de metadatos ascendente en la pila mediante la adición de pares clave-valor de los metadatos con middleware.MetadataResultMetadata.
En los ejemplos siguientes se muestra cómo un middleware personalizado puede agregar metadatos que se devuelven como parte del resultado de la operación.
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)