

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用适用于 Go 的 AWS SDK
<a name="using"></a>

了解在应用程序中使用适用于 Go 的 AWS SDK 进行编程的常用和推荐方法。

**Topics**
+ [构造服务客户端](#constructing-a-service-client)
+ [调用服务操作](#calling-service-operations)
+ [同时使用服务客户端](#concurrently-using-service-clients)
+ [使用操作分页器](#using-operation-paginators)
+ [使用 Waiter](#using-waiters)
+ [处理 SDK 中的错误](handle-errors.md)

## 构造服务客户端
<a name="constructing-a-service-client"></a>

 可以使用服务客户端 Go 程序包中提供的 `New` 或 `NewFromConfig` 函数来构造服务客户端。每个函数都将返回一个 `Client` 结构类型，其中包含用于调用服务 API 的方法。`New` 和 `NewFromConfig` 均为构造服务客户端提供了相同的可配置选项集，但提供的构造模式略有不同，我们将在以下几节进行介绍。

### NewFromConfig
<a name="newfromconfig"></a>

 `NewFromConfig` 函数为使用 [aws.Config](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws#Config) 构造服务客户端提供了一致的接口。可以使用 [config.LoadDefaultConfig](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/config#LoadDefaultConfig) 加载 `aws.Config`。有关构造 `aws.Config` 的更多信息，请参阅[配置 SDK](configure-gosdk.md)。以下示例演示了如何使用 `aws.Config` 和 `NewFromConfig` 函数构造 Amazon S3 服务客户端：

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

#### 覆盖配置
<a name="overriding-configuration"></a>

 `NewFromConfig` 可以采用一个或多个函数参数，这些参数可以更改客户端的配置 `Options` 结构。这样便可进行特定的覆盖，例如更改区域或修改特定于服务的选项，例如 Amazon S3 `UseAccelerate` 选项。例如：

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

 对客户端 `Options` 值的覆盖由提供给 `NewFromConfig` 的函数参数的顺序决定。

### New
<a name="new"></a>

**注意**  
 `New` 被视为一种更高级的客户端构造形式。建议您使用 `NewFromConfig` 构造客户端，因为其允许使用 `aws.Config` 结构进行构造。这样就无需为应用程序所需的每个服务客户端构造 `Options` 结构实例。

 `New` 函数是客户端构造函数，它为构造客户端提供了一个接口，仅使用客户端程序包 `Options` 结构来定义客户端的配置选项。例如，使用 `New` 构造 Amazon S3 客户端：

```
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, "")),
    })
```

#### 覆盖配置
<a name="overriding-configuration-1"></a>

 `New` 可以采用一个或多个函数参数，这些参数可以更改客户端的配置 `Options` 结构。这样便可进行特定的覆盖，例如更改区域或修改特定于服务的选项，例如 Amazon S3 `UseAccelerate` 选项。例如：

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

 对客户端 `Options` 值的覆盖由提供给 `New` 的函数参数的顺序决定。

## 调用服务操作
<a name="calling-service-operations"></a>

 拥有服务客户端实例后，就可以用它来调用服务的操作。例如，调用 Amazon S3 `GetObject` 操作：

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

 当您调用服务操作时，SDK 会同步验证输入，对请求进行序列化，使用您的凭证对其进行签名，将其发送到 AWS，然后对响应或错误进行反序列化。大多数情况下，您可以直接调用服务操作。每种服务操作客户端方法都将返回一个操作响应结构和一个错误接口类型。尝试访问服务操作的响应结构之前，应始终检查 `error` 类型以确定是否出现了错误。

### 向服务操作传递参数
<a name="passing-parameters-to-a-service-operation"></a>

 每种服务操作方法都采用一个 [context.Context](https://golang.org/pkg/context/#Context) 值，该值可用于设置 SDK 将遵守的请求截止日期。此外，每项服务操作都将采用在服务相应的 Go 程序包中找到的 `<OperationName>Input` 结构。您可以使用操作输入结构传入 API 输入参数。

 操作输入结构可以包含输入参数，例如标准 Go 数字、布尔值、字符串、映射和列表类型。在更复杂的 API 操作中，一项服务可能会对输入参数进行更复杂的建模。上述其他类型（例如特定于服务的结构和枚举值）可在服务的 `types` Go 程序包中找到。

 此外，服务可能会区分某种 Go 类型的默认值以及该值是否由用户设置。在这些情况下，输入参数可能需要您传递一个指向相关类型的指针引用。对于数字、布尔值和字符串等标准 Go 类型，[aws](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws) 中提供了一些 `<Type>` 和 `From<Type>` 便利函数来简化这种转换。例如，对于需要指向字符串的指针的输入参数，[aws.String](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws#String) 可用于将 `string` 转换为 `*string` 类型。相反，[aws.ToString](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws#ToString) 可用于将 `*string` 转换为 `string`，同时可防止空指针取消引用。在处理服务响应时，`To<Type>` 函数很有用。

 我们来看一个示例，该示例介绍如何使用 Amazon S3 客户端调用 `GetObject` API，并使用 `types` 程序包和 `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,
    })
```

### 覆盖客户机选项以进行操作调用
<a name="overriding-client-options"></a>

 与在构造客户端时使用函数参数修改客户端操作选项的方式类似，在调用操作方法时，可以通过向服务操作方法提供一个或多个函数参数来修改客户端选项。此操作具有并发安全性，不会影响客户端上的其他并发操作。

 例如，将客户端区域从“us-west-2”改写为“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"
    })
```

### 处理操作响应
<a name="handling-operation-responses"></a>

 每项服务操作都有一个关联的输出结构，其中包含服务的操作响应成员。输出结构遵循以下命名模式 `<OperationName>Output`。某些操作可能没有为其操作输出定义成员。调用服务操作后，应始终检查返回 `error` 参数类型，以确定在调用服务操作时是否出现了错误。返回的错误可能涵盖客户端输入验证错误，以及服务端返回给客户端的错误响应。如果客户端返回非 nil 错误，则不应访问操作的输出结构。

 例如，记录操作错误并提前从调用函数返回：

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

 有关错误处理的更多信息，包括如何检查特定错误类型，请参阅 TODO 

#### 包含 `io.ReadCloser` 的响应
<a name="responses-with-ioreadcloser"></a>

 某些 API 操作会返回一个响应结构，其中包含一个输出成员，即 `io.ReadCloser`。对于在 HTTP 响应本身正文中提供某些输出元素的 API 操作，将会出现这种情况。

 例如，Amazon S3 `GetObject` 操作会返回一个响应，其 `Body` 成员是用于访问对象有效载荷的 `io.ReadCloser`。

**警告**  
 您必须始终 `Close()` 任何 `io.ReadCloser` 输出成员，无论是否使用了其内容。如果不这样做，可能会导致资源泄漏，并有可能导致在未来调用的操作期间读取响应正文时出现问题。

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

#### 响应元数据
<a name="response-metadata"></a>

 所有服务操作输出结构都包含一个 [middleware.Metadata](https://pkg.go.dev/github.com/aws/smithy-go/middleware#Metadata) 类型的 `ResultMetadata` 成员。SDK 中间件使用 `middleware.Metadata` 提供服务响应中并非由服务建模的其他信息。这包括 `RequestID` 之类的元数据。例如，检索与服务响应关联的 `RequestID` 以帮助 AWS Support 对请求进行故障排除：

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

## 同时使用服务客户端
<a name="concurrently-using-service-clients"></a>

 您可以创建一些 goroutine，以便同时使用同一服务客户端发送多个请求。您可以根据需要，使用包含任意数量 goroutine 的服务客户端。

 在以下示例中，多个 goroutine 中使用了 Amazon S3 服务客户端。此示例同时将两个数据上传到 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))
    }
```

## 使用操作分页器
<a name="using-operation-paginators"></a>

 通常，在检索项目列表时，可能需要检查输出结构中是否有令牌或标记，以确认 AWS 服务是否返回了您请求的所有结果。如果存在令牌或标记，则将其用于请求下一页结果。您可以使用服务包中可用的分页器类型，而无需管理这些令牌或标记。

 分页器辅助标记可用于受支持的服务操作，可以在服务客户端的 Go 程序包中找到。要为受支持的操作构造分页器，请使用 `New<OperationName>Paginator` 函数。分页器构造函数采用服务 `Client`、操作的 `<OperationName>Input` 输入参数和一组可选的函数参数，使您可以配置其他可选的分页器设置。

 返回的操作分页器类型提供了一种便捷的方式来迭代分页操作，直至到达最后一页，或者直至找到应用程序正在搜索的项目。分页器类型有两种方法：`HasMorePages` 和 `NextPage`。如果尚未检索第一页，或者还有其他页面可以使用该操作进行检索，则 `HasMorePages` 返回布尔值 `true`。要检索操作的第一页或后续页面，必须调用 `NextPage` 操作。`NextPage` 获取 `context.Context` 并返回操作输出和任何相应的错误。与客户端操作方法返回参数一样，在尝试使用返回的响应结构之前，应始终检查返回错误。请参阅[处理操作响应](#handling-operation-responses)。

 以下示例使用 `ListObjectsV2` 分页器列出 `ListObjectV2` 操作中最多三页的对象键。每页最多包含 10 个键，具体数量由 `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++
    }
```

 与客户端操作方法类似，可以通过向 `NextPage` 提供一个或多个函数参数来修改诸如请求区域之类的客户端选项。有关在调用操作时覆盖客户端选项的更多信息，请参阅[覆盖客户机选项以进行操作调用](#overriding-client-options)。

## 使用 Waiter
<a name="using-waiters"></a>

 在与异步 AWS API 交互时，您通常需要等待某个特定资源可用之后，才能对其执行进一步的操作。

 例如，Amazon DynamoDB `CreateTable` API 会立即返回，并且 TableStatus 为 CREATING，在表状态转变为 `ACTIVE` 之前，您无法调用读取或写入操作。

 编写逻辑以持续轮询表状态可能非常繁琐且容易出错。Waiter 可以帮助您消除复杂性，它们是简单 API，可以为您处理轮询任务。

 例如，您可以使用 Waiter 来轮询 DynamoDB 表是否已创建并准备好进行写入操作。

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

### 覆盖 Waiter 配置
<a name="overriding-waiter-configuration"></a>

 默认情况下，SDK 使用由 AWS 服务为不同 API 定义的最佳值所配置的最小延迟值和最大延迟值。您可以通过在 Waiter 构造期间或在调用 Waiter 操作时，提供函数选项来覆盖 Waiter 配置。

 例如，在 Waiter 构造期间覆盖 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
    })
```

每个 Waiter 的 `Wait` 函数还包括函数选项。与上面的示例类似，您可以根据 `Wait` 请求覆盖 Waiter 配置。

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

### 高级 Waiter 配置覆盖
<a name="advanced-waiter-configuration-overrides"></a>

 此外，您还可以通过提供自定义的可重试函数来自定义 Waiter 的默认行为。特定于 Waiter 的选项还提供 `APIOptions` 以[自定义操作中间件](middleware.md#writing-a-custom-middleware)。

 例如，配置高级 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
    })
```

# 处理适用于 Go 的 AWS SDK V2 中的错误
<a name="handle-errors"></a>

 适用于 Go 的 AWS SDK 返回满足 Go `error` 接口类型的错误。您可以使用 `Error()` 方法获取 SDK 错误消息的格式化字符串，无需进行任何特殊处理。SDK 返回的错误可能会实现 `Unwrap` 方法。SDK 使用 `Unwrap` 方法为错误提供额外的上下文信息，同时提供对底层错误或错误链的访问权限。应将 `Unwrap` 方法与 [errors.As](https://golang.org/pkg/errors#As) 结合使用以处理展开错误链。

 调用一个可以返回 `error` 接口类型的函数或方法后，您的应用程序应检查是否出现了错误，这一点很重要。错误处理的最基本形式与以下示例类似：

```
if err != nil {
    // Handle error
    return
}
```

## 日志记录错误
<a name="logging-errors"></a>

 通常，最简单的错误处理形式是在返回或退出应用程序之前记录或打印错误消息。

```
import "log"

// ...

if err != nil {
    log.Printf("error: %s", err.Error())
    return
}
```

## 服务客户端错误
<a name="service-client-errors"></a>

 SDK 会将服务客户端返回的所有错误都用 [smithy.OperationError](https://pkg.go.dev/github.com/aws/smithy-go#OperationError) 错误类型进行封装。`OperationError` 提供与底层错误关联的服务名称和操作的上下文信息。这些信息对于采用集中式错误处理机制对一项或多项服务执行批量操作的应用程序非常有用。您的应用程序可以使用 `errors.As` 访问此 `OperationError` 元数据。

```
import "log"
import "github.com/aws/smithy-go"

// ...

if err != nil {
    var oe *smithy.OperationError
    if errors.As(err, &oe) {
        log.Printf("failed to call service: %s, operation: %s, error: %v", oe.Service(), oe.Operation(), oe.Unwrap())
    }
    return
}
```

### API 错误响应
<a name="api-error-responses"></a>

 服务操作可以返回已建模的错误类型以指示特定错误。这些建模类型可以与 `errors.As` 结合使用，以展开并确定操作失败是否是由特定错误引起。例如，如果同名存储桶已经存在，Amazon S3 `CreateBucket` 可能会返回 [BucketAlreadyExists](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3/types#BucketAlreadyExists) 错误。

 例如，检查错误是否为 `BucketAlreadyExists` 错误：

```
import "log"
import "github.com/aws/aws-sdk-go-v2/service/s3/types"

// ...

if err != nil {
    var bne *types.BucketAlreadyExists
    if errors.As(err, &bne) {
        log.Println("error:", bne)
    }
    return
}
```

 所有服务 API 响应错误都实现了 [smithy.APIError](https://pkg.go.dev/github.com/aws/smithy-go/#APIError) 接口类型。此接口可用于处理已建模或未建模的服务错误响应。此类型提供对服务返回的错误代码和消息的访问。此外，此类型还可指示错误是由客户端还是服务器引起（如果已知）。

```
import "log"
import "github.com/aws/smithy-go"

// ...

if err != nil {
    var ae smithy.APIError
    if errors.As(err, &ae) {
        log.Printf("code: %s, message: %s, fault: %s", ae.ErrorCode(), ae.ErrorMessage(), ae.ErrorFault().String())
    }
    return
}
```

## 检索请求标识符
<a name="retrieving-request-identifiers"></a>

 与 AWS Support 合作时，您可能需要提供请求标识符，以标识您正在尝试进行故障排除的请求。您可以使用 [http.ResponseError](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws/transport/http#ResponseError) 并使用 `ServiceRequestID()` 方法来检索与错误响应关联的请求标识符。

```
import "log"
import awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"

// ...

if err != nil {
    var re *awshttp.ResponseError
    if errors.As(err, &re) {
        log.Printf("requestID: %s, error: %v", re.ServiceRequestID(), re.Unwrap());
    }
    return
}
```

### Amazon S3 请求标识符
<a name="s3-request-identifiers"></a>

 Amazon S3 请求包含其他标识符，可用于帮助 AWS Support 对您的请求进行故障排除。您可以使用 [s3.ResponseError](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/s3#ResponseError) 并调用 `ServiceRequestID()` 和 `ServiceHostID()` 来检索请求 ID 和主机 ID。

```
import "log"
import "github.com/aws/aws-sdk-go-v2/service/s3"

// ...

if err != nil {
    var re s3.ResponseError
    if errors.As(err, &re) {
        log.Printf("requestID: %s, hostID: %s request failure", re.ServiceRequestID(), re.ServiceHostID());
    }
    return
}
```