Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Entkoppeln Sie die Backend-Verarbeitung von einem Frontend-Host, wenn die Backend-Verarbeitung asynchron betrieben werden muss, das Frontend jedoch eine klare Antwort benötigt.
Kontext und Problem
In der modernen Anwendungsentwicklung sind Clientanwendungen häufig von Remote-APIs abhängig, um Geschäftslogik und Verfassenfunktionen bereitzustellen. Viele Anwendungen führen Code in einem Webbrowser aus, und andere Umgebungen hosten auch Clientcode. Die APIs können sich direkt auf die Anwendung beziehen oder als gemeinsame Dienste von einem externen Dienst ausgeführt werden. Die meisten API-Aufrufe verwenden HTTP oder HTTPS und folgen der REST-Semantik.
In den meisten Fällen reagieren APIs für eine Clientanwendung in etwa 100 Millisekunden (ms) oder weniger. Viele Faktoren können sich auf die Antwortlatenz auswirken:
- Hoststapel der Anwendung
- Sicherheitskomponenten
- Der relative geografische Standort des Anrufers und des Back-Ends
- Netzwerkinfrastruktur
- Aktuelle Last
- Die Größe der Anforderungsnutzlast
- Verarbeitungswarteschlangenlänge
- Die Zeit für das Back-End zum Verarbeiten der Anforderung
Diese Faktoren können der Antwort Latenz hinzufügen. Sie können einige Faktoren mindern, indem Sie das Back-End skalieren. Andere Faktoren, z. B. die Netzwerkinfrastruktur, liegen außerhalb der Kontrolle des Anwendungsentwicklers. Die meisten APIs reagieren schnell genug, damit die Antwort über dieselbe Verbindung zurückgegeben wird. Anwendungscode kann einen synchronen API-Aufruf auf eine nicht blockierende Weise durchführen, um die Darstellung der asynchronen Verarbeitung zu ermöglichen. Wir empfehlen diesen Ansatz für Eingabe- und Ausgabevorgänge (I/O)-gebundene Vorgänge.
In einigen Szenarien erledigt das Back-End Aufgaben, die lang andauernd sind und einige Sekunden benötigen. In anderen Szenarien führt das Back-End lang laufende Hintergrundarbeit für Minuten oder sogar über längere Zeiträume hinweg durch. In diesen Fällen können Sie nicht warten, bis die Arbeit abgeschlossen ist, bevor Sie eine Antwort senden. Diese Situation kann ein Problem für synchrone Anforderungs-Antwort-Muster verursachen. Anleitungen zum Entwerfen der Back-End-Verarbeitung finden Sie unter "Hintergrundaufträge".
Einige Architekturen lösen dieses Problem mithilfe eines Nachrichtenbrokers, um Anforderungs- und Antwortphasen zu trennen. Viele Systeme erreichen diese Trennung durch das Warteschlangenbasierte Lastenausgleichsmuster. Durch diese Trennung kann der Clientprozess und die Back-End-API unabhängig voneinander skaliert werden. Es führt auch zu einer zusätzlichen Komplexität, wenn der Client eine Erfolgsbenachrichtigung erfordert, da dieser Schritt auch asynchron werden muss.
Viele der gleichen Überlegungen, die für Clientanwendungen gelten, gelten auch für Server-zu-Server-REST-API-Aufrufe in verteilten Systemen, z. B. in einer Microservices-Architektur.
Lösung
Eine Lösung für dieses Problem ist die Verwendung von HTTP-Polling. Das Abfrageverfahren eignet sich gut für clientseitigen Code, wenn Callback-Endpunkte nicht verfügbar sind oder wenn langanhaltende Verbindungen zu viel Komplexität verursachen. Auch wenn Rückrufe möglich sind, können die zusätzlichen Bibliotheken und Dienste, die sie benötigen, die Komplexität erhöhen.
Die folgenden Schritte beschreiben die Lösung:
Die Clientanwendung ruft die API synchron auf, um einen lang andauernden Vorgang auf dem Back-End auszulösen.
Die API reagiert so schnell wie möglich synchron. Er gibt einen HTTP 202 (Accepted)-Statuscode zurück, um zu bestätigen, dass er die Anforderung zur Verarbeitung empfangen hat.
Hinweis
Die API sollte die Anforderung und die auszuführende Aktion überprüfen, bevor sie den langen Prozess startet. Wenn die Anforderung ungültig ist, antworten Sie sofort mit einem Fehlercode wie HTTP 400 (ungültige Anforderung).
Die Antwort enthält einen Standortverweis, der auf einen Endpunkt verweist, den der Client abfragen kann, um das Ergebnis des lange ausgeführten Vorgangs zu überprüfen.
Die API entlädt die Verarbeitung in eine andere Komponente, z. B. eine Nachrichtenwarteschlange.
Für jeden erfolgreichen Aufruf des Statusendpunkts gibt der Endpunkt HTTP 200 (OK) zurück. Während die Arbeit ausgeführt wird, gibt der Statusendpunkt eine Ressource zurück, die diesen Zustand angibt. Der Statusantworttext sollte genügend Informationen für den Client enthalten, um den aktuellen Status des Vorgangs zu verstehen.
Wenn die Arbeit abgeschlossen ist, gibt der Statusendpunkt eine Ressource zurück, die den Abschluss angibt oder an eine andere Ressourcen-URL umleitet. Wenn der asynchrone Vorgang beispielsweise eine neue Ressource erstellt, leitet der Statusendpunkt an die URL für diese Ressource um.
Das folgende Diagramm zeigt einen typischen Fluss.
Der Client sendet eine Anforderung und empfängt die Antwort „HTTP 202 (Akzeptiert)“.
Der Client sendet eine HTTP-GET-Anforderung an den Statusendpunkt. Da die Arbeit noch aussteht, gibt dieser Aufruf „HTTP 200“ zurück.
Irgendwann wird die Arbeit abgeschlossen, und der Statusendpunkt gibt HTTP 303 (Siehe Andere) zurück, um zur Ressource umzuleiten.
Der Client ruft die Ressource an der angegebenen URL ab.
Probleme und Überlegungen
Berücksichtigen Sie die folgenden Punkte, wenn Sie sich für die Implementierung dieses Musters entscheiden:
Es gibt mehrere Möglichkeiten, dieses Muster über HTTP zu implementieren, und upstream-Dienste verwenden nicht immer dieselbe Semantik. Beispielsweise verwenden einige Implementierungen keinen separaten Statusendpunkt. Stattdessen fragt der Client die Zielressourcen-URL direkt ab und empfängt HTTP 404 (Nicht gefunden), bis die Ressource erstellt wird. Diese Antwort ist sinnvoll, da die Ressource noch nicht vorhanden ist. Dieser Ansatz kann jedoch mehrdeutig sein, wenn 404 auch für ungültige Anforderungs-IDs zurückgegeben wird. Ein dedizierter Statusendpunkt, der HTTP 200 mit einem Statustext zurückgibt, wie in diesem Muster beschrieben, vermeidet diese Mehrdeutigkeit.
Eine HTTP 202-Antwort gibt an, wo der Client abruft und wie oft. Sie sollte die folgenden Kopfzeilen enthalten.
Header Beschreibung Hinweise LocationEine URL, die der Client für einen Antwortstatus abruft Diese URL kann ein freigegebenes Zugriffssignaturtoken sein. Das Valet Key-Muster eignet sich gut, wenn für diesen Speicherort die Zugriffssteuerung erforderlich ist. Das Muster gilt auch, wenn die Antwortabfragung zu einem anderen Back-End wechseln muss. Retry-AfterEine Schätzung, wann die Verarbeitung abgeschlossen sein wird. Dieser Header ist dazu gedacht, zu verhindern, dass Polling-Clients zu viele Anfragen an das Back-End senden. Erwägen Sie das erwartete Clientverhalten, wenn Sie diese Antwort entwerfen. Ein Client, den Sie steuern, kann diesen Antwortwerten genau folgen. Clients, die andere erstellen, einschließlich solcher, die mithilfe von No-Code- oder Low-Code-Tools wie Azure Logic Apps gebaut wurden, können ihre eigene Verarbeitung für HTTP 202 anwenden.
Erwägen Sie die Einbeziehung der folgenden Felder in die Statusendpunktantwort.
Feld Beschreibung Hinweise statusDer aktuelle Status des Vorgangs, z. B. Ausstehend, Läuft, Erfolgreich, Fehlgeschlagen oder Abgebrochen. Verwenden Sie einen konsistenten, dokumentierten Satz von Terminal- und Nicht-Terminalwerten. createdAtDie Zeit, zu der der Vorgang angenommen wurde. Unterstützt Clients beim Erkennen veralteter oder abgebrochener Vorgänge. lastUpdatedAtDer Zeitpunkt, zu dem der Status zuletzt aktualisiert wurde. Ermöglicht Clients die Unterscheidung eines festgefahrenen Vorgangs von einem Vorgang, der aktiv voranschreitet. percentCompleteEine optionale Statusanzeige. Nützlich, wenn das Back-End den Fortschritt sinnvoll schätzen kann. errorEin strukturiertes Fehlerobjekt, wenn der Status fehlgeschlagen ist. Erwägen Sie die Verwendung des RFC 9457-Formats zur Konsistenz. Möglicherweise müssen Sie einen Verarbeitungs-Proxy verwenden, um die Antwortheader oder Nutzlast anzupassen, abhängig von den zugrunde liegenden Diensten, die Sie verwenden.
Wenn der Statusendpunkt nach Abschluss umgeleitet wird, verwenden Sie HTTP 303 (Siehe Weitere Informationen). Ein 303 weist den Client an, unabhängig von der ursprünglichen HTTP-Anforderungsmethode eine GET-Anforderung an die Umleitungs-URL zu senden. Dieses Verhalten ist die richtige Semantik für dieses Muster, da der Client eine eindeutige Ergebnisressource abruft und den ursprünglichen Vorgang nicht erneut übermittelt. HTTP 302 (Found) garantiert keine Methodenänderung; einige Clients geben die ursprüngliche Methode für die Umleitung wieder, was zu unbeabsichtigten Nebenwirkungen wie doppelten POST-Anforderungen führen kann.
Nachdem der Server die Anforderung erfolgreich verarbeitet hat, gibt die vom Header angegebene Ressource
Locationeinen HTTP-Statuscode wie 200, 201 (Erstellt) oder 204 (Kein Inhalt) zurück.Wenn während der Verarbeitung ein Fehler auftritt, speichern Sie den Fehler bei der Ressourcen-URL, die der
LocationHeader angibt, und geben einen 4xx-Statuscode aus dieser Ressource zurück, der dem Fehler entspricht. Verwenden Sie ein strukturiertes Fehlerformat wie RFC 9457 (Problemdetails für HTTP-APIs), damit Clients Fehler programmgesteuert analysieren und behandeln können.Die Statusressource und alle gespeicherten Ergebnisse verbrauchen Speicher und Berechnung. Definieren Sie eine Aufbewahrungsrichtlinie, um sie nach einem angemessenen Zeitraum zu bereinigen, und erwägen Sie, das Aufbewahrungsfenster über einen
ExpiresHeader in der Statusantwort an Benutzer zu kommunizieren.Lösungen implementieren dieses Muster nicht auf die gleiche Weise, und einige Dienste enthalten zusätzliche oder alternative Header. Beispielsweise verwendet Azure Resource Manager eine geänderte Variante dieses Musters. Weitere Informationen finden Sie unter Resource Manager asynchrone Vorgänge.
Dieses Muster wird von Legacy-Clients möglicherweise nicht unterstützt. In diesem Fall müssen Sie möglicherweise eine Fassade über der asynchronen API platzieren, um die asynchrone Verarbeitung vor dem ursprünglichen Client auszublenden. Logik-Apps unterstützen dieses Muster beispielsweise nativ, und Sie können es als Integrationsebene zwischen einer asynchronen API und einem Client verwenden, der synchrone Aufrufe vorgibt. Weitere Informationen finden Sie unter "Asynchrones Anforderungsantwortverhalten" in Azure Logic Apps.
In einigen Szenarien möchten Sie möglicherweise Clients die Möglichkeit bieten, eine zeitintensive Anforderung abzubrechen. Machen Sie in diesem Fall einen DELETE-Vorgang für die Statusendpunktressource verfügbar. Diese Anforderung sollte eine Abbruchanweisung an die Back-End-Verarbeitungskomponente weiterleiten. Nachdem das Back-End den Abbruch verarbeitet hat, sollte die Statusressource aktualisiert werden, um den abgebrochenen Zustand widerzuspiegeln. Dieser Vorgang verhindert, dass unvollständige Arbeiten ressourcen unbegrenzt verbrauchen. Überlegen Sie, ob der Vorgang teilweises Rollback unterstützt oder als Ausgleichstransaktion am besten behandelt wird.
Erwägen Sie, dass Clients einen idempotenten Schlüssel (z. B. in einem
Idempotency-KeyAnforderungsheader) angeben müssen, wenn die ursprüngliche Anforderung übermittelt wird. Wenn das Backend einen doppelten Schlüssel empfängt, sollte die vorhandene Statusressource zurückgegeben werden, anstatt eine zweite Aufgabe in die Warteschlange zu stellen. Dieser Ansatz schützt vor Netzwerkfehlern, die dazu führen, dass der Client einen POST erneut versucht, den der Server bereits akzeptiert hat. Es ist besonders wichtig in diesem Muster, da der Client keine Möglichkeit hat, eine verlorene Antwort von einer Anforderung zu unterscheiden, die nie empfangen wurde.
Hinweis
Dieses Muster beschreibt die HTTP-Abfrage, bei der der Client regelmäßig neue Anforderungen ausgibt, um den Status zu überprüfen. Long Polling ist eine ähnliche, aber unterschiedliche Technik: Der Client sendet eine Anfrage, und der Server hält die Verbindung geöffnet, bis neue Daten verfügbar sind oder eine Zeitüberschreitung auftritt. Long Polling reduziert die Antwortlatenz im Vergleich zu regelmäßigen Abfragen, führt jedoch zu einer erhöhten Komplexität bei der Verbindungsverwaltung und den Timeouts.
Wann Sie dieses Muster verwenden sollten
Verwenden Sie dieses Muster in folgenden Fällen:
Sie arbeiten mit clientseitigem Code, wie z. B. Browseranwendungen, und aufgrund dieser Einschränkungen sind Callback-Endpunkte schwer bereitzustellen, oder lang andauernde Verbindungen bringen zu viel Komplexität mit sich.
Sie rufen einen Dienst auf, der nur das HTTP-Protokoll verwendet und der Rückgabedienst aufgrund von Firewalleinschränkungen auf clientseitiger Seite keine Rückrufe senden kann.
Sie integrieren sich mit Workloads, die moderne Rückrufmechanismen wie WebSockets oder Webhooks nicht unterstützen.
Dieses Muster ist möglicherweise nicht geeignet, wenn:
Sie können stattdessen einen Dienst verwenden, der für asynchrone Benachrichtigungen erstellt wurde, z. B. Azure Event Grid.
Antworten müssen in Echtzeit an den Client gestreamt werden. Erwägen Sie Server-Sent Ereignisse (SSE), die einen einfachen, http-nativen, unidirektionalen Pushkanal vom Server zum Client bereitstellen, ohne dass der Client abfragen muss.
Der Client muss viele Ergebnisse sammeln, und die Latenz dieser Ergebnisse ist wichtig. Erwägen Sie stattdessen einen Nachrichtenbroker.
Serverseitige persistente Netzwerkverbindungen wie WebSockets oder SignalR sind verfügbar. Sie können diese Verbindungen verwenden, um den Anrufer über das Ergebnis zu benachrichtigen.
Das Netzwerkdesign unterstützt offene Ports zum Empfangen asynchroner Rückrufe oder Webhooks.
Workloadentwurf
Ein Architekt sollte bewerten, wie sie/er das Muster "Asynchrones Request-Reply" im Entwurf seines Workloads verwenden kann, um auf die Ziele und Prinzipien einzugehen, die in den Azure Well-Architected Framework-Säulen behandelt werden.
| Säule | So unterstützt dieses Muster die Säulenziele |
|---|---|
| Performance Efficiency hilft Ihrem Workload durch Optimierungen bei Skalierung, Daten und Code, die Anforderungen effizient zu erfüllen . | Sie verbessern die Reaktionsfähigkeit und Skalierbarkeit, indem Sie die Anforderungs- und Antwortphasen für Prozesse entkoppeln, die keine sofortige Antwort erfordern. Ein asynchroner Ansatz erhöht die Parallelität und ermöglicht es dem Server, die Arbeit zu planen, sobald die Kapazität verfügbar wird. - PE:05 Skalierung und Partitionierung - PE:07-Code und -Infrastruktur |
Wie bei jeder Entwurfsentscheidung sollten Sie Abwägungen im Hinblick auf die Ziele der anderen Säulen vornehmen, die dieses Muster einführen könnte.
Beispiel
Der folgende Code zeigt Auszüge aus einer Anwendung, die Azure Functions verwendet, um dieses Muster zu implementieren. Diese Lösung hat drei Funktionen:
- Der asynchrone API-Endpunkt
- Der Statusendpunkt
- Eine Back-End-Funktion, die Auftragsobjekte aus der Warteschlange verarbeitet und ausführt.
Dieses Beispiel ist auf GitHub verfügbar.
Die Implementierung verwendet verwaltete Identität zur Authentifizierung bei Azure Service Bus und Azure Blob Storage, wodurch keine Verbindungszeichenfolgen oder Kontoschlüssel gespeichert werden. Abhängigkeiten werden in Program.cs mithilfe DefaultAzureCredential registriert und durch primäre Konstruktoren eingefügt.
AsyncProcessingWorkAcceptor-Funktion
Die AsyncProcessingWorkAcceptor Funktion implementiert einen Endpunkt, der Arbeit von einer Clientanwendung akzeptiert und für die Verarbeitung queuesiert:
Die Funktion generiert eine Anforderungs-ID und fügt sie der Warteschlangennachricht als Metadaten hinzu.
Die HTTP-Antwort enthält einen
LocationHeader, der auf einen Statusendpunkt verweist, und einen Header, der einRetry-AfterAbrufintervall vorschlägt. Die Anforderungs-ID wird im URL-Pfad angezeigt.
public class AsyncProcessingWorkAcceptor(ServiceBusClient _serviceBusClient)
{
[Function("AsyncProcessingWorkAcceptor")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
[FromBody] CustomerPOCO customer)
{
if (string.IsNullOrEmpty(customer.id) || string.IsNullOrEmpty(customer.customername))
{
return new BadRequestResult();
}
string requestId = Guid.NewGuid().ToString();
string statusUrl = $"https://{Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME")}/api/RequestStatus/{requestId}";
var messagePayload = JsonConvert.SerializeObject(customer);
var message = new ServiceBusMessage(messagePayload);
message.ApplicationProperties.Add("RequestGUID", requestId);
message.ApplicationProperties.Add("RequestSubmittedAt", DateTime.UtcNow);
message.ApplicationProperties.Add("RequestStatusURL", statusUrl);
var sender = _serviceBusClient.CreateSender("outqueue");
await sender.SendMessageAsync(message);
req.HttpContext.Response.Headers["Retry-After"] = "5";
return new AcceptedResult(statusUrl, null);
}
}
AsyncProcessingBackgroundWorker-Funktion
Die AsyncProcessingBackgroundWorker Funktion liest den Vorgang aus der Warteschlange, verarbeitet ihn basierend auf der Nachrichtennutzlast und schreibt das Ergebnis in ein Speicherkonto.
public class AsyncProcessingBackgroundWorker(BlobContainerClient _blobContainerClient)
{
[Function("AsyncProcessingBackgroundWorker")]
public async Task Run(
[ServiceBusTrigger("outqueue", Connection = "ServiceBusConnection")] ServiceBusReceivedMessage message)
{
// Perform an actual action against the blob data source for the async readers to be able to check against.
// This is where your actual service worker processing will be performed
var requestGuid = message.ApplicationProperties["RequestGUID"].ToString();
string blobName = $"{requestGuid}.blobdata";
var blobClient = _blobContainerClient.GetBlobClient(blobName);
using (MemoryStream memoryStream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(memoryStream))
{
writer.Write(message.Body.ToString());
writer.Flush();
memoryStream.Position = 0;
await blobClient.UploadAsync(memoryStream, overwrite: true);
}
}
}
AsyncOperationStatusChecker-Funktion
Die AsyncOperationStatusChecker-Funktion implementiert den Statusendpunkt. Diese Funktion überprüft den Status der Anforderung:
Wenn die Anforderung abgeschlossen ist, gibt die Funktion HTTP 303 (Siehe Andere) zurück, wobei der Client zu einer Valet-Schlüssel-URL für das Ergebnis umgeleitet wird.
Wenn die Anforderung aussteht, gibt die Funktion einen HTTP 200-Code zurück, der den aktuellen Zustand enthält.
public class AsyncOperationStatusChecker(ILogger<AsyncOperationStatusChecker> _logger)
{
[Function("AsyncOperationStatusChecker")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "RequestStatus/{requestId}")] HttpRequest req,
[BlobInput("data/{requestId}.blobdata", Connection = "DataStorage")] BlockBlobClient inputBlob, string requestId)
{
OnCompleteEnum OnComplete = Enum.Parse<OnCompleteEnum>(req.Query["OnComplete"].FirstOrDefault() ?? "Redirect");
OnPendingEnum OnPending = Enum.Parse<OnPendingEnum>(req.Query["OnPending"].FirstOrDefault() ?? "OK");
_logger.LogInformation("Received status request for {RequestId} - OnComplete {OnComplete} - OnPending {OnPending}",
requestId, OnComplete, OnPending);
// Check whether the blob exists.
if (await inputBlob.ExistsAsync())
{
// If the blob exists, the function uses the OnComplete parameter to determine the next action.
return await OnCompleted(OnComplete, inputBlob, requestId, req);
}
else
{
// If the blob doesn't exist, the function uses the OnPending parameter to determine the next action.
switch (OnPending)
{
case OnPendingEnum.OK:
{
// Return an HTTP 200 status code.
return new OkObjectResult(new { status = "In progress", Location = rqs });
}
case OnPendingEnum.Synchronous:
{
// Long polling example: hold the connection open and check for completion
// using exponential backoff. Time out after approximately one minute.
int backoff = 250;
while (!await inputBlob.ExistsAsync() && backoff < 64000)
{
_logger.LogInformation("Synchronous mode {RequestId} - retrying in {Backoff} ms", requestId, backoff);
backoff = backoff * 2;
await Task.Delay(backoff);
}
if (await inputBlob.ExistsAsync())
{
_logger.LogInformation("Synchronous mode {RequestId} - completed after {Backoff} ms", requestId, backoff);
return await OnCompleted(OnComplete, inputBlob, requestId, req);
}
else
{
_logger.LogInformation("Synchronous mode {RequestId} - NOT FOUND after timeout {Backoff} ms", requestId, backoff);
return new NotFoundResult();
}
}
default:
{
throw new InvalidOperationException($"Unexpected value: {OnPending}");
}
}
}
}
private async Task<IActionResult> OnCompleted(OnCompleteEnum OnComplete, BlockBlobClient inputBlob, string requestId, HttpRequest req)
{
switch (OnComplete)
{
case OnCompleteEnum.Redirect:
{
// Generate a user delegation SAS URI using managed identity credentials.
BlobServiceClient blobServiceClient = inputBlob.GetParentBlobContainerClient().GetParentBlobServiceClient();
var userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(7));
// Return 303 See Other to redirect the client to the result resource.
// GenerateUserDelegationSasUri is a custom helper; see the full implementation on GitHub.
req.HttpContext.Response.Headers.Location = GenerateUserDelegationSasUri(inputBlob, userDelegationKey);;
return new StatusCodeResult(StatusCodes.Status303SeeOther);
}
case OnCompleteEnum.Stream:
{
// Download the file and return it directly to the caller.
// For larger files, use a stream to minimize RAM usage.
return new OkObjectResult(await inputBlob.DownloadContentAsync());
}
default:
{
throw new InvalidOperationException($"Unexpected value: {OnComplete}");
}
}
}
}
public enum OnCompleteEnum
{
Redirect,
Stream
}
public enum OnPendingEnum
{
OK,
Synchronous
}
Nächste Schritte
- Azure Logic Apps – Asynchrones Anforderungsantwortverhalten.
- Allgemeine bewährte Methoden zum Entwerfen einer Web-API finden Sie unter API-Design.