Uso de AWS SDK para Go - AWS SDK para Go  v2

Uso de AWS SDK para Go

Aprenda formas comunes y recomendadas de programar con AWS SDK para Go en las aplicaciones.

Creación de un cliente de servicio

Los clientes de servicio se pueden crear mediante las funciones New o NewFromConfig disponibles en el paquete Go del cliente de servicio. Cada función devolverá un tipo de estructura Client que contiene los métodos para invocar las API de servicio. New y NewFromConfig proporcionan el mismo conjunto de opciones configurables para crear un cliente de servicio, pero ofrecen patrones de creación distintos que veremos en las secciones posteriores.

NewFromConfig

La función NewFromConfig proporciona una interfaz coherente para crear clientes de servicio mediante aws.Config. aws.Config se puede cargar mediante config.LoadDefaultConfig. Para obtener más información sobre cómo crear una instancia de aws.Config, consulte Configurar el SDK. En el siguiente ejemplo se muestra cómo crear un cliente de servicio Amazon S3 mediante aws.Config y la función NewFromConfig:

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } client := s3.NewFromConfig(cfg)

Reemplazo de la configuración

NewFromConfig puede tomar uno o más argumentos funcionales que pueden modificar la estructura Options de configuración de un cliente. Esto le permite realizar reemplazos específicos, como cambiar la región o modificar opciones específicas del servicio (por ejemplo, la opción UseAccelerate de Amazon S3). Por ejemplo:

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } client := s3.NewFromConfig(cfg, func(o *s3.Options) { o.Region = "us-west-2" o.UseAccelerate = true })

Los reemplazos del valor Options del cliente vienen determinados por el orden en el que se asignan los argumentos funcionales a NewFromConfig.

New

nota

New se considera una forma más avanzada de creación de clientes. Le recomendamos que utilice NewFromConfig para la creación de clientes, ya que permite crear con la estructura de aws.Config. Esto elimina la necesidad de crear una instancia de estructura Options para cada cliente de servicio que requiera la aplicación.

New es una función creadora de clientes que proporciona una interfaz de creación de clientes con el uso exclusivo de la estructura Options de los paquetes de clientes para definir las opciones de configuración del cliente. Por ejemplo, para crear un cliente de Amazon S3 mediante New:

import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/credentials" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... client := s3.New(s3.Options{ Region: "us-west-2", Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")), })

Reemplazo de la configuración

New puede tomar uno o más argumentos funcionales que pueden modificar la estructura Options de configuración de un cliente. Esto le permite realizar reemplazos específicos, como cambiar la región o modificar opciones específicas del servicio (por ejemplo, la opción UseAccelerate de Amazon S3). Por ejemplo:

import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/credentials" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... options := s3.Options{ Region: "us-west-2", Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, "")), } client := s3.New(options, func(o *s3.Options) { o.Region = "us-east-1" o.UseAccelerate = true })

Los reemplazos del valor Options del cliente vienen determinados por el orden en el que se asignan los argumentos funcionales a New.

Llamada a operaciones de servicio

Una vez que tenga una instancia de cliente de servicio, puede usarla para llamar a las operaciones de un servicio. Por ejemplo, para llamar a la operación GetObject de Amazon S3:

response, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("obj-key"), })

Al llamar a una operación de servicio, el SDK valida de forma sincrónica la entrada, serializa la solicitud, la firma con sus credenciales, la envía a AWS y, a continuación, deserializa una respuesta o un error. En la mayoría de los casos, puede llamar a las operaciones de servicio de forma directa. Cada método de cliente para operaciones de servicio devolverá una estructura de respuesta de la operación y un tipo de interfaz de error. Compruebe siempre el tipo error para determinar si se ha producido un error antes de intentar acceder a la estructura de respuesta de la operación de servicio.

Transferencia de parámetros a una operación de servicio

Cada método de operación de servicio toma un valor context.Context que se puede utilizar para establecer plazos de solicitud que el SDK cumplirá. Además, cada operación de servicio tomará una estructura <OperationName>Input que se encuentra en el paquete Go respectivo del servicio. Para transferir los parámetros de entrada de la API use la estructura de entrada de la operación.

Las estructuras de entrada de la operación pueden tener parámetros de entrada como los tipos numérico, booleano, de cadena, de mapa y de lista Go estándar. En operaciones de la API más complejas, un servicio puede tener un modelado más complejo de los parámetros de entrada. Estos otros tipos, como valores de enumeración y estructuras específicas del servicio, se encuentran en el paquete Go types del servicio.

Además, los servicios pueden distinguir entre el valor predeterminado de un tipo Go y si el valor lo ha establecido el usuario o no. En estos casos, puede que los parámetros de entrada requieran que pases una referencia del puntero al tipo en cuestión. En el caso de los tipos Go estándar (por ejemplo, numéricos, booleanos y de cadena), existen las funciones prácticas <Type> y From<Type> disponibles en aws para facilitar esta conversión. Por ejemplo, aws.String se puede usar para convertir string en un tipo *string para los parámetros de entrada que requieren un puntero a una cadena. Por el contrario, aws.ToString se puede usar para transformar un elemento *string en string, al tiempo que se brinda protección frente a la cancelación de referencias a un puntero nulo. Las funciones To<Type> son útiles a la hora de gestionar las respuestas del servicio.

Veamos un ejemplo de cómo podemos usar un cliente de Amazon S3 para llamar a la API GetObject y crear nuestra entrada mediante el paquete types y los ayudantes aws.<Type>.

import "context" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" import "github.com/aws/aws-sdk-go-v2/service/s3/types" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } client := s3.NewFromConfig(cfg) resp, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("keyName"), RequestPayer: types.RequestPayerRequester, })

Reemplazo de opciones de cliente para la llamada a operaciones

De forma similar a la que se usa para modificar las opciones de operación de cliente durante la creación de un cliente con argumentos funcionales, las opciones de cliente pueden modificarse en el momento en el que se llama al método de operación al proporcionar uno o varios argumentos funcionales al método de operación de servicio. Esta acción se puede ejecutar a la vez que otras de forma segura y no afectará a otras operaciones simultáneas del cliente.

Por ejemplo, para reemplazar la región del cliente “us-west-2” por “us-east-1”:

cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2")) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) params := &s3.GetObjectInput{ // ... } resp, err := client.GetObject(context.TODO(), params, func(o *Options) { o.Region = "us-east-1" })

Gestión de respuestas de operación

Cada operación de servicio tiene una estructura de salida asociada que contiene los miembros de respuesta de la operación del servicio. La estructura de salida sigue el patrón de nomenclatura <OperationName>Output. Puede que algunas operaciones no tengan ningún miembro definido para su salida de operación. Tras llamar a una operación de servicio, siempre se debe comprobar el tipo de argumento error devuelto para determinar si se ha producido un error al invocar la operación de servicio. Los errores devueltos pueden abarcar desde errores de validación de entradas del cliente hasta respuestas de error del servicio devueltas al cliente. No se debe acceder a la estructura de salida de la operación en caso de que el cliente devuelva un error que no sea nulo.

Por ejemplo, para registrar un error de operación y volver de forma prematura de la función de llamada:

response, err := client.GetObject(context.TODO()) if err != nil { log.Printf("GetObject error: %v", err) return }

Para obtener más información sobre la gestión de errores, incluida la forma de inspeccionar tipos de error específicos, consulte TODO.

Respuestas con io.ReadCloser

Algunas operaciones de la API devuelven una estructura de respuesta que contiene un miembro de salida que es io.ReadCloser. Este será el caso de las operaciones de la API que expongan algún elemento de su salida en el cuerpo de la propia respuesta HTTP.

Por ejemplo, la operación GetObject de Amazon S3 devuelve una respuesta cuyo miembro Body es un objeto io.ReadCloser para acceder a la carga útil del objeto.

aviso

SIEMPRE DEBE implementar Close() en cualquier miembro de salida io.ReadCloser, independientemente de si ha consumido su contenido. De lo contrario, se pueden filtrar recursos y crear posibles problemas al leer los cuerpos de las respuestas en operaciones a las que se llame más adelante.

resp, err := s3svc.GetObject(context.TODO(), &s3.GetObjectInput{...}) if err != nil { // handle error return } // Make sure to always close the response Body when finished defer resp.Body.Close() decoder := json.NewDecoder(resp.Body) if err := decoder.Decode(&myStruct); err != nil { // handle error return }

Metadatos de respuesta

Todas las estructuras de salida de las operaciones de servicio incluyen un miembro ResultMetadata de tipo middleware.Metadata. El middleware del SDK utiliza middleware.Metadata para proporcionar información adicional a partir de una respuesta del servicio no modelada por este. Esto incluye metadatos como el valor RequestID. Por ejemplo, para recuperar el valor RequestID asociado a una respuesta del servicio para ayudar a AWS Support a solucionar los problemas de una solicitud:

import "fmt" import "log" import "github.com/aws/aws-sdk-go-v2/aws/middleware" import "github.com/aws/aws-sdk-go-v2/service/s3" // .. resp, err := client.GetObject(context.TODO(), &s3.GetObjectInput{ // ... }) if err != nil { log.Printf("error: %v", err) return } requestID, ok := middleware.GetRequestIDMetadata(resp.ResultMetadata) if !ok { fmt.Println("RequestID not included with request") } fmt.Printf("RequestID: %s\n", requestID)

Uso simultáneo de clientes de servicio

Puede crear rutinas de Go que utilicen el mismo cliente de servicio de forma simultánea para enviar varias solicitudes. Un cliente de servicio se puede usar con tantas rutinas de Go como desee.

En el siguiente ejemplo, se utiliza un cliente de servicio Amazon S3 en varias rutinas de Go. En este ejemplo se cargan dos objetos en un bucket de Amazon S3 de forma simultánea.

import "context" import "log" import "strings" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) type result struct { Output *s3.PutObjectOutput Err error } results := make(chan result, 2) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() output, err := client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("foo"), Body: strings.NewReader("foo body content"), }) results <- result{Output: output, Err: err} }() go func() { defer wg.Done() output, err := client.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String("amzn-s3-demo-bucket"), Key: aws.String("bar"), Body: strings.NewReader("bar body content"), }) results <- result{Output: output, Err: err} }() wg.Wait() close(results) for result := range results { if result.Err != nil { log.Printf("error: %v", result.Err) continue } fmt.Printf("etag: %v", aws.ToString(result.Output.ETag)) }

Uso de paginadores de operaciones

Por lo general, al recuperar una lista de elementos, es posible que tenga que comprobar la estructura de salida en busca de un token o marcador para confirmar si el servicio de AWS ha devuelto todos los resultados de su solicitud. Si el token o marcador está presente, se utiliza para solicitar la siguiente página de resultados. En lugar de administrar estos tokens o marcadores, puede usar los tipos de paginadores disponibles del paquete de servicios.

Hay ayudantes de paginadores disponibles para las operaciones de servicio admitidas y los encontrará en el paquete Go del cliente de servicio. A fin de crear un paginador para una operación admitida, utilice la función New<OperationName>Paginator. Las funciones de creación de paginadores toman el valor de Client del servicio, los parámetros de entrada <OperationName>Input de la operación y un conjunto opcional de argumentos funcionales que permiten configurar otros ajustes opcionales del paginador.

El tipo de paginador de operaciones devuelto ofrece una forma cómoda de recorrer en iteración una operación paginada hasta que llegue a la última página o encuentre los elementos que buscaba la aplicación. Un tipo de paginador tiene dos métodos: HasMorePages y NextPage. HasMorePages devuelve un valor booleano de true si no se ha recuperado la primera página o si hay páginas adicionales disponibles para su recuperación mediante la operación. Para recuperar la primera página de la operación o páginas posteriores, se debe llamar a la operación NextPage. NextPage toma context.Context y devuelve la salida de la operación y cualquier error correspondiente. Del mismo modo que se hace con los parámetros devueltos del método de operación de cliente, el error devuelto debe comprobarse siempre antes de intentar usar la estructura de respuesta devuelta. Consulte Gestión de respuestas de operación.

En el siguiente ejemplo se utiliza el paginador ListObjectsV2 para enumerar hasta tres páginas de claves de objeto de la operación ListObjectV2. Cada página consta de un máximo de diez claves, que se define mediante la opción de paginador Limit.

import "context" import "log" import "github.com/aws/aws-sdk-go-v2/config" import "github.com/aws/aws-sdk-go-v2/aws" import "github.com/aws/aws-sdk-go-v2/service/s3" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := s3.NewFromConfig(cfg) params := &s3.ListObjectsV2Input{ Bucket: aws.String("amzn-s3-demo-bucket"), } paginator := s3.NewListObjectsV2Paginator(client, params, func(o *s3.ListObjectsV2PaginatorOptions) { o.Limit = 10 }) pageNum := 0 for paginator.HasMorePages() && pageNum < 3 { output, err := paginator.NextPage(context.TODO()) if err != nil { log.Printf("error: %v", err) return } for _, value := range output.Contents { fmt.Println(*value.Key) } pageNum++ }

Al igual que sucede con el método de operación de cliente, se pueden modificar opciones de cliente como la región de la solicitud. Para ello, se proporcionan uno o varios argumentos funcionales a NextPage. Para obtener más información sobre cómo reemplazar las opciones de cliente al llamar a una operación, consulte Reemplazo de opciones de cliente para la llamada a operaciones.

Uso de esperadores

Al interactuar con las API de AWS que son asíncronas, a menudo es necesario esperar a que un recurso concreto esté disponible para poder realizar más acciones en este.

Por ejemplo, la API CreateTable de Amazon DynamoDB devuelve una respuesta inmediata con el valor CREATING para TableStatus y el usuario no puede invocar operaciones de lectura o escritura hasta que el estado de la tabla haya cambiado a ACTIVE.

Escribir lógica para sondear continuamente el estado de la tabla puede resultar engorroso y aumentar la probabilidad de errores. Los esperadores contribuyen a simplificar este proceso y son API sencillas que gestionan la tarea de sondeo de forma automática.

Por ejemplo, puede utilizar esperadores para sondear si se ha creado una tabla de DynamoDB y si está lista para una operación de escritura.

import "context" import "fmt" import "log" import "time" 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/dynamodb" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := dynamodb.NewFromConfig(cfg) // we create a waiter instance by directly passing in a client // that satisfies the waiters client Interface. waiter := dynamodb.NewTableExistsWaiter(client) // params is the input to api operation used by the waiter params := &dynamodb.DescribeTableInput { TableName: aws.String("test-table") } // maxWaitTime is the maximum wait time, the waiter will wait for // the resource status. maxWaitTime := 5 * time.Minutes // Wait will poll until it gets the resource status, or max wait time // expires. err := waiter.Wait(context.TODO(), params, maxWaitTime) if err != nil { log.Printf("error: %v", err) return } fmt.Println("Dynamodb table is now ready for write operations")

Reemplazo de la configuración de un esperador

De forma predeterminada, el SDK utiliza el valor de retraso mínimo y retraso máximo configurado con valores óptimos definidos por los servicios de AWS para diferentes API. Puede reemplazar la configuración del esperador; para ello, proporcione opciones funcionales durante el proceso de creación de este o al invocar una operación de esperador.

Por ejemplo, para reemplazar la configuración del esperador durante el proceso de creación de este:

import "context" import "fmt" import "log" import "time" 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/dynamodb" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := dynamodb.NewFromConfig(cfg) // we create a waiter instance by directly passing in a client // that satisfies the waiters client Interface. waiter := dynamodb.NewTableExistsWaiter(client, func (o *dynamodb.TableExistsWaiterOptions) { // override minimum delay to 10 seconds o.MinDelay = 10 * time.Second // override maximum default delay to 300 seconds o.MaxDelay = 300 * time.Second })

La función Wait de cada esperador también incluye opciones funcionales. Al igual que en el ejemplo anterior, puede reemplazar la configuración del esperador por solicitud Wait.

// params is the input to api operation used by the waiter params := &dynamodb.DescribeTableInput { TableName: aws.String("test-table") } // maxWaitTime is the maximum wait time, the waiter will wait for // the resource status. maxWaitTime := 5 * time.Minutes // Wait will poll until it gets the resource status, or max wait time // expires. err := waiter.Wait(context.TODO(), params, maxWaitTime, func (o *dynamodb.TableExistsWaiterOptions) { // override minimum delay to 5 seconds o.MinDelay = 5 * time.Second // override maximum default delay to 120 seconds o.MaxDelay = 120 * time.Second }) if err != nil { log.Printf("error: %v", err) return } fmt.Println("Dynamodb table is now ready for write operations")

Reemplazo avanzado de la configuración de un esperador

También puede personalizar el comportamiento predeterminado del esperador mediante una función reintentable personalizada. Las opciones específicas del esperador también proporcionan APIOptions para personalizar los middlewares de las operaciones.

Por ejemplo, para configurar reemplazos avanzados del esperador:

import "context" import "fmt" import "log" import "time" 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/dynamodb" import "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" // ... cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Printf("error: %v", err) return } client := dynamodb.NewFromConfig(cfg) // custom retryable defines if a waiter state is retryable or a terminal state. // For example purposes, we will configure the waiter to not wait // if table status is returned as `UPDATING` customRetryable := func(ctx context.Context, params *dynamodb.DescribeTableInput, output *dynamodb.DescribeTableOutput, err error) (bool, error) { if output.Table != nil { if output.Table.TableStatus == types.TableStatusUpdating { // if table status is `UPDATING`, no need to wait return false, nil } } } // we create a waiter instance by directly passing in a client // that satisfies the waiters client Interface. waiter := dynamodb.NewTableExistsWaiter(client, func (o *dynamodb.TableExistsWaiterOptions) { // override the service defined waiter-behavior o.Retryable = customRetryable })