Como usar o AWS SDK para Go - AWS SDK para Go v2

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Como usar o AWS SDK para Go

Aprenda maneiras comuns e recomendadas de programar com o AWS SDK para Go em suas aplicações.

Construir um cliente de serviço

Os clientes de serviço podem ser construídos usando as funções New ou NewFromConfig disponíveis no pacote Go do cliente de serviço. Cada função retornará um tipo de estrutura Client contendo os métodos para invocar as APIs de serviço. New e NewFromConfig fornecem o mesmo conjunto de opções configuráveis para construir um cliente de serviço, mas fornecem padrões de construção ligeiramente diferentes, que veremos nas seções a seguir.

NewFromConfig

A função NewFromConfig fornece uma interface consistente para construir clientes de serviço usando o aws.Config. Um aws.Config pode ser carregado usando config.LoadDefaultConfig. Consulte mais informações sobre como construir um aws.Config em Configurar o SDK. O exemplo a seguir mostra como construir um cliente de serviço do Amazon S3 usando a função aws.Config e 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)

Substituição de configuração

NewFromConfig pode usar um ou mais argumentos funcionais que podem alterar a estrutura Options de configuração de um cliente. Isso permite fazer substituições específicas, como alterar a região ou modificar opções específicas do serviço, como a opção UseAccelerate do Amazon S3. Por exemplo:

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 })

As substituições no valor Options do cliente são determinadas pela ordem em que os argumentos funcionais são fornecidos para NewFromConfig.

Novo

nota

New é considerada uma forma mais avançada de construção de clientes. Recomendamos que você use NewFromConfig para a construção do cliente, já que assim é possível fazer a construção usando a estrutura aws.Config. Isso elimina a necessidade de construir uma instância de estrutura Options para cada cliente de serviço que a aplicação exige.

A função New é um construtor de cliente que fornece uma interface para construir clientes usando somente a estrutura Options de pacotes do cliente para definir as opções de configuração do cliente. Por exemplo, para construir um cliente do Amazon S3 usando 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, "")), })

Substituição de configuração

New pode usar um ou mais argumentos funcionais que podem alterar a estrutura Options de configuração de um cliente. Isso permite fazer substituições específicas, como alterar a região ou modificar opções específicas do serviço, como a opção UseAccelerate do Amazon S3. Por exemplo:

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 })

As substituições no valor Options do cliente são determinadas pela ordem em que os argumentos funcionais são fornecidos para New.

Como chamar operações de serviço

Depois de criar uma instância de cliente de serviço, você poderá usá-la para chamar operações de serviço. Por exemplo, para chamar a operação GetObject do Amazon S3:

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

Quando você chama uma operação de serviço, o SDK valida de forma síncrona a entrada, serializa a solicitação, a assina com suas credenciais, a envia para a AWS e, então, desserializa uma resposta ou um erro. Na maioria dos casos, você pode chamar operações de serviço diretamente. Cada método cliente da operação de serviço retornará uma estrutura de resposta da operação e um tipo de interface de erro. Você deve sempre verificar o tipo error para determinar se ocorreu um erro antes de tentar acessar a estrutura de resposta da operação de serviço.

Como passar parâmetros para uma operação de serviço

Cada método de operação de serviço usa um valor context.Context para definir prazos de solicitação que serão cumpridos pelo SDK. Além disso, cada operação de serviço usará uma estrutura <OperationName>Input encontrada no respectivo pacote de Go do serviço. Você passa os parâmetros de entrada da API usando a estrutura de entrada da operação.

As estruturas de entrada de operação podem ter parâmetros de entrada, como os tipos padrão do Go de numéricos, boolianos, strings, mapas e listas. Em operações de API mais complexas, um serviço pode ter uma modelagem mais complexa dos parâmetros de entrada. Esses outros tipos, como estruturas específicas do serviço e valores de enumeração, são encontrados no pacote types de Go do serviço.

Além disso, os serviços podem distinguir entre o valor padrão de um tipo Go e se o valor foi definido ou não pelo usuário. Nesses casos, os parâmetros de entrada podem exigir que você passe uma referência de ponteiro para o tipo em questão. Para tipos padrão do Go, como numéricos, boolianos e strings, existem funções de conveniência <Type> e From<Type> disponíveis na AWS para facilitar essa conversão. Por exemplo, aws.String pode ser usado para converter um tipo string em *string para parâmetros de entrada que exigem um ponteiro para uma string. Inversamente, aws.ToString pode ser usado para transformar *string em string, oferecendo proteção contra a desreferência de um ponteiro nulo. As funções To<Type> são úteis ao lidar com as respostas de serviço.

Vamos ver um exemplo de como podemos usar um cliente do Amazon S3 para chamar a API GetObject e construir nossa entrada usando o pacote types e os auxiliares 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, })

Substituir as opções do cliente para a chamada de operação

Da mesma forma que as opções de operação do cliente podem ser modificadas durante a construção de um cliente usando argumentos funcionais, as opções do cliente podem ser modificadas no momento em que o método de operação é chamado, fornecendo um ou mais argumentos funcionais ao método de operação de serviço. Essa ação é segura para simultaneidade e não afeta outras operações simultâneas no cliente.

Por exemplo, para substituir a região “us-west-2” do cliente 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" })

Como gerenciar respostas de operações

Cada operação de serviço tem uma estrutura de saída associada que contém os membros da resposta da operação do serviço. A estrutura de saída segue o seguinte padrão de nomenclatura <OperationName>Output. Algumas operações podem não ter membros definidos para a saída da operação. Depois de chamar uma operação de serviço, o tipo de argumento error de retorno deve sempre ser verificado para determinar se ocorreu um erro ao invocar a operação de serviço. Os erros retornados podem variar de erros de validação de entrada do lado do cliente até respostas de erro do lado do serviço retornadas ao cliente. A estrutura de saída da operação não deve ser acessada no caso de um erro diferente de nulo ser retornado pelo cliente.

Por exemplo, para registrar um erro de operação e retornar prematuramente da função de chamada:

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

Consulte mais informações sobre como lidar com erros, incluindo como inspecionar tipos de erro específicos em TODO

Respostas com io.ReadCloser

Algumas operações de API retornam uma estrutura de resposta que contém um membro de saída que é um io.ReadCloser. Esse é o caso das operações de API que expõem algum elemento de sua saída no corpo da própria resposta HTTP.

Por exemplo, a operação GetObject do Amazon S3 retorna uma resposta cujo membro Body é um io.ReadCloser para acessar a carga útil do objeto.

Atenção

Você DEVE SEMPRE Close() qualquer membro de saída io.ReadCloser, independentemente de ter consumido ou não seu conteúdo. A falha em fazer isso pode causar vazamento de recursos e gerar problemas ao ler corpos de resposta em operações que forem chamadas no futuro.

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 }

Metadados de resposta

Todas as estruturas de saída da operação de serviço incluem um membro ResultMetadata do tipo middleware.Metadata. middleware.Metadata é usado pelo middleware do SDK para fornecer mais informações de uma resposta de serviço que não é modelada pelo serviço. Isso inclui metadados como RequestID. Por exemplo, para recuperar o RequestID associado a uma resposta de serviço para ajudar o AWS Support a solucionar uma solicitação:

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)

Usar clientes de serviço simultaneamente

Você pode criar goroutines que usam simultaneamente o mesmo cliente de serviço para enviar várias solicitações. É possível usar um cliente de serviço com quantas goroutines quiser.

No exemplo a seguir, um cliente de serviço do Amazon S3 é usado em várias goroutines. Esse exemplo faz upload simultaneamente de dois objetos em um bucket do Amazon S3.

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)) }

Usar paginadores de operação

Normalmente, quando você recupera uma lista de itens, talvez seja necessário verificar a estrutura de saída em busca de um token ou marcador para confirmar se o serviço AWS retornou todos os resultados da solicitação. Caso o token ou marcador esteja presente, use-o para solicitar a próxima página de resultados. Em vez de gerenciar esses tokens ou marcadores, você pode usar os tipos de paginadores disponíveis no pacote de serviços.

Os auxiliares do paginador estão disponíveis para operações de serviço compatíveis e podem ser encontrados no pacote Go do cliente de serviço. Para construir um paginador para uma operação compatível, use a função New<OperationName>Paginator. As funções de construção do paginador usam o serviço Client, os parâmetros de entrada <OperationName>Input da operação e um conjunto opcional de argumentos funcionais, permitindo que você defina outras configurações opcionais do paginador.

O tipo de paginador de operação retornado fornece uma maneira conveniente de iterar sobre uma operação paginada até chegar à última página ou encontrar os itens que a aplicação estava procurando. Um tipo de paginador tem dois métodos: HasMorePages e NextPage. HasMorePages retornará um valor booliano de true se a primeira página não tiver sido recuperada ou se páginas adicionais estiverem disponíveis para recuperação usando a operação. Para recuperar a primeira página ou as páginas subsequentes da operação, a operação NextPage deve ser chamada. NextPage usa context.Context e retorna a saída da operação e qualquer erro correspondente. Assim como os parâmetros de retorno do método de operação do cliente, o erro de retorno sempre deve ser verificado antes de tentar usar a estrutura de resposta retornada. Consulte Como gerenciar respostas de operações.

O exemplo a seguir usa o paginador ListObjectsV2 para listar até três páginas de chaves de objeto da operação ListObjectV2. Cada página consiste em até 10 chaves, definidas pela opção Limit do paginador.

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++ }

Semelhante ao método de operação do cliente, as opções do cliente, como a região da solicitação, podem ser modificadas fornecendo um ou mais argumentos funcionais para NextPage. Consulte mais informações sobre como substituir as opções do cliente ao chamar uma operação em Substituir as opções do cliente para a chamada de operação.

Usar agentes de espera

Ao interagir com APIs assíncronas da AWS, muitas vezes você precisa esperar que determinado recurso esteja disponível para realizar outras ações nele.

Por exemplo, a API CreateTable do Amazon DynamoDB retorna imediatamente com um TableStatus de CREATING, e você não pode invocar operações de leitura ou gravação até que o status da tabela mude para ACTIVE.

Escrever uma lógica para pesquisar continuamente o status da tabela pode ser uma tarefa complicada e propensa a erros. Os waiters ajudam a eliminar a complexidade e são APIs simples que lidam com a tarefa de pesquisa para você.

Por exemplo, é possível usar waiters para pesquisar se uma tabela do DynamoDB foi criada e está pronta para uma operação de gravação.

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")

Substituir configuração do waiter

Por padrão, o SDK usa o atraso mínimo e o valor máximo de atraso configurados com valores ideais definidos pelos serviços da AWS para diferentes APIs. É possível substituir a configuração do waiter fornecendo opções funcionais durante a construção do waiter ou ao invocar uma operação de waiter.

Por exemplo, para substituir a configuração do waiter durante a construção do waiter

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 })

A função Wait em cada waiter também inclui opções funcionais. Assim como no exemplo acima, é possível substituir a configuração do waiter por solicitação 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")

Substituições avançadas da configuração do waiter

Além disso, é possível personalizar o comportamento padrão do waiter fornecendo uma função personalizada que pode ser repetida. As opções específicas do waiter também oferecem APIOptions personalizar middlewares da operação.

Por exemplo, para configurar substituições avançadas do waiter.

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 })