Personalización de las solicitudes de cliente de AWS SDK para Go v2 con middleware - AWS SDK para Go  v2

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 de una operación de servicio. La pila se compone de una serie de pasos: Initialize, Serialize, Build, Finalize y Deserialize. El middleware (cero o más) que contiene cada paso opera en los tipos de entrada y salida de ese paso. El diagrama y la tabla siguientes proporcionan información general sobre cómo la solicitud y la respuesta de una operación recorren la pila:

Middleware

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.Before como RelativePosition, y middleware.After para asociarlo al final del paso. Use Insert 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 StepMiddlewareFunc 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.

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.Config. En los ejemplos siguientes se asocia el middleware defaultBucket 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.Context para pasar estos metadatos mediante middleware.WithStackValue. middleware.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.GetStackValue y con la clave usada para almacenar el valor correspondiente. Las claves deben ser comparables y debe definir sus propios tipos como claves de contexto para evitar conflictos. En los ejemplos siguientes se muestra cómo dos middleware pueden utilizar context.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.Metadata. Cada paso de middleware devuelve una estructura de salida, metadatos y un error. Su middleware personalizado debe devolver los metadatos recibidos al llamar al siguiente controlador del paso. Esto garantiza que los metadatos agregados por el middleware descendente se propaguen a la aplicación que invoca la operación de servicio. La aplicación que realiza la invocación puede acceder a los metadatos resultantes mediante la forma de salida de la operación a través del miembro de la estructura ResultMetadata.

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)