

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Das AWS SDK für Rust verwenden
<a name="using"></a>

Lernen Sie gängige und empfohlene Methoden zur Verwendung von AWS SDK für Rust für die Arbeit mit AWS Diensten kennen.

**Topics**
+ [Serviceanfragen stellen](make-request.md)
+ [Bewährte Methoden](best-practices.md)
+ [Nebenläufigkeit](concurrency.md)
+ [Erstellen von Lambda-Funktionen](lambda.md)
+ [Vorsignierte erstellen URLs](presigned-urls.md)
+ [Fehlerbehandlung](error-handling.md)
+ [Paginierung](paginating.md)
+ [Komponententests](testing.md)
+ [Waiter](waiters.md)

# AWS-Service Anfragen mit dem AWS SDK für Rust stellen
<a name="make-request"></a>

 Für den programmgesteuerten Zugriff AWS-Services verwendet das AWS SDK für Rust jeweils eine Client-Struktur. AWS-Service Wenn Ihre Anwendung beispielsweise auf Amazon zugreifen muss EC2, erstellt Ihre Anwendung eine EC2 Amazon-Client-Struktur als Schnittstelle zu diesem Service. Anschließend verwenden Sie den Service-Client, um Anfragen an diesen zu stellen AWS-Service. 

Um eine Anfrage an einen zu stellen AWS-Service, müssen Sie zunächst einen Service-Client erstellen und [konfigurieren](configure.md). Für jeden Code, den AWS-Service Sie verwenden, gibt es eine eigene Kiste und einen eigenen Typ für die Interaktion mit ihm. Der Client stellt für jeden API-Vorgang, der vom Dienst verfügbar gemacht wird, eine Methode zur Verfügung. 

 AWS-Services Um mit dem AWS SDK für Rust zu interagieren, erstellen Sie einen dienstspezifischen Client, verwenden dessen API-Methoden mit fließender Verkettung im Builder-Stil und rufen Sie auf, um die Anfrage auszuführen. `send()`

Das `Client` macht eine Methode für jeden API-Vorgang verfügbar, der vom Dienst verfügbar gemacht wird. Der Rückgabewert jeder dieser Methoden ist ein „Fluent Builder“, bei dem verschiedene Eingaben für diese API durch Verkettung von Funktionsaufrufen im Builder-Stil hinzugefügt werden. Rufen Sie nach dem Aufrufen der Methoden des Dienstes auf, `send()` um eine zu erhalten [https://doc.rust-lang.org/nightly/core/future/trait.Future.html](https://doc.rust-lang.org/nightly/core/future/trait.Future.html), die entweder zu einer erfolgreichen Ausgabe oder zu einer führt. `SdkError` Weitere Informationen zu `SdkError` finden Sie unter [Umgang mit Fehlern im AWS SDK für Rust](error-handling.md). 

Das folgende Beispiel zeigt eine grundlegende Operation mit Amazon S3 zur Erstellung eines Buckets in `us-west-2` AWS-Region: 

```
let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;
  
let s3 = aws_sdk_s3::Client::new(&config);
  
let result = s3.create_bucket()
    // Set some of the inputs for the operation.
    .bucket("my-bucket")
    .create_bucket_configuration(
        CreateBucketConfiguration::builder()
            .location_constraint(aws_sdk_s3::types::BucketLocationConstraint::UsWest2)
            .build()
        )
    // send() returns a Future that does nothing until awaited.
    .send()
    .await;
```

Jede Service-Kiste enthält zusätzliche Module, die für API-Eingaben verwendet werden, wie zum Beispiel die folgenden: 
+ Das `types` Modul verfügt über Strukturen oder Aufzählungen, um komplexere strukturierte Informationen bereitzustellen.
+  Das `primitives` Modul verfügt über einfachere Typen zur Darstellung von Daten wie Datums- und Uhrzeitangaben oder binären Blobs.

 Eine detailliertere Organisation und Informationen zu Crate finden Sie in der [API-Referenzdokumentation](https://awslabs.github.io/aws-sdk-rust/) für den Service Crate. Zum Beispiel hat die `aws-sdk-s3` Kiste für den Amazon Simple Storage Service mehrere [Module](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/#modules). Zwei davon sind:
+ [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/types/index.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/types/index.html)
+ [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/primitives/index.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/primitives/index.html)

# Bewährte Methoden für die Verwendung von AWS SDK für Rust
<a name="best-practices"></a>

Im Folgenden finden Sie bewährte Methoden für die AWS SDK für Rust Verwendung von. 

## Verwenden Sie SDK-Clients nach Möglichkeit wieder
<a name="bp-reuseClient"></a>

Je nachdem, wie ein SDK-Client aufgebaut ist, kann die Erstellung eines neuen Clients dazu führen, dass jeder Client seine eigenen HTTP-Verbindungspools, Identitätscaches usw. beibehält. Wir empfehlen, einen Client gemeinsam zu nutzen oder zumindest gemeinsam `SdkConfig` zu nutzen, um den Aufwand einer teuren Ressourcenerstellung zu vermeiden. Alle SDK-Clients implementieren `Clone` die Aktualisierung in Form eines einzigen atomaren Referenzzählers. 

## API-Timeouts konfigurieren
<a name="bp-apiTimeouts"></a>

 Das SDK bietet Standardwerte für einige Timeout-Optionen wie Verbindungs-Timeout und Socket-Timeouts, jedoch nicht für API-Aufruf-Timeouts oder einzelne API-Aufrufversuche. Es empfiehlt sich, Timeouts sowohl für den einzelnen Versuch als auch für die gesamte Anfrage festzulegen. Auf diese Weise wird sichergestellt, dass Ihre Anwendung schnell und optimal fehlschlägt, wenn vorübergehende Probleme auftreten, die dazu führen können, dass Anforderungsversuche länger dauern können, oder bei schwerwiegenden Netzwerkproblemen. 

Weitere Informationen zur Konfiguration von Betriebs-Timeouts finden Sie unter. [Timeouts im AWS SDK für Rust konfigurieren](timeouts.md) 

# Parallelität in der AWS SDK für Rust
<a name="concurrency"></a>

Das bietet AWS SDK für Rust keine Parallelitätssteuerung, aber Benutzer haben viele Möglichkeiten, ihre eigenen zu implementieren.

## Bedingungen
<a name="conc-terms"></a>

Begriffe, die sich auf dieses Thema beziehen, sind leicht zu verwechseln, und einige Begriffe sind zu Synonymen geworden, obwohl sie ursprünglich unterschiedliche Konzepte darstellten. In diesem Leitfaden werden wir Folgendes definieren:
+  **Aufgabe**: Eine „Arbeitseinheit“, die Ihr Programm bis zum Abschluss ausführen wird oder versucht, es bis zum Abschluss auszuführen. 
+  **Sequentielles Rechnen**: Wenn mehrere Aufgaben nacheinander ausgeführt werden. 
+  **Gleichzeitiges Rechnen**: Wenn mehrere Aufgaben in überlappenden Zeiträumen ausgeführt werden.
+  **Parallelität**: Die Fähigkeit eines Computers, mehrere Aufgaben in beliebiger Reihenfolge auszuführen. 
+  **Multitasking**: Die Fähigkeit eines Computers, mehrere Aufgaben gleichzeitig auszuführen. 
+  **Race Condition**: Wenn sich das Verhalten Ihres Programms ändert, je nachdem, wann eine Aufgabe gestartet wird oder wie lange es dauert, eine Aufgabe zu bearbeiten. 
+  **Streit**: Konflikt über den Zugriff auf eine gemeinsam genutzte Ressource. Wenn zwei oder mehr Aufgaben gleichzeitig auf eine Ressource zugreifen wollen, befindet sich diese Ressource in einem „Konflikt“. 
+  **Deadlock**: Ein Zustand, in dem keine Fortschritte mehr erzielt werden können. Dies ist in der Regel der Fall, weil zwei Aufgaben die Ressourcen des jeweils anderen übernehmen wollen, aber keine Aufgabe ihre Ressourcen freigibt, bis die Ressource des anderen wieder verfügbar ist. Deadlocks führen dazu, dass ein Programm ganz oder teilweise nicht mehr reagiert. 

## Ein einfaches Beispiel
<a name="conc-simple"></a>

Unser erstes Beispiel ist ein sequentielles Programm. In späteren Beispielen werden wir diesen Code mithilfe von Parallelitätstechniken ändern. In `main()` späteren Beispielen wird dieselbe `build_client_and_list_objects_to_download()` Methode wiederverwendet und Änderungen daran vorgenommen. Führen Sie die folgenden Befehle aus, um Ihrem Projekt Abhängigkeiten hinzuzufügen:
+ `cargo add aws-sdk-s3`
+ `cargo add aws-config tokio --features tokio/full`

Die folgende Beispielaufgabe besteht darin, alle Dateien in einem Amazon Simple Storage Service-Bucket herunterzuladen:

1.  Beginnen Sie mit der Auflistung aller Dateien. Speichern Sie die Schlüssel in einer Liste. 

1.  Iterieren Sie die Liste und laden Sie nacheinander jede Datei herunter 

```
use aws_sdk_s3::{Client, Error};
const EXAMPLE_BUCKET: &str = "amzn-s3-demo-bucket";  // Update to name of bucket you own.

// This initialization function won't be reproduced in
// examples following this one, in order to save space.
async fn build_client_and_list_objects_to_download() -> (Client, Vec<String>) {
    let cfg = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
    let client = Client::new(&cfg);
    let objects_to_download: Vec<_> = client
        .list_objects_v2()
        .bucket(EXAMPLE_BUCKET)
        .send()
        .await
        .expect("listing objects succeeds")
        .contents()
        .into_iter()
        .flat_map(aws_sdk_s3::types::Object::key)
        .map(ToString::to_string)
        .collect();
         
    (client, objects_to_download)
}
```

```
#[tokio::main]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
    
    for object in objects_to_download {
        let res = client
            .get_object()
            .key(&object)
            .bucket(EXAMPLE_BUCKET)
            .send()
            .await
            .expect("get_object succeeds");
        let body = res.body.collect().await.expect("reading body succeeds").into_bytes();
        std::fs::write(object, body).expect("write succeeds");
    }
}
```

**Anmerkung**  
 In diesen Beispielen behandeln wir keine Fehler und gehen davon aus, dass der Beispiel-Bucket keine Objekte mit Schlüsseln enthält, die wie Dateipfade aussehen. Daher werden wir uns nicht mit der Erstellung verschachtelter Verzeichnisse befassen.

Aufgrund der Architektur moderner Computer können wir dieses Programm so umschreiben, dass es viel effizienter ist. Wir werden das in einem späteren Beispiel tun, aber zuerst wollen wir ein paar weitere Konzepte lernen.

## Eigentum und Wandelbarkeit
<a name="conc-ownership"></a>

Jeder Wert in Rust hat einen einzigen Besitzer. Wenn ein Besitzer den Gültigkeitsbereich verlässt, werden auch alle Werte, die er besitzt, gelöscht. Der Eigentümer kann entweder einen oder mehrere unveränderliche Verweise auf einen Wert **oder** eine einzelne veränderbare Referenz angeben. Der Rust-Compiler ist dafür verantwortlich, dass keine Referenz ihren Besitzer überlebt.

Zusätzliche Planung und Gestaltung sind erforderlich, wenn mehrere Aufgaben wechselseitig auf dieselbe Ressource zugreifen müssen. Beim sequentiellen Rechnen kann jede Aufgabe ohne Konflikte wechselseitig auf dieselbe Ressource zugreifen, da sie nacheinander in einer Reihenfolge ausgeführt werden. Beim gleichzeitigen Rechnen können Aufgaben jedoch in beliebiger Reihenfolge und gleichzeitig ausgeführt werden. Daher müssen wir mehr tun, um dem Compiler zu beweisen, dass mehrere veränderbare Referenzen unmöglich sind (oder zumindest abstürzen, falls sie auftreten).

Die Rust-Standardbibliothek bietet viele Tools, die uns dabei helfen. Weitere Informationen zu diesen Themen finden Sie im Buch [Variablen und Veränderlichkeit und](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) [Grundlegendes zur Eigentümerschaft](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html) in der Programmiersprache Rust.

## Weitere Begriffe\$1
<a name="conc-moreTerms"></a>

Im Folgenden finden Sie Listen von „Synchronisationsobjekten“. Insgesamt sind sie die Werkzeuge, die notwendig sind, um den Compiler davon zu überzeugen, dass unser Parallelprogramm nicht gegen Eigentumsregeln verstößt. 

 [https://doc.rust-lang.org/std/sync/index.html](https://doc.rust-lang.org/std/sync/index.html): 
+ [https://doc.rust-lang.org/std/sync/struct.Arc.html](https://doc.rust-lang.org/std/sync/struct.Arc.html): ***Ein** atomisch **R-Referenz-C-montierter** **A-Zeiger**.* Wenn Daten in einer Box verpackt sind`Arc`, können sie frei geteilt werden, ohne sich Sorgen machen zu müssen, dass ein bestimmter Eigentümer den Wert vorzeitig verliert. In diesem Sinne wird das Eigentum an dem Wert „geteilt“. Werte innerhalb eines `Arc` können nicht veränderbar sein, können aber innerlich [veränderbar](https://doc.rust-lang.org/reference/interior-mutability.html) sein. 
+ [https://doc.rust-lang.org/std/sync/struct.Barrier.html](https://doc.rust-lang.org/std/sync/struct.Barrier.html): Stellt sicher, dass mehrere Threads darauf warten, dass sie jeweils einen Punkt im Programm erreichen, bevor sie die Ausführung gemeinsam fortsetzen. 
+ [https://doc.rust-lang.org/std/sync/struct.Condvar.html](https://doc.rust-lang.org/std/sync/struct.Condvar.html): Eine *****Bedingungsvariable****, die die Möglichkeit bietet,* einen Thread zu blockieren, während auf das Eintreten eines Ereignisses gewartet wird. 
+ [https://doc.rust-lang.org/std/sync/struct.Mutex.html](https://doc.rust-lang.org/std/sync/struct.Mutex.html): Ein ***Mutual** **Exclusion-Mechanismus***, der sicherstellt, dass jeweils höchstens ein Thread auf einige Daten zugreifen kann. Im Allgemeinen sollte eine `Mutex` Sperre niemals an einer `.await` Stelle im Code versperrt werden. 

 [https://docs.rs/tokio/latest/tokio/sync/index.html](https://docs.rs/tokio/latest/tokio/sync/index.html): 

Sie AWS SDKs sollen zwar `async` -runtime-agnostisch sein, wir empfehlen jedoch die Verwendung von Synchronisationsobjekten für bestimmte Fälle. `tokio`
+ [https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html](https://docs.rs/tokio/latest/tokio/sync/struct.Mutex.html): Ähnlich wie die Standardbibliothek`Mutex`, aber mit etwas höheren Kosten. Im Gegensatz zum Standard `Mutex` kann dieser an einem beliebigen `.await` Punkt im Code gespeichert werden.
+ [https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html](https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html): Eine Variable, die verwendet wird, um den Zugriff mehrerer Aufgaben auf eine gemeinsame Ressource zu steuern.

## Wir haben unser Beispiel umgeschrieben, um es effizienter zu machen (Single-Thread-Parallelität)
<a name="conc_singleThread"></a>

Im folgenden modifizierten Beispiel verwenden wir die Methode, **ALLE [https://docs.rs/futures-util/latest/futures_util/future/fn.join_all.html](https://docs.rs/futures-util/latest/futures_util/future/fn.join_all.html)**`get_object`Anfragen gleichzeitig auszuführen. Führen Sie den folgenden Befehl aus, um Ihrem Projekt eine neue Abhängigkeit hinzuzufügen:
+ `cargo add futures-util`

```
#[tokio::main]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
        
    let get_object_futures = objects_to_download.into_iter().map(|object| {
        let req = client
            .get_object()
            .key(&object)
            .bucket(EXAMPLE_BUCKET);

        async {
            let res = req
                .send()
                .await
                .expect("get_object succeeds");
            let body = res.body.collect().await.expect("body succeeds").into_bytes();
           // Note that we MUST use the async runtime's preferred way
           // of writing files. Otherwise, this call would block,
           // potentially causing a deadlock.
            tokio::fs::write(object, body).await.expect("write succeeds");
        }
    });

    futures_util::future::join_all(get_object_futures).await;
}
```

 Dies ist der einfachste Weg, um von Parallelität zu profitieren, hat aber auch einige Probleme, die auf den ersten Blick vielleicht nicht offensichtlich sind:

1.  Wir erstellen alle Anforderungseingaben gleichzeitig. Wenn wir nicht genug Speicher haben, um alle `get_object` Anforderungseingaben aufzunehmen, tritt ein Zuweisungsfehler out-of-memory "" auf. 

1.  Wir erschaffen alle Zukünfte und erwarten sie gleichzeitig. Amazon S3 drosselt Anfragen, wenn wir versuchen, zu viel auf einmal herunterzuladen. 

Um diese beiden Probleme zu beheben, müssen wir die Anzahl der Anfragen, die wir gleichzeitig senden, begrenzen. Wir machen das mit einer `tokio` [Semaphore](https://docs.rs/tokio/latest/tokio/sync/struct.Semaphore.html):

```
use std::sync::Arc;
use tokio::sync::Semaphore;
const CONCURRENCY_LIMIT: usize = 50; 

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
    let concurrency_semaphore = Arc::new(Semaphore::new(CONCURRENCY_LIMIT));

    let get_object_futures = objects_to_download.into_iter().map(|object| {
        // Since each future needs to acquire a permit, we need to clone
        // the Arc'd semaphore before passing it in.
        let semaphore = concurrency_semaphore.clone();
        // We also need to clone the client so each task has its own handle.
        let client = client.clone();
        async move {
            let permit = semaphore
                .acquire()
                .await
                .expect("we'll get a permit if we wait long enough");
            let res = client
                .get_object()
                .key(&object)
                .bucket(EXAMPLE_BUCKET)
                .send()
                .await
                .expect("get_object succeeds");
            let body = res.body.collect().await.expect("body succeeds").into_bytes();
            tokio::fs::write(object, body).await.expect("write succeeds");
            std::mem::drop(permit);
        }
    });

    futures_util::future::join_all(get_object_futures).await;
}
```

Wir haben das potenzielle Problem mit der Speichernutzung behoben, indem wir die Anforderungserstellung in den `async` Block verschoben haben. Auf diese Weise werden Anfragen erst erstellt, wenn es Zeit ist, sie zu senden. 

**Anmerkung**  
 Wenn Sie den Speicher dafür haben, ist es möglicherweise effizienter, alle Ihre Anforderungseingaben auf einmal zu erstellen und sie im Speicher zu behalten, bis sie zum Senden bereit sind. Um dies zu versuchen, verschieben Sie die Erstellung von Anforderungseingaben außerhalb des `async` Blocks. 

 Wir haben auch das Problem behoben, dass zu viele Anfragen gleichzeitig gesendet wurden, indem wir die Anzahl der Anfragen im Flug auf beschränkt haben`CONCURRENCY_LIMIT`. 

**Anmerkung**  
 Der richtige Wert für `CONCURRENCY_LIMIT` ist für jedes Projekt anders. Wenn Sie Ihre eigenen Anfragen erstellen und senden, versuchen Sie, ihn so hoch wie möglich zu setzen, ohne dass Drosselungsfehler auftreten. Es ist zwar möglich, Ihr Parallelitätslimit auf der Grundlage des Verhältnisses zwischen erfolgreichen und gedrosselten Antworten, die ein Dienst zurücksendet, dynamisch zu aktualisieren, aber das ist aufgrund seiner Komplexität nicht Gegenstand dieses Handbuchs. 

## Wir haben unser Beispiel umgeschrieben, um es effizienter zu machen (Parallelität mit mehreren Threads)
<a name="conc-multiThread"></a>

 In den beiden vorherigen Beispielen haben wir unsere Anfragen gleichzeitig ausgeführt. Das ist zwar effizienter, als sie synchron auszuführen, aber wir können die Dinge noch effizienter gestalten, indem wir Multithreading verwenden. Um das zu tun`tokio`, müssen wir sie als separate Aufgaben starten. 

**Anmerkung**  
 Für dieses Beispiel müssen Sie die Multithread-Laufzeit `tokio` verwenden. Diese Laufzeit befindet sich hinter der `rt-multi-thread` Funktion. Und natürlich müssen Sie Ihr Programm auf einem Multicore-Computer ausführen. 

Führen Sie den folgenden Befehl aus, um Ihrem Projekt eine neue Abhängigkeit hinzuzufügen:
+ `cargo add tokio --features=rt-multi-thread`

```
// Set this based on the amount of cores your target machine has.
const THREADS: usize = 8; 

#[tokio::main(flavor = "multi_thread")]
async fn main() {
    let (client, objects_to_download) =
        build_client_and_list_objects_to_download().await;
    let concurrency_semaphore = Arc::new(Semaphore::new(THREADS));

    let get_object_task_handles = objects_to_download.into_iter().map(|object| {
        // Since each future needs to acquire a permit, we need to clone
        // the Arc'd semaphore before passing it in.
        let semaphore = concurrency_semaphore.clone();
        // We also need to clone the client so each task has its own handle.
        let client = client.clone();
        
        // Note this difference! We're using `tokio::task::spawn` to
        // immediately begin running these requests.
        tokio::task::spawn(async move {
            let permit = semaphore
                .acquire()
                .await
                .expect("we'll get a permit if we wait long enough");
            let res = client
                .get_object()
                .key(&object)
                .bucket(EXAMPLE_BUCKET)
                .send()
                .await
                .expect("get_object succeeds");
            let body = res.body.collect().await.expect("body succeeds").into_bytes();
            tokio::fs::write(object, body).await.expect("write succeeds");
            std::mem::drop(permit);
        })
    });

    futures_util::future::join_all(get_object_task_handles).await;
}
```

Die Aufteilung von Arbeit in Aufgaben kann komplex sein. Das Ausführen I/O (*Eingabe/Ausgabe*) ist in der Regel blockierend. Bei Laufzeiten kann es schwierig sein, die Anforderungen von Aufgaben mit langer Laufzeit und denen von Aufgaben mit kurzer Laufzeit in Einklang zu bringen. Für welche Laufzeit Sie sich auch entscheiden, lesen Sie unbedingt deren Empfehlungen, wie Sie Ihre Arbeit am effizientesten in Aufgaben unterteilen können. Empfehlungen zur `tokio` Laufzeit finden Sie unter [Modul `tokio::task`](https://docs.rs/tokio/latest/tokio/task/index.html).

## Debuggen von Apps mit mehreren Threads
<a name="conc-debug"></a>

Aufgaben, die gleichzeitig ausgeführt werden, können in beliebiger Reihenfolge ausgeführt werden. Daher können die Protokolle von gleichzeitigen Programmen sehr schwer zu lesen sein. Im SDK für Rust empfehlen wir die Verwendung des `tracing` Logging-Systems. Es kann Logs nach ihren spezifischen Aufgaben gruppieren, unabhängig davon, wann sie ausgeführt werden. Anleitungen finden Sie unter [Konfiguration und Verwendung der Protokollierung im AWS SDK für Rust](logging.md). 

Ein sehr nützliches Tool zur Identifizierung von Aufgaben, die blockiert wurden [https://github.com/tokio-rs/console](https://github.com/tokio-rs/console), ist ein Diagnose- und Debugging-Tool für asynchrone Rust-Programme. Indem Sie Ihr Programm instrumentieren und ausführen und dann die `tokio-console` App ausführen, können Sie eine Live-Ansicht der Aufgaben sehen, die Ihr Programm gerade ausführt. Diese Ansicht enthält hilfreiche Informationen wie die Zeit, die eine Aufgabe damit verbracht hat, auf gemeinsam genutzte Ressourcen zu warten, oder wie oft sie abgefragt wurde. 

# Lambda-Funktionen erstellen in der AWS SDK für Rust
<a name="lambda"></a>

Eine ausführliche Dokumentation zur Entwicklung von AWS Lambda Funktionen mit dem AWS SDK für Rust finden Sie unter [Erstellen von Lambda-Funktionen mit Rust](https://docs.aws.amazon.com/lambda/latest/dg/lambda-rust.html) im *AWS Lambda Entwicklerhandbuch*. Diese Dokumentation führt Sie durch die Verwendung von:
+ Die Rust Lambda Runtime-Client-Crate für Kernfunktionen,. [https://github.com/awslabs/aws-lambda-rust-runtime](https://github.com/awslabs/aws-lambda-rust-runtime) 
+ [Das empfohlene Befehlszeilentool für die Bereitstellung der Rust-Funktionsbinärdatei auf Lambda mit Cargo Lambda.](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html) 

Zusätzlich zu den angeleiteten Beispielen, die im *AWS Lambda Developer Guide* enthalten sind, gibt es auch Beispiele für einen Lambda-Rechner, die im [AWS SDK Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/lambda) unter GitHub verfügbar sind.

# Vorsignierte erstellen URLs mit dem AWS SDK für Rust
<a name="presigned-urls"></a>

 Sie können Anfragen für einige AWS API-Operationen vorab signieren, sodass ein anderer Aufrufer die Anfrage später verwenden kann, ohne seine eigenen Anmeldeinformationen angeben zu müssen. 

 Nehmen wir zum Beispiel an, dass Jane Zugriff auf ein Amazon Simple Storage Service (Amazon S3) -Objekt hat und den Objektzugriff vorübergehend mit Alejandro teilen möchte. Jane kann eine vorab signierte `GetObject` Anfrage zur Weitergabe an Alejandro generieren, sodass er das Objekt herunterladen kann, ohne Zugriff auf Janes Anmeldeinformationen zu benötigen oder eigene zu haben. Die von der vorsignierten URL verwendeten Anmeldeinformationen gehören Jane, da sie die AWS Benutzerin ist, die die URL generiert hat.

Weitere Informationen zu presigned URLs in Amazon S3 finden Sie unter [Working with presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html) im *Amazon Simple Storage Service-Benutzerhandbuch*.

## Grundlagen der Vorsignierung
<a name="presign-basics"></a>

 Das AWS SDK für Rust stellt eine `presigned()` Methode zur Operation Fluent-Builder bereit, die verwendet werden kann, um eine vorab signierte Anfrage abzurufen. 

 Das folgende Beispiel erstellt eine vorsignierte `GetObject` Anfrage für Amazon S3. Die Anfrage ist nach ihrer Erstellung 5 Minuten lang gültig. 

```
use std::time::Duration;
use aws_config::BehaviorVersion;
use aws_sdk_s3::presigning::PresigningConfig;

let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;

let s3 = aws_sdk_s3::Client::new(&config);

let presigned = s3.get_object()
    .presigned(
        PresigningConfig::builder()
            .expires_in(Duration::from_secs(60 * 5))
            .build()
            .expect("less than one week")
    )
    .await?;
```

 Die `presigned()` Methode gibt a zurück`Result<PresignedRequest, SdkError<E, R>>`. 

Die zurückgegebene `PresignedRequest` Datei enthält Methoden zum Abrufen der Komponenten einer HTTP-Anfrage, einschließlich der Methode, des URI und aller Header. All diese müssen an den Dienst gesendet werden, falls vorhanden, damit die Anfrage gültig ist. Viele vorab signierte Anfragen können jedoch allein durch die URI dargestellt werden. 

## Vorsignierung `POST` und Anfragen `PUT`
<a name="presign-post-put"></a>

 Viele Operationen, die vorsignierbar sind, benötigen nur eine URL und müssen als HTTP-Anfragen gesendet werden. `GET` Einige Operationen benötigen jedoch einen Hauptteil und müssen in einigen Fällen als HTTP `POST` - oder `PUT` HTTP-Anfrage zusammen mit Headern gesendet werden. Das Vorsignieren dieser Anfragen ist identisch mit dem Vorsignieren von `GET` Anfragen, das Aufrufen der vorab signierten Anfrage ist jedoch komplizierter. 

 Im Folgenden finden Sie ein Beispiel für die Vorsignierung einer Amazon S3 `PutObject` S3-Anfrage und [https://docs.rs/http/latest/http/request/struct.Request.html](https://docs.rs/http/latest/http/request/struct.Request.html)deren Konvertierung in eine, die mit einem HTTP-Client Ihrer Wahl gesendet werden kann. 

Um diese `into_http_1x_request()` Methode zu verwenden, fügen Sie die `http-1x` Funktion zu Ihrer `aws-sdk-s3` Kiste in Ihrer `Cargo.toml` Datei hinzu:

```
aws-sdk-s3 = { version = "1", features = ["http-1x"] }
```

Quelldatei:

```
let presigned = s3.put_object()
    .presigned(
        PresigningConfig::builder()
            .expires_in(Duration::from_secs(60 * 5))
            .build()
            .expect("less than one week")
    )
    .await?;


let body = "Hello AWS SDK for Rust";
let http_req = presigned.into_http_1x_request(body);
```

## Eigenständiger Unterzeichner
<a name="standalone-signer"></a>

**Anmerkung**  
Dies ist ein Anwendungsfall für Fortgeschrittene. Es wird für die meisten Benutzer nicht benötigt oder empfohlen.

Es gibt einige Anwendungsfälle, in denen es erforderlich ist, eine signierte Anfrage außerhalb des SDK für Rust-Kontextes zu erstellen. Dafür können Sie die [https://docs.rs/aws-sigv4/latest/aws_sigv4/index.html](https://docs.rs/aws-sigv4/latest/aws_sigv4/index.html)Kiste unabhängig vom SDK verwenden. 

 Das Folgende ist ein Beispiel zur Veranschaulichung der grundlegenden Elemente. Weitere Informationen finden Sie in der Crate-Dokumentation. 

Fügen Sie die `http` Kisten `aws-sigv4` und zu Ihrer `Cargo.toml` Datei hinzu:

```
[dependencies]
aws-sigv4 = "1"
http = "1"
```

Quelldatei:

```
use aws_smithy_runtime_api::client::identity::Identity;
use aws_sigv4::http_request::{sign, SigningSettings, SigningParams, SignableRequest};
use aws_sigv4::sign::v4;
use std::time::SystemTime;

// Set up information and settings for the signing.
// You can obtain credentials from `SdkConfig`.
let identity = Credentials::new(
    "AKIDEXAMPLE",
    "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
    None,
    None,
    "hardcoded-credentials").into();

let settings = SigningSettings::default();

let params = v4::SigningParams::builder()
    .identity(&identity)
    .region("us-east-1")
    .name("service")
    .time(SystemTime::now())
    .settings(settings)
    .build()?
    .into();

// Convert the HTTP request into a signable request.
let signable = SignableRequest::new(
    "GET",
    "https://some-endpoint.some-region.amazonaws.com",
    std::iter::empty(),
    SignableBody::UnsignedPayload
)?;

// Sign and then apply the signature to the request.
let (signing_instructions, _signature) = sign(signable, &params)?.into_parts();

let mut my_req = http::Request::new("...");
signing_instructions.apply_to_request_http1x(&mut my_req);
```

# Umgang mit Fehlern im AWS SDK für Rust
<a name="error-handling"></a>

Es ist wichtig zu verstehen, wie und wann die Fehler AWS SDK für Rust zurückgegeben werden, um hochwertige Anwendungen mit dem SDK zu erstellen. In den folgenden Abschnitten werden die verschiedenen Fehler beschrieben, auf die Sie beim SDK stoßen können, und wie Sie sie angemessen behandeln können. 

Bei jeder Operation wird ein `Result` Typ zurückgegeben, bei dem der Fehlertyp auf gesetzt ist [https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html). `SdkError`ist eine Aufzählung mit mehreren möglichen Typen, die als Varianten bezeichnet werden.

## Dienstfehler
<a name="serviceErrors"></a>

Die häufigste Art von Fehler ist [https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html#variant.ServiceError](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/result/enum.SdkError.html#variant.ServiceError). Dieser Fehler stellt eine Fehlerantwort von einem dar AWS-Service. Wenn Sie beispielsweise versuchen, ein Objekt von Amazon S3 abzurufen, das nicht existiert, gibt Amazon S3 eine Fehlerantwort zurück.

Wenn Sie auf eine stoßen, bedeutet `SdkError::ServiceError` dies, dass Ihre Anfrage erfolgreich an die gesendet wurde AWS-Service , aber nicht bearbeitet werden konnte. Dies kann an Fehlern in den Parametern der Anforderung oder an Probleme auf Seiten des Services liegen. 

 Die Details zur Fehlerantwort sind in der Fehlervariante enthalten. Das folgende Beispiel zeigt, wie Sie bequem zur zugrunde liegenden `ServiceError` Variante gelangen und verschiedene Fehlerfälle behandeln können:

```
// Needed to access the '.code()' function on the error type:
use aws_sdk_s3::error::ProvideErrorMetadata;

let result = s3.get_object()
    .bucket("my-bucket")
    .key("my-key")
    .send()
    .await;

match result {
    Ok(_output) => { /* Success. Do something with the output. */ }
    Err(err) => match err.into_service_error() {
        GetObjectError::InvalidObjectState(value) =>  {
            println!("invalid object state: {:?}", value);
        }
        GetObjectError::NoSuchKey(_) => {
            println!("object didn't exist");
        }
        // err.code() returns the raw error code from the service and can be 
        //     used as a last resort for handling unmodeled service errors. 
        err if err.code() == Some("SomeUnmodeledError") => {}
        err => return Err(err.into())
    }
};
```

## Fehler-Metadaten
<a name="errorMetadata"></a>

 Jeder Servicefehler hat zusätzliche Metadaten, auf die durch den Import dienstspezifischer Merkmale zugegriffen werden kann. 
+ Das `<service>::error::ProvideErrorMetadata` Merkmal bietet Zugriff auf alle verfügbaren zugrunde liegenden Rohfehlercodes und Fehlermeldungen, die vom Dienst zurückgegeben werden.
  + Für Amazon S3 ist dieses Merkmal [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/trait.ProvideErrorMetadata.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/trait.ProvideErrorMetadata.html). 

Sie können auch Informationen abrufen, die bei der Behebung von Servicefehlern nützlich sein können:
+ Das `<service>::operation::RequestId` Merkmal fügt Erweiterungsmethoden hinzu, um die eindeutige AWS Anforderungs-ID abzurufen, die vom Dienst generiert wurde. 
  + Für Amazon S3 ist dieses Merkmal [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestId.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestId.html).
+ Das `<service>::operation::RequestIdExt` Merkmal fügt die `extended_request_id()` Methode hinzu, um eine zusätzliche, erweiterte Anfrage-ID zu erhalten. 
  + Wird nur von einigen Diensten unterstützt.
  +  Für Amazon S3 ist dieses Merkmal [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestIdExt.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/trait.RequestIdExt.html).

## Detaillierter Fehler beim Drucken mit `DisplayErrorContext`
<a name="displayErrorContext"></a>

 Fehler im SDK sind im Allgemeinen das Ergebnis einer Reihe von Fehlern wie:

1. Das Versenden einer Anfrage ist fehlgeschlagen, weil der Connector einen Fehler zurückgegeben hat.

1. Der Connector hat einen Fehler zurückgegeben, weil der Anbieter für Anmeldeinformationen einen Fehler zurückgegeben hat. 

1. Der Anbieter für Anmeldeinformationen hat einen Fehler zurückgegeben, weil er einen Dienst aufgerufen hat und dieser Dienst einen Fehler zurückgegeben hat.

1. Der Dienst hat einen Fehler zurückgegeben, weil die Anforderung der Anmeldeinformationen nicht die richtige Autorisierung hatte.

Standardmäßig wird bei der Anzeige dieses Fehlers nur die Meldung „Versandfehler“ ausgegeben. Hier fehlen Details, die zur Behebung des Fehlers beitragen könnten. Das SDK für Rust bietet einen einfachen Fehlerreporter namens`DisplayErrorContext`.
+  Die `<service>::error::DisplayErrorContext` Struktur fügt Funktionen hinzu, um den vollständigen Fehlerkontext auszugeben.
  + Für Amazon S3 ist diese Struktur [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/struct.DisplayErrorContext.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/error/struct.DisplayErrorContext.html).

Wenn wir den anzuzeigenden Fehler einpacken und ausdrucken, `DisplayErrorContext` wird eine viel detailliertere Meldung angezeigt, die der folgenden ähnelt:

```
dispatch failure: other: Session token not found or invalid.
DispatchFailure(
    DispatchFailure { 
        source: ConnectorError { 
            kind: Other(None), 
            source: ProviderError(
                ProviderError { 
                    source: ProviderError(
                        ProviderError { 
                            source: ServiceError(
                                ServiceError { 
                                    source: UnauthorizedException(
                                        UnauthorizedException { 
                                            message: Some("Session token not found or invalid"), 
                                            meta: ErrorMetadata { 
                                                code: Some("UnauthorizedException"), 
                                                message: Some("Session token not found or invalid"), 
                                                extras: Some({"aws_request_id": "1b6d7476-f5ec-4a16-9890-7684ccee7d01"})
                                            } 
                                        }
                                    ), 
                                    raw: Response {
                                        status: StatusCode(401), 
                                        headers: Headers {
                                            headers: {
                                                "date": HeaderValue { _private: H0("Thu, 04 Jul 2024 07:41:21 GMT") }, 
                                                "content-type": HeaderValue { _private: H0("application/json") }, 
                                                "content-length": HeaderValue { _private: H0("114") }, 
                                                "access-control-expose-headers": HeaderValue { _private: H0("RequestId") }, 
                                                "access-control-expose-headers": HeaderValue { _private: H0("x-amzn-RequestId") }, 
                                                "requestid": HeaderValue { _private: H0("1b6d7476-f5ec-4a16-9890-7684ccee7d01") }, 
                                                "server": HeaderValue { _private: H0("AWS SSO") }, 
                                                "x-amzn-requestid": HeaderValue { _private: H0("1b6d7476-f5ec-4a16-9890-7684ccee7d01") }
                                            } 
                                        }, 
                                        body: SdkBody {
                                            inner: Once(
                                                Some(
                                                    b"{
                                                        \"message\":\"Session token not found or invalid\",
                                                        \"__type\":\"com.amazonaws.switchboard.portal#UnauthorizedException\"}"
                                                    )
                                                ), 
                                            retryable: true 
                                        }, 
                                        extensions: Extensions {
                                            extensions_02x: Extensions, 
                                            extensions_1x: Extensions 
                                        }
                                    } 
                                }
                            ) 
                        }
                    ) 
                }
            ), 
            connection: Unknown 
        } 
    }
)
```

# Verwendung paginierter Ergebnisse im AWS SDK für Rust
<a name="paginating"></a>

Viele AWS Operationen geben gekürzte Ergebnisse zurück, wenn die Nutzlast zu groß ist, um sie in einer einzigen Antwort zurückzugeben. Stattdessen gibt der Dienst einen Teil der Daten und ein Token zurück, um den nächsten Satz von Elementen abzurufen. Dieses Muster wird als Paginierung bezeichnet. 

 AWS SDK für Rust Dazu gehören Erweiterungsmethoden `into_paginator` für Operation Builder, mit denen Sie die Ergebnisse automatisch paginieren können. Sie müssen nur den Code schreiben, der die Ergebnisse verarbeitet. Allen Generatoren für Paginierungsoperationen steht eine `into_paginator()` Methode zur Verfügung, mit der die Ergebnisse paginiert werden [https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html](https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html)können.
+ In Amazon S3 ist ein Beispiel dafür [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/list_objects_v2/builders/struct.ListObjectsV2FluentBuilder.html#method.into_paginator](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/operation/list_objects_v2/builders/struct.ListObjectsV2FluentBuilder.html#method.into_paginator). 

In den folgenden Beispielen wird Amazon Simple Storage Service verwendet. Die Konzepte sind jedoch für jeden Service identisch, der über einen oder mehrere Seiten verfügt. APIs 

 Das folgende Codebeispiel zeigt das einfachste Beispiel, in dem die [https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html#method.try_collect](https://docs.rs/aws-smithy-async/latest/aws_smithy_async/future/pagination_stream/struct.PaginationStream.html#method.try_collect)Methode verwendet wird, um alle paginierten Ergebnisse in einem zusammenzufassen: `Vec` 

```
let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;

let s3 = aws_sdk_s3::Client::new(&config);

let all_objects = s3.list_objects_v2()
    .bucket("my-bucket")
    .into_paginator()
    .send()
    .try_collect()
    .await?
    .into_iter()
    .flat_map(|o| o.contents.unwrap_or_default())
    .collect::<Vec<_>>();
```

Manchmal möchten Sie mehr Kontrolle über das Paging haben und nicht alles auf einmal in den Speicher ziehen. Im folgenden Beispiel wird über Objekte in einem Amazon S3 S3-Bucket iteriert, bis keine weiteren mehr vorhanden sind.

```
let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;

let s3 = aws_sdk_s3::Client::new(&config);

let mut paginator = s3.list_objects_v2()
    .bucket("my-bucket")
    .into_paginator()
    // customize the page size (max results per/response)
    .page_size(10)
    .send();

println!("Objects in bucket:");

while let Some(result) = paginator.next().await {
    let resp = result?;
    for obj in resp.contents() {
        println!("\t{:?}", obj);
    }
}
```

# Hinzufügen von Unit-Tests zu Ihrer AWS SDK für Rust-Anwendung
<a name="testing"></a>

Es gibt zwar viele Möglichkeiten, Unit-Tests in Ihrem AWS SDK für Rust Projekt zu implementieren, wir empfehlen jedoch einige:
+ [Komponententests mit `mockall`](testing-automock.md)— Verwenden Sie es `automock` aus der `mockall` Kiste, um Ihre Tests automatisch zu generieren und auszuführen.
+ [Statische Wiedergabe](testing-replay.md)— Verwenden Sie die AWS Smithy-Runtime's`StaticReplayClient`, um einen gefälschten HTTP-Client zu erstellen, der anstelle des Standard-HTTP-Clients verwendet werden kann, der normalerweise von verwendet wird. AWS-Services Dieser Client gibt die von Ihnen angegebenen HTTP-Antworten zurück, anstatt mit dem Dienst über das Netzwerk zu kommunizieren, sodass Tests bekannte Daten für Testzwecke abrufen.
+ [Unit-Tests mit `aws-smithy-mocks`](testing-smithy-mocks.md)— Verwenden Sie `mock` und `mock_client` aus der `aws-smithy-mocks` Kiste, um Antworten von AWS SDK-Clients nachzuahmen und Scheinregeln zu erstellen, die definieren, wie das SDK auf bestimmte Anfragen reagieren soll.

# Generieren Sie automatisch Mocks mithilfe `mockall` des AWS SDK für Rust
<a name="testing-automock"></a>

Das AWS SDK für Rust bietet mehrere Ansätze zum Testen Ihres Codes, der mit AWS-Services interagiert. Sie können die meisten Scheinimplementierungen, die Ihre Tests benötigen, automatisch generieren, indem Sie die beliebten `[automock](https://docs.rs/mockall/latest/mockall/attr.automock.html)` aus der `[mockall](https://docs.rs/mockall/latest/mockall)` Kiste verwenden.

In diesem Beispiel wird eine benutzerdefinierte Methode namens getestet. `determine_prefix_file_size()` Diese Methode ruft eine benutzerdefinierte `list_objects()` Wrapper-Methode auf, die Amazon S3 aufruft. Durch Spott kann die `determine_prefix_file_size()` Methode getestet werden`list_objects()`, ohne Amazon S3 tatsächlich zu kontaktieren. 

1. Fügen Sie in einer Befehlszeile für Ihr Projektverzeichnis die `[mockall](https://docs.rs/mockall/latest/mockall)` Kiste als Abhängigkeit hinzu:

   ```
   $ cargo add --dev mockall
   ```

   Wenn Sie die `--dev` [Option](https://doc.rust-lang.org/cargo/commands/cargo-add.html) verwenden, wird die Kiste dem `[dev-dependencies]` Abschnitt Ihrer `Cargo.toml` Datei hinzugefügt. Da es sich um eine [Entwicklungsabhängigkeit](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies) handelt, wird sie nicht kompiliert und nicht in Ihre endgültige Binärdatei aufgenommen, die für den Produktionscode verwendet wird.

   In diesem Beispielcode wird auch Amazon Simple Storage Service als Beispiel verwendet AWS-Service.

   ```
   $ cargo add aws-sdk-s3
   ```

   Dadurch wird die Kiste dem `[dependencies]` Abschnitt Ihrer `Cargo.toml` Datei hinzugefügt.

1. Fügen Sie das `automock` Modul aus der `mockall` Kiste hinzu. 

   Schließen Sie auch alle anderen Bibliotheken ein AWS-Service , die sich auf das beziehen, das Sie testen, in diesem Fall Amazon S3.

   ```
   use aws_sdk_s3 as s3;
   #[allow(unused_imports)]
   use mockall::automock;
   
   use s3::operation::list_objects_v2::{ListObjectsV2Error, ListObjectsV2Output};
   ```

1. Fügen Sie als Nächstes Code hinzu, der bestimmt, welche von zwei Implementierungen der Amazon S3 S3-Wrapper-Struktur der Anwendung verwendet werden soll. 
   + Der echte, der für den Zugriff auf Amazon S3 über das Netzwerk geschrieben wurde.
   + Die Scheinimplementierung, generiert von`mockall`.

   In diesem Beispiel erhält die ausgewählte Datei den Namen`S3`. Die Auswahl ist abhängig von dem `test` Attribut:

   ```
   #[cfg(test)]
   pub use MockS3Impl as S3;
   #[cfg(not(test))]
   pub use S3Impl as S3;
   ```

1. Die `S3Impl` Struktur ist die Implementierung der Amazon S3 S3-Wrapper-Struktur, an die tatsächlich Anfragen gesendet AWS werden.
   + Wenn das Testen aktiviert ist, wird dieser Code nicht verwendet, da die Anfrage an den Mock gesendet wird und nicht AWS. Das `dead_code` Attribut weist den Linter an, kein Problem zu melden, wenn der `S3Impl` Typ nicht verwendet wird.
   +  Die Bedingung `#[cfg_attr(test, automock)]` gibt an, dass das `automock` Attribut gesetzt werden sollte, wenn das Testen aktiviert ist. Dies weist darauf `mockall` hin, `S3Impl` dass ein Modell generiert werden soll, das benannt wird`MockS3Impl`.
   + In diesem Beispiel ist die `list_objects()` Methode der Aufruf, den Sie verspotten möchten. `automock`erstellt automatisch eine `expect_list_objects()` Methode für Sie. 

   ```
   #[allow(dead_code)]
   pub struct S3Impl {
       inner: s3::Client,
   }
   
   #[cfg_attr(test, automock)]
   impl S3Impl {
       #[allow(dead_code)]
       pub fn new(inner: s3::Client) -> Self {
           Self { inner }
       }
   
       #[allow(dead_code)]
       pub async fn list_objects(
           &self,
           bucket: &str,
           prefix: &str,
           continuation_token: Option<String>,
       ) -> Result<ListObjectsV2Output, s3::error::SdkError<ListObjectsV2Error>> {
           self.inner
               .list_objects_v2()
               .bucket(bucket)
               .prefix(prefix)
               .set_continuation_token(continuation_token)
               .send()
               .await
       }
   }
   ```

1. Erstellen Sie die Testfunktionen in einem Modul mit dem Namen`test`.
   + Die Bedingung `#[cfg(test)]` gibt an, dass das Testmodul erstellt werden `mockall` soll, wenn das `test` `true` Attribut

   ```
   #[cfg(test)]
   mod test {
       use super::*;
       use mockall::predicate::eq;
   
       #[tokio::test]
       async fn test_single_page() {
           let mut mock = MockS3Impl::default();
           mock.expect_list_objects()
               .with(eq("test-bucket"), eq("test-prefix"), eq(None))
               .return_once(|_, _, _| {
                   Ok(ListObjectsV2Output::builder()
                       .set_contents(Some(vec![
                           // Mock content for ListObjectsV2 response
                           s3::types::Object::builder().size(5).build(),
                           s3::types::Object::builder().size(2).build(),
                       ]))
                       .build())
               });
   
           // Run the code we want to test with it
           let size = determine_prefix_file_size(mock, "test-bucket", "test-prefix")
               .await
               .unwrap();
   
           // Verify we got the correct total size back
           assert_eq!(7, size);
       }
   
       #[tokio::test]
       async fn test_multiple_pages() {
           // Create the Mock instance with two pages of objects now
           let mut mock = MockS3Impl::default();
           mock.expect_list_objects()
               .with(eq("test-bucket"), eq("test-prefix"), eq(None))
               .return_once(|_, _, _| {
                   Ok(ListObjectsV2Output::builder()
                       .set_contents(Some(vec![
                           // Mock content for ListObjectsV2 response
                           s3::types::Object::builder().size(5).build(),
                           s3::types::Object::builder().size(2).build(),
                       ]))
                       .set_next_continuation_token(Some("next".to_string()))
                       .build())
               });
           mock.expect_list_objects()
               .with(
                   eq("test-bucket"),
                   eq("test-prefix"),
                   eq(Some("next".to_string())),
               )
               .return_once(|_, _, _| {
                   Ok(ListObjectsV2Output::builder()
                       .set_contents(Some(vec![
                           // Mock content for ListObjectsV2 response
                           s3::types::Object::builder().size(3).build(),
                           s3::types::Object::builder().size(9).build(),
                       ]))
                       .build())
               });
   
           // Run the code we want to test with it
           let size = determine_prefix_file_size(mock, "test-bucket", "test-prefix")
               .await
               .unwrap();
   
           assert_eq!(19, size);
       }
   }
   ```
   + Jeder Test verwendet`let mut mock = MockS3Impl::default();`, um eine `mock` Instanz von zu erstellen`MockS3Impl`. 
   + Es verwendet die `expect_list_objects()` Methode des Mocks (die automatisch von erstellt wurde`automock`), um das erwartete Ergebnis festzulegen, wenn die `list_objects()` Methode an anderer Stelle im Code verwendet wird.
   + Nachdem die Erwartungen festgelegt wurden, werden diese verwendet, um die Funktion durch Aufrufen zu testen`determine_prefix_file_size()`. Der zurückgegebene Wert wird anhand einer Assertion überprüft, um zu bestätigen, dass er korrekt ist.

1. Die `determine_prefix_file_size()` Funktion verwendet den Amazon S3 S3-Wrapper, um die Größe der Präfixdatei abzurufen:

   ```
   #[allow(dead_code)]
   pub async fn determine_prefix_file_size(
       // Now we take a reference to our trait object instead of the S3 client
       // s3_list: ListObjectsService,
       s3_list: S3,
       bucket: &str,
       prefix: &str,
   ) -> Result<usize, s3::Error> {
       let mut next_token: Option<String> = None;
       let mut total_size_bytes = 0;
       loop {
           let result = s3_list
               .list_objects(bucket, prefix, next_token.take())
               .await?;
   
           // Add up the file sizes we got back
           for object in result.contents() {
               total_size_bytes += object.size().unwrap_or(0) as usize;
           }
   
           // Handle pagination, and break the loop if there are no more pages
           next_token = result.next_continuation_token.clone();
           if next_token.is_none() {
               break;
           }
       }
       Ok(total_size_bytes)
   }
   ```

Der Typ `S3` wird verwendet, um das verpackte SDK für Rust-Funktionen aufzurufen, um beide Funktionen zu unterstützen, `S3Impl` und `MockS3Impl` wenn HTTP-Anfragen gestellt werden. Der automatisch generierte Mock `mockall` meldet alle Testfehler, wenn das Testen aktiviert ist.

[Den vollständigen Code für diese Beispiele finden](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/examples/testing) Sie unter GitHub.

# Simulieren Sie den HTTP-Verkehr mithilfe der statischen Wiedergabe im AWS SDK für Rust
<a name="testing-replay"></a>

Das AWS SDK für Rust bietet mehrere Ansätze zum Testen Ihres Codes, der mit AWS-Services interagiert. In diesem Thema wird beschrieben, wie Sie mit `StaticReplayClient` dem einen gefälschten HTTP-Client erstellen, der anstelle des standardmäßigen HTTP-Clients verwendet werden kann, der normalerweise von AWS-Services verwendet wird. Dieser Client gibt die von Ihnen angegebenen HTTP-Antworten zurück, anstatt mit dem Dienst über das Netzwerk zu kommunizieren, sodass Tests bekannte Daten für Testzwecke abrufen.

Die `aws-smithy-http-client` Kiste enthält eine Testdienstprogrammklasse namens [https://docs.rs/aws-smithy-http-client/latest/aws_smithy_http_client/test_util/struct.StaticReplayClient.html](https://docs.rs/aws-smithy-http-client/latest/aws_smithy_http_client/test_util/struct.StaticReplayClient.html). Diese HTTP-Client-Klasse kann anstelle des Standard-HTTP-Clients angegeben werden, wenn ein AWS-Service Objekt erstellt wird.

Bei der `StaticReplayClient` Initialisierung von geben Sie eine Liste von HTTP-Anforderungs- und Antwortpaaren als `ReplayEvent` Objekte an. Während der Test ausgeführt wird, wird jede HTTP-Anfrage aufgezeichnet und der Client gibt die nächste HTTP-Antwort, die in der nächsten in der Ereignisliste gefunden wurde, als Antwort des HTTP-Clients zurück. `ReplayEvent` Dadurch kann der Test mit bekannten Daten und ohne Netzwerkverbindung ausgeführt werden.

## Statische Wiedergabe wird verwendet
<a name="testing-replay-steps"></a>

Um Static Replay zu verwenden, müssen Sie keinen Wrapper verwenden. Ermitteln Sie stattdessen, wie der tatsächliche Netzwerkverkehr für die Daten aussehen sollte, die Ihr Test verwenden wird, und stellen Sie diese Verkehrsdaten zur Verfügung, damit sie sie jedes Mal verwenden können, wenn das SDK eine Anfrage vom Client ausgibt. `StaticReplayClient` AWS-Service 

**Anmerkung**  
Es gibt mehrere Möglichkeiten, den erwarteten Netzwerkverkehr zu erfassen, darunter die AWS CLI und viele Tools zur Analyse des Netzwerkverkehrs und Paket-Sniffer-Tools.
+ Erstellen Sie eine Liste von `ReplayEvent` Objekten, die die erwarteten HTTP-Anfragen und die Antworten angeben, die für sie zurückgegeben werden sollen.
+ Erstellen Sie eine `StaticReplayClient` mithilfe der im vorherigen Schritt erstellten HTTP-Transaktionsliste.
+ Erstellen Sie ein Konfigurationsobjekt für den AWS Client und geben Sie das `StaticReplayClient` als das `Config` Objekt an`http_client`.
+ Erstellen Sie das AWS-Service Client-Objekt mithilfe der im vorherigen Schritt erstellten Konfiguration.
+ Führen Sie die Operationen, die Sie testen möchten, mit dem Serviceobjekt aus, das für die Verwendung von konfiguriert ist`StaticReplayClient`. Jedes Mal, wenn das SDK eine API-Anfrage an sendet AWS, wird die nächste Antwort in der Liste verwendet.
**Anmerkung**  
Die nächste Antwort in der Liste wird immer zurückgegeben, auch wenn die gesendete Anfrage nicht mit der Antwort im `ReplayEvent` Objektvektor übereinstimmt.
+ Wenn alle gewünschten Anfragen gestellt wurden, rufen Sie die `StaticReplayClient.assert_requests_match()` Funktion auf, um zu überprüfen, ob die vom SDK gesendeten Anfragen mit denen in der `ReplayEvent` Objektliste übereinstimmen.

## Beispiel
<a name="testing-replay-example"></a>

Schauen wir uns die Tests für dieselbe `determine_prefix_file_size()` Funktion im vorherigen Beispiel an, verwenden aber Static Replay statt Mocking.

1. Fügen Sie in einer Befehlszeile für Ihr Projektverzeichnis die [https://crates.io/crates/aws-smithy-http-client](https://crates.io/crates/aws-smithy-http-client)Crate als Abhängigkeit hinzu:

   ```
   $ cargo add --dev aws-smithy-http-client --features test-util
   ```

   Wenn Sie die `--dev` [Option](https://doc.rust-lang.org/cargo/commands/cargo-add.html) verwenden, wird die Kiste dem `[dev-dependencies]` Abschnitt Ihrer `Cargo.toml` Datei hinzugefügt. Da es sich um eine [Entwicklungsabhängigkeit](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies) handelt, wird sie nicht kompiliert und nicht in Ihre endgültige Binärdatei aufgenommen, die für den Produktionscode verwendet wird.

   In diesem Beispielcode wird auch Amazon Simple Storage Service als Beispiel verwendet AWS-Service.

   ```
   $ cargo add aws-sdk-s3
   ```

   Dadurch wird die Kiste dem `[dependencies]` Abschnitt Ihrer `Cargo.toml` Datei hinzugefügt.

1. Fügen Sie in Ihr Testcode-Modul beide Typen ein, die Sie benötigen.

   ```
   use aws_smithy_http_client::test_util::{ReplayEvent, StaticReplayClient};
   use aws_sdk_s3::primitives::SdkBody;
   ```

1. Der Test beginnt mit der Erstellung der `ReplayEvent` Strukturen, die jede der HTTP-Transaktionen darstellen, die während des Tests stattfinden sollen. Jedes Ereignis enthält ein HTTP-Anforderungsobjekt und ein HTTP-Antwortobjekt, die die Informationen darstellen, mit denen AWS-Service sie normalerweise antworten würden. Diese Ereignisse werden an einen Aufruf übergeben an`StaticReplayClient::new()`:

   ```
           let page_1 = ReplayEvent::new(
                   http::Request::builder()
                       .method("GET")
                       .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=test-prefix")
                       .body(SdkBody::empty())
                       .unwrap(),
                   http::Response::builder()
                       .status(200)
                       .body(SdkBody::from(include_str!("./testing/response_multi_1.xml")))
                       .unwrap(),
               );
           let page_2 = ReplayEvent::new(
                   http::Request::builder()
                       .method("GET")
                       .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=test-prefix&continuation-token=next")
                       .body(SdkBody::empty())
                       .unwrap(),
                   http::Response::builder()
                       .status(200)
                       .body(SdkBody::from(include_str!("./testing/response_multi_2.xml")))
                       .unwrap(),
               );
           let replay_client = StaticReplayClient::new(vec![page_1, page_2]);
   ```

   Das Ergebnis wird gespeichert in`replay_client`. Dies stellt einen HTTP-Client dar, der dann vom SDK für Rust verwendet werden kann, indem er in der Konfiguration des Clients angegeben wird.

1. Um den Amazon S3 S3-Client zu erstellen, rufen Sie die `from_conf()` Funktion der Client-Klasse auf, um den Client mithilfe eines Konfigurationsobjekts zu erstellen:

   ```
           let client: s3::Client = s3::Client::from_conf(
               s3::Config::builder()
                   .behavior_version(BehaviorVersion::latest())
                   .credentials_provider(make_s3_test_credentials())
                   .region(s3::config::Region::new("us-east-1"))
                   .http_client(replay_client.clone())
                   .build(),
           );
   ```

   Das Konfigurationsobjekt wird mit der `http_client()` Methode des Builders angegeben, und die Anmeldeinformationen werden mit der `credentials_provider()` Methode angegeben. Die Anmeldeinformationen werden mithilfe einer aufgerufenen Funktion erstellt`make_s3_test_credentials()`, die eine falsche Struktur der Anmeldeinformationen zurückgibt:

   ```
   fn make_s3_test_credentials() -> s3::config::Credentials {
       s3::config::Credentials::new(
           "ATESTCLIENT",
           "astestsecretkey",
           Some("atestsessiontoken".to_string()),
           None,
           "",
       )
   }
   ```

   Diese Anmeldeinformationen müssen nicht gültig sein, da sie nicht wirklich gesendet werden AWS.

1. Führen Sie den Test aus, indem Sie die Funktion aufrufen, die getestet werden muss. In diesem Beispiel lautet der Name dieser Funktion`determine_prefix_file_size()`. Sein erster Parameter ist das Amazon S3 S3-Client-Objekt, das für seine Anfragen verwendet werden soll. Geben Sie daher den Client an, der mit dem `StaticReplayClient` so erstellt wurde, dass Anfragen von diesem bearbeitet werden, anstatt über das Netzwerk gesendet zu werden:

   ```
           let size = determine_prefix_file_size(client, "test-bucket", "test-prefix")
               .await
               .unwrap();
   
           assert_eq!(19, size);
   
           replay_client.assert_requests_match(&[]);
   ```

   Wenn der Aufruf von abgeschlossen `determine_prefix_file_size()` ist, wird eine Bestätigung verwendet, um zu bestätigen, dass der zurückgegebene Wert dem erwarteten Wert entspricht. Dann wird die `StaticReplayClient` `assert_requests_match()` Methodenfunktion aufgerufen. Diese Funktion scannt die aufgezeichneten HTTP-Anfragen und bestätigt, dass sie alle mit denen übereinstimmen, die im `ReplayEvent` Objektarray angegeben wurden, das bei der Erstellung des Replay-Clients bereitgestellt wurde.

[Den vollständigen Code für diese Beispiele finden](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/examples/testing) Sie unter GitHub.

# Unit-Tests mit `aws-smithy-mocks` im AWS SDK für Rust
<a name="testing-smithy-mocks"></a>

Das AWS SDK für Rust bietet mehrere Ansätze zum Testen Ihres Codes, der mit AWS-Services interagiert. In diesem Thema wird beschrieben, wie Sie die [https://docs.rs/aws-smithy-mocks/latest/aws_smithy_mocks/](https://docs.rs/aws-smithy-mocks/latest/aws_smithy_mocks/)Crate verwenden. Sie bietet eine einfache und dennoch leistungsstarke Möglichkeit, Antworten von AWS SDK-Clients zu Testzwecken nachzuahmen.

## -Übersicht
<a name="overview-smithy-mock"></a>

Wenn Sie Tests für Code schreiben, der Use verwendet AWS-Services, möchten Sie häufig vermeiden, tatsächliche Netzwerkaufrufe zu tätigen. Die `aws-smithy-mocks` Crate bietet eine Lösung, indem sie Ihnen Folgendes ermöglicht:
+ Erstellen Sie Scheinregeln, die definieren, wie das SDK auf bestimmte Anfragen reagieren soll.
+ Gibt verschiedene Arten von Antworten zurück (Erfolg, Fehler, HTTP-Antworten).
+ Ordnen Sie Anfragen anhand ihrer Eigenschaften zu.
+ Definieren Sie Antwortsequenzen zum Testen des Wiederholungsverhaltens.
+ Stellen Sie sicher, dass Ihre Regeln wie erwartet verwendet wurden.

## Die Abhängigkeit wird hinzugefügt
<a name="dependency-smithy-mock"></a>

Fügen Sie in einer Befehlszeile für Ihr Projektverzeichnis die [https://crates.io/crates/aws-smithy-mocks](https://crates.io/crates/aws-smithy-mocks)Crate als Abhängigkeit hinzu:

```
$ cargo add --dev aws-smithy-mocks
```

Wenn Sie die `--dev` [Option](https://doc.rust-lang.org/cargo/commands/cargo-add.html) verwenden, wird die Kiste dem `[dev-dependencies]` Abschnitt Ihrer `Cargo.toml` Datei hinzugefügt. Da es sich um eine [Entwicklungsabhängigkeit](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies) handelt, wird sie nicht kompiliert und nicht in Ihre endgültige Binärdatei aufgenommen, die für den Produktionscode verwendet wird.

Dieser Beispielcode verwendet auch Amazon Simple Storage Service als AWS-Service Beispiel und erfordert eine Funktion`test-util`.

```
$ cargo add aws-sdk-s3 --features test-util
```

Dadurch wird die Kiste dem `[dependencies]` Abschnitt Ihrer `Cargo.toml` Datei hinzugefügt.

## Grundlegende Verwendung
<a name="basic-smithy-mocks"></a>

 Hier ist ein einfaches Beispiel für die Verwendung `aws-smithy-mocks` zum Testen von Code, der mit Amazon Simple Storage Service (Amazon S3) interagiert:

```
use aws_sdk_s3::operation::get_object::GetObjectOutput;
use aws_sdk_s3::primitives::ByteStream;
use aws_smithy_mocks::{mock, mock_client};

#[tokio::test]
async fn test_s3_get_object() {
    // Create a rule that returns a successful response
    let get_object_rule = mock!(aws_sdk_s3::Client::get_object)
        .then_output(|| {
            GetObjectOutput::builder()
                .body(ByteStream::from_static(b"test-content"))
                .build()
        });

    // Create a mocked client with the rule
    let s3 = mock_client!(aws_sdk_s3, [&get_object_rule]);

    // Use the client as you would normally
    let result = s3
        .get_object()
        .bucket("test-bucket")
        .key("test-key")
        .send()
        .await
        .expect("success response");

    // Verify the response
    let data = result.body.collect().await.expect("successful read").to_vec();
    assert_eq!(data, b"test-content");

    // Verify the rule was used
    assert_eq!(get_object_rule.num_calls(), 1);
}
```

## Scheinregeln erstellen
<a name="creating-rules-smithy-mocks"></a>

Regeln werden mithilfe des `mock!` Makros erstellt, das eine Client-Operation als Argument verwendet. Anschließend können Sie konfigurieren, wie sich die Regel verhalten soll. 

### Passende Anfragen
<a name="matching-requests-smithy-mocks"></a>

 Sie können Regeln spezifischer gestalten, indem Sie Eigenschaften auf Anfrage abgleichen:

```
let rule = mock!(Client::get_object)
    .match_requests(|req| req.bucket() == Some("test-bucket") && req.key() == Some("test-key"))
    .then_output(|| {
        GetObjectOutput::builder()
            .body(ByteStream::from_static(b"test-content"))
            .build()
    });
```

### Verschiedene Antworttypen
<a name="diff-response-smithy-mocks"></a>

Sie können verschiedene Arten von Antworten zurückgeben:

```
// Return a successful response
let success_rule = mock!(Client::get_object)
    .then_output(|| GetObjectOutput::builder().build());

// Return an error
let error_rule = mock!(Client::get_object)
    .then_error(|| GetObjectError::NoSuchKey(NoSuchKey::builder().build()));

// Return a specific HTTP response
let http_rule = mock!(Client::get_object)
    .then_http_response(|| {
        HttpResponse::new(
            StatusCode::try_from(503).unwrap(),
            SdkBody::from("service unavailable")
        )
    });
```

## Verhalten bei Wiederholungsversuchen testen
<a name="testing-retry-behavior-smithy-mocks"></a>

Eine der leistungsstärksten Funktionen von `aws-smithy-mocks` ist die Fähigkeit, das Wiederholungsverhalten zu testen, indem Sequenzen von Antworten definiert werden:

```
// Create a rule that returns 503 twice, then succeeds
let retry_rule = mock!(aws_sdk_s3::Client::get_object)
    .sequence()
    .http_status(503, None)                          // First call returns 503
    .http_status(503, None)                          // Second call returns 503
    .output(|| GetObjectOutput::builder().build())   // Third call succeeds
    .build();

// With repetition using times()
let retry_rule = mock!(Client::get_object)
    .sequence()
    .http_status(503, None)
    .times(2)                                        // First two calls return 503
    .output(|| GetObjectOutput::builder().build())   // Third call succeeds
    .build();
```

## Regelmodi
<a name="rule-modes-smithy-mocks"></a>

Mithilfe von Regelmodi können Sie steuern, wie Regeln abgeglichen und angewendet werden:

```
// Sequential mode: Rules are tried in order, and when a rule is exhausted, the next rule is used
let client = mock_client!(aws_sdk_s3, RuleMode::Sequential, [&rule1, &rule2]);

// MatchAny mode: The first matching rule is used, regardless of order
let client = mock_client!(aws_sdk_s3, RuleMode::MatchAny, [&rule1, &rule2]);
```

## Beispiel: Testen des Wiederholungsverhaltens
<a name="example-retry-smithy-mocks"></a>

Hier ist ein vollständigeres Beispiel, das zeigt, wie das Verhalten bei Wiederholungen getestet wird:

```
use aws_sdk_s3::operation::get_object::GetObjectOutput;
use aws_sdk_s3::config::RetryConfig;
use aws_sdk_s3::primitives::ByteStream;
use aws_smithy_mocks::{mock, mock_client, RuleMode};

#[tokio::test]
async fn test_retry_behavior() {
    // Create a rule that returns 503 twice, then succeeds
    let retry_rule = mock!(aws_sdk_s3::Client::get_object)
        .sequence()
        .http_status(503, None)
        .times(2)
        .output(|| GetObjectOutput::builder()
            .body(ByteStream::from_static(b"success"))
            .build())
        .build();

    // Create a mocked client with the rule and custom retry configuration
    let s3 = mock_client!(
        aws_sdk_s3,
        RuleMode::Sequential,
        [&retry_rule],
        |client_builder| {
            client_builder.retry_config(RetryConfig::standard().with_max_attempts(3))
        }
    );

    // This should succeed after two retries
    let result = s3
        .get_object()
        .bucket("test-bucket")
        .key("test-key")
        .send()
        .await
        .expect("success after retries");

    // Verify the response
    let data = result.body.collect().await.expect("successful read").to_vec();
    assert_eq!(data, b"success");

    // Verify all responses were used
    assert_eq!(retry_rule.num_calls(), 3);
}
```

## Beispiel: Verschiedene Antworten basierend auf Anforderungsparametern
<a name="example-request-param-smithy-mocks"></a>

Sie können auch Regeln erstellen, die auf der Grundlage von Anforderungsparametern unterschiedliche Antworten zurückgeben:

```
use aws_sdk_s3::operation::get_object::{GetObjectOutput, GetObjectError};
use aws_sdk_s3::types::error::NoSuchKey;
use aws_sdk_s3::Client;
use aws_sdk_s3::primitives::ByteStream;
use aws_smithy_mocks::{mock, mock_client, RuleMode};

#[tokio::test]
async fn test_different_responses() {
    // Create rules for different request parameters
    let exists_rule = mock!(Client::get_object)
        .match_requests(|req| req.bucket() == Some("test-bucket") && req.key() == Some("exists"))
        .sequence()
        .output(|| GetObjectOutput::builder()
            .body(ByteStream::from_static(b"found"))
            .build())
        .build();

    let not_exists_rule = mock!(Client::get_object)
        .match_requests(|req| req.bucket() == Some("test-bucket") && req.key() == Some("not-exists"))
        .sequence()
        .error(|| GetObjectError::NoSuchKey(NoSuchKey::builder().build()))
        .build();

    // Create a mocked client with the rules in MatchAny mode
    let s3 = mock_client!(aws_sdk_s3, RuleMode::MatchAny, [&exists_rule, &not_exists_rule]);

    // Test the "exists" case
    let result1 = s3
        .get_object()
        .bucket("test-bucket")
        .key("exists")
        .send()
        .await
        .expect("object exists");

    let data = result1.body.collect().await.expect("successful read").to_vec();
    assert_eq!(data, b"found");

    // Test the "not-exists" case
    let result2 = s3
        .get_object()
        .bucket("test-bucket")
        .key("not-exists")
        .send()
        .await;

    assert!(result2.is_err());
    assert!(matches!(result2.unwrap_err().into_service_error(),
                    GetObjectError::NoSuchKey(_)));
}
```

## Best Practices
<a name="best-practices-smithy-mocks"></a>

Bei der Verwendung `aws-smithy-mocks` zum Testen:

1.  Spezifische Anfragen zuordnen: Verwenden Sie diese Option`match_requests()`, um sicherzustellen, dass Ihre Regeln nur für die beabsichtigten Anfragen gelten, insbesondere für`RuleMode:::MatchAny`.

1.  Überprüfen Sie die Regelverwendung: Stellen `rule.num_calls()` Sie sicher, dass Ihre Regeln tatsächlich verwendet wurden.

1.  Testen Sie die Fehlerbehandlung: Erstellen Sie Regeln, die Fehler zurückgeben, um zu testen, wie Ihr Code mit Fehlern umgeht.

1.  Testen Sie die Wiederholungslogik: Verwenden Sie Antwortsequenzen, um zu überprüfen, ob Ihr Code alle benutzerdefinierten Wiederholungsklassifikatoren oder anderes Wiederholungsverhalten korrekt behandelt.

1. Konzentrieren Sie sich auf Tests: Erstellen Sie separate Tests für verschiedene Szenarien, anstatt zu versuchen, alles in einem Test abzudecken.

# Kellner im AWS SDK für Rust verwenden
<a name="waiters"></a>

 Waiter sind eine clientseitige Abstraktion, die verwendet wird, um eine Ressource abzufragen, bis ein gewünschter Status erreicht ist oder bis festgestellt wird, dass die Ressource nicht in den gewünschten Zustand übergeht. Dies ist eine häufige Aufgabe, wenn Sie mit Diensten arbeiten, die irgendwann konsistent sind, wie Amazon Simple Storage Service, oder mit Diensten, die asynchron Ressourcen erstellen, wie Amazon Elastic Compute Cloud. Das Schreiben von Logik zur kontinuierlichen Abfrage des Status einer Ressource kann umständlich und fehleranfällig sein. Das Ziel der Kellner ist es, diese Verantwortung aus dem Kundencode in den Kundencode zu verlagern AWS SDK für Rust, der über fundierte Kenntnisse der zeitlichen Abläufe verfügt. AWS 

AWS-Services Diese Dienste bieten Unterstützung für Kellner und beinhalten ein `<service>::waiters` Modul. 
+ Das `<service>::client::Waiters` Merkmal bietet Kellnermethoden für den Kunden. Die Methoden sind für die `Client` Struktur implementiert. Alle Kellnermethoden folgen einer Standardbenennungskonvention von `wait_until_<Condition>` 
  + Für Amazon S3 ist dieses Merkmal [https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/client/trait.Waiters.html](https://docs.rs/aws-sdk-s3/latest/aws_sdk_s3/client/trait.Waiters.html). 

Das folgende Beispiel verwendet Amazon S3. Die Konzepte sind jedoch für alle AWS-Service , für die ein oder mehrere Kellner definiert sind, dieselben. 

Das folgende Codebeispiel zeigt, wie eine Waiter-Funktion verwendet wird, anstatt eine Abfragelogik zu schreiben, um darauf zu warten, dass ein Bucket nach seiner Erstellung existiert. 

```
use std::time::Duration;
use aws_config::BehaviorVersion;
// Import Waiters trait to get `wait_until_<Condition>` methods on Client.
use aws_sdk_s3::client::Waiters;


let config = aws_config::defaults(BehaviorVersion::latest())
    .load()
    .await;
    
let s3 = aws_sdk_s3::Client::new(&config);

// This initiates creating an S3 bucket and potentially returns before the bucket exists.
s3.create_bucket()
    .bucket("my-bucket")
    .send()
    .await?;

// When this function returns, the bucket either exists or an error is propagated.
s3.wait_until_bucket_exists()
    .bucket("my-bucket")
    .wait(Duration::from_secs(5))
    .await?;

// The bucket now exists.
```

**Anmerkung**  
 Jede Wartemethode gibt einen Wert zurück`Result<FinalPoll<...>, WaiterError<...>> `, der verwendet werden kann, um die endgültige Antwort nach Erreichen der gewünschten Bedingung oder eines Fehlers zu ermitteln. Einzelheiten finden Sie unter [FinalPoll](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/waiters/struct.FinalPoll.html)und [WaiterError](https://docs.rs/aws-smithy-runtime-api/latest/aws_smithy_runtime_api/client/waiters/error/enum.WaiterError.html)in der Rust-API-Dokumentation.