Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Il modello di interazione umana descrive i flussi di lavoro che sospendono e attendono l'input da una persona prima di continuare. Il modello è utile per i flussi di lavoro di approvazione, l'autenticazione a più fattori e qualsiasi scenario in cui una persona risponde entro un limite di tempo.
A livello generale, il modello funziona come segue:
- L'orchestratore chiama un'attività per notificare una persona (inviare un codice SMS, inviare un messaggio di posta elettronica a un approvatore e così via).
- L'orchestratore avvia un timer durevole e attende simultaneamente un evento esterno dall'utente.
- Se la persona risponde prima dell'attivazione del timer, l'agente di orchestrazione elabora la risposta.
- Se il timer viene attivato per primo, l'agente di orchestrazione gestisce il timeout, ad esempio rifiutando la richiesta.
Contenuto dell'articolo:
- Panoramica dello scenario di interazione umana - Perché questo modello è importante
- Definire l'agente di orchestrazione : implementare la gara timer-vs-event
- Definire le attività - Inviare notifiche ed elaborare i risultati
- Eseguire l'esempio di interazione umana - Testare il flusso di lavoro di testa a coda
Annotazioni
Gli esempi di Durable Functions e Durable Task SDK illustrano lo stesso modello con scenari diversi: Durable Functions usa un esempio di verifica SMS, mentre Durable Task SDK utilizza un esempio di flusso di lavoro di approvazione.
Questo esempio illustra come creare un'orchestrazione Durable Functions che include l'interazione umana. L'esempio implementa un sistema di verifica telefono basato su SMS. È comune nei flussi di verifica dei numeri di telefono e dell'autenticazione a più fattori (MFA).
Annotazioni
Gli esempi di codice completi sono disponibili per C#, JavaScript e Python. Gli esempi di PowerShell e Java non sono attualmente disponibili.
Annotazioni
La versione 4 del modello di programmazione Node.js per Funzioni di Azure è disponibile a livello generale. Il modello v4 è progettato per offrire un'esperienza più flessibile e intuitiva per sviluppatori JavaScript e TypeScript. Per altre informazioni sulle differenze tra v3 e v4, vedere la guida alla migrazione.
Nei frammenti di codice seguenti JavaScript (PM4) indica il modello di programmazione v4, la nuova esperienza.
Prerequisiti
- Completare l'articolo introduttivo
- Clonare o scaricare il progetto di esempio da GitHub (utilizza il modello in-process)
Questo articolo illustra come implementare il modello di interazione umana usando gli SDK di Durable Task. Nell'esempio viene implementato un flusso di lavoro di approvazione in cui un'orchestrazione attende che una persona approvi o rifiuti una richiesta prima che continui.
Panoramica dello scenario di interazione umana
La verifica telefonica consente di verificare che le persone che usano l'app non siano spammer e che controllino il numero di telefono fornito. L'autenticazione a più fattori è un modo comune per proteggere gli account. La creazione di una verifica tramite telefono personalizzata richiede un'interazione con stato con una persona. Un utente ottiene in genere un codice (ad esempio, un numero a quattro cifre) e deve rispondere in un periodo di tempo ragionevole.
I Funzioni di Azure standard sono senza stato (come molti altri endpoint cloud), quindi questo tipo di interazione richiede di archiviare lo stato in un database o in un altro archivio permanente. È anche possibile suddividere l'interazione tra più funzioni e coordinarle. Ad esempio, una funzione genera un codice, lo archivia e lo invia al telefono dell'utente. Un'altra funzione riceve la risposta dell'utente e la mappa alla richiesta originale per convalidare il codice. Aggiungere un timeout per proteggere la sicurezza. Questo flusso di lavoro diventa rapidamente complesso.
Durable Functions riduce la complessità di questo scenario. In questo esempio, una funzione di orchestrazione gestisce l'interazione stateful senza un archivio dati esterno. Poiché le funzioni dell'agente di orchestrazione sono durevoli, questi flussi interattivi sono altamente affidabili.
I flussi di lavoro di approvazione sono comuni nelle applicazioni aziendali in cui una richiesta deve essere esaminata da un essere umano prima di procedere. I requisiti del flusso di lavoro sono:
- Attendere indefinitamente per una risposta umana o fino a un timeout
- Gestire sia i risultati di approvazione che di rifiuto
- Timeout di supporto quando non si riceve alcuna risposta
- Tenere traccia dello stato in modo che il richiedente possa controllare lo stato di avanzamento
Gli SDK per attività durevoli semplificano questo scenario con:
- Eventi esterni: l'orchestrazione può sospendere e attendere un evento generato da un sistema esterno o da un utente
- Timer durevoli: impostare un timeout che si attiva se non si riceve alcuna risposta
- Stato personalizzato: tenere traccia ed esporre lo stato corrente del flusso di lavoro ai client
Configurare l'integrazione di Twilio
Questo esempio prevede l'uso del servizio Twilio per inviare messaggi SMS al telefono cellulare. Funzioni di Azure ha già supporto per Twilio tramite l'associazione Twilio e l'esempio usa tale funzionalità.
È necessario per prima cosa disporre di un account Twilio. È possibile crearne uno gratuitamente all'indirizzo https://www.twilio.com/try-twilio. Dopo aver creato un account, aggiungere le seguenti tre impostazioni dell'app alla propria app per le funzioni.
| Nome dell'impostazione dell'app | Descrizione del valore |
|---|---|
| TwilioAccountSid | SID dell'account Twilio |
| TwilioAuthToken | Token di autenticazione per l'account Twilio |
| TwilioPhoneNumber | Numero di telefono associato all'account Twilio usato per inviare messaggi SMS. |
Definire l'orchestratore
Funzione di orchestrazione E4_SmsPhoneVerification
[FunctionName("E4_SmsPhoneVerification")]
public static async Task<bool> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
string phoneNumber = context.GetInput<string>();
if (string.IsNullOrEmpty(phoneNumber))
{
throw new ArgumentNullException(
nameof(phoneNumber),
"A phone number input is required.");
}
int challengeCode = await context.CallActivityAsync<int>(
"E4_SendSmsChallenge",
phoneNumber);
using (var timeoutCts = new CancellationTokenSource())
{
// The user has 90 seconds to respond with the code they received in the SMS message.
DateTime expiration = context.CurrentUtcDateTime.AddSeconds(90);
Task timeoutTask = context.CreateTimer(expiration, timeoutCts.Token);
bool authorized = false;
for (int retryCount = 0; retryCount <= 3; retryCount++)
{
Task<int> challengeResponseTask =
context.WaitForExternalEvent<int>("SmsChallengeResponse");
Task winner = await Task.WhenAny(challengeResponseTask, timeoutTask);
if (winner == challengeResponseTask)
{
// We got back a response! Compare it to the challenge code.
if (challengeResponseTask.Result == challengeCode)
{
authorized = true;
break;
}
}
else
{
// Timeout expired
break;
}
}
if (!timeoutTask.IsCompleted)
{
// All pending timers must be complete or canceled before the function exits.
timeoutCts.Cancel();
}
return authorized;
}
}
Annotazioni
Potrebbe non essere ovvio in un primo momento, ma questo agente di orchestrazione non viola il vincolo di orchestrazione deterministico. È deterministico perché la CurrentUtcDateTime proprietà calcola l'ora di scadenza del timer e restituisce lo stesso valore in ogni riproduzione in questo punto nel codice dell'agente di orchestrazione. Questo comportamento garantisce che winner sia lo stesso per ogni chiamata ripetuta a Task.WhenAny.
Una volta avviata, questa funzione di orchestrazione esegue le seguenti operazioni:
- Ottiene un numero di telefono a cui inviare la notifica SMS.
- Chiama E4_SendSmsChallenge per inviare un messaggio SMS all'utente e restituisce il codice di verifica a quattro cifre previsto.
- Crea un timer durevole che attiva 90 secondi dopo l'ora corrente.
- In parallelo con il timer, si attende un evento SmsChallengeResponse da parte dell'utente.
L'utente riceve un messaggio SMS con un codice di quattro cifre Hanno 90 secondi per inviare lo stesso codice all'istanza orchestratore per completare la verifica. Se inviano il codice errato, ottengono altri tre tentativi nella stessa finestra di 90 secondi.
Avvertimento
Annullare i timer non più necessari. Nell'esempio precedente, l'orchestrazione annulla il timer quando accetta una risposta alla sfida.
L'agente di orchestrazione invia una richiesta di approvazione, quindi attende una risposta umana o un timeout.
using Microsoft.DurableTask;
using System;
using System.Threading;
using System.Threading.Tasks;
[DurableTask(nameof(ApprovalOrchestration))]
public class ApprovalOrchestration : TaskOrchestrator<ApprovalRequestData, ApprovalResult>
{
public override async Task<ApprovalResult> RunAsync(
TaskOrchestrationContext context, ApprovalRequestData input)
{
string requestId = input.RequestId;
double timeoutHours = input.TimeoutHours;
// Step 1: Submit the approval request (notify approver)
SubmissionResult submissionResult = await context.CallActivityAsync<SubmissionResult>(
nameof(SubmitApprovalRequestActivity), input);
// Make the status available via custom status
context.SetCustomStatus(submissionResult);
// Step 2: Create a durable timer for the timeout
DateTime timeoutDeadline = context.CurrentUtcDateTime.AddHours(timeoutHours);
using var timeoutCts = new CancellationTokenSource();
Task timeoutTask = context.CreateTimer(timeoutDeadline, timeoutCts.Token);
// Step 3: Wait for an external event (approval/rejection)
Task<ApprovalResponseData> approvalTask = context.WaitForExternalEvent<ApprovalResponseData>(
"approval_response");
// Step 4: Wait for either the timeout or the approval response
Task completedTask = await Task.WhenAny(approvalTask, timeoutTask);
// Step 5: Process based on which task completed
ApprovalResult result;
if (completedTask == approvalTask)
{
// Human responded in time - cancel the timeout timer
timeoutCts.Cancel();
ApprovalResponseData approvalData = approvalTask.Result;
// Process the approval
result = await context.CallActivityAsync<ApprovalResult>(
nameof(ProcessApprovalActivity),
new ProcessApprovalInput
{
RequestId = requestId,
IsApproved = approvalData.IsApproved,
Approver = approvalData.Approver
});
}
else
{
// Timeout occurred
result = new ApprovalResult
{
RequestId = requestId,
Status = "Timeout",
ProcessedAt = context.CurrentUtcDateTime.ToString("o")
};
}
return result;
}
}
Questo agente di orchestrazione esegue le azioni seguenti:
- Invia la richiesta di approvazione chiamando un'attività che invia una notifica al responsabile dell'approvazione.
- Imposta lo stato personalizzato in modo che i client possano tenere traccia dello stato di avanzamento.
- Crea un timer durevole per la scadenza del timeout.
- Attende un evento esterno (
approval_response) generato dal responsabile approvazione. - Usa
WhenAny,when_anyoanyOfper attendere quella che si completa per prima: l'approvazione o il timeout. - Elabora il risultato in base all'attività completata.
Avvertimento
Annullare i timer non più necessari. Nell'esempio C# l'orchestrazione annulla il timer di timeout quando riceve l'approvazione.
Definire le attività
Funzione dell’attività E4_SendSmsChallenge
La funzione E4_SendSmsChallenge usa l'associazione Twilio per inviare un messaggio SMS che include un codice a quattro cifre all'utente.
[FunctionName("E4_SendSmsChallenge")]
public static int SendSmsChallenge(
[ActivityTrigger] string phoneNumber,
ILogger log,
[TwilioSms(AccountSidSetting = "TwilioAccountSid", AuthTokenSetting = "TwilioAuthToken", From = "%TwilioPhoneNumber%")]
out CreateMessageOptions message)
{
// Get a random number generator with a random seed (not time-based)
var rand = new Random(Guid.NewGuid().GetHashCode());
int challengeCode = rand.Next(10000);
log.LogInformation($"Sending verification code {challengeCode} to {phoneNumber}.");
message = new CreateMessageOptions(new PhoneNumber(phoneNumber));
message.Body = $"Your verification code is {challengeCode:0000}";
return challengeCode;
}
Annotazioni
Per eseguire l'esempio, installare il pacchetto NuGet Microsoft.Azure.WebJobs.Extensions.Twilio. Non installare il pacchetto NuGet Twilio principale perché può causare conflitti di versione ed errori di compilazione.
Le attività inviano la richiesta di approvazione ed elaborano la risposta.
Invia l'attività di richiesta di approvazione
using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
[DurableTask(nameof(SubmitApprovalRequestActivity))]
public class SubmitApprovalRequestActivity : TaskActivity<ApprovalRequestData, SubmissionResult>
{
private readonly ILogger<SubmitApprovalRequestActivity> _logger;
public SubmitApprovalRequestActivity(ILogger<SubmitApprovalRequestActivity> logger)
{
_logger = logger;
}
public override Task<SubmissionResult> RunAsync(
TaskActivityContext context, ApprovalRequestData input)
{
_logger.LogInformation(
"Submitting approval request {RequestId} from {Requester} for {Item}",
input.RequestId, input.Requester, input.Item);
// In a real system, this would send an email, notification, or update a database
var result = new SubmissionResult
{
RequestId = input.RequestId,
Status = "Pending",
SubmittedAt = DateTime.UtcNow.ToString("o"),
ApprovalUrl = $"http://localhost:8000/api/approvals/{input.RequestId}"
};
return Task.FromResult(result);
}
}
Attività di approvazione del processo
using Microsoft.DurableTask;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
[DurableTask(nameof(ProcessApprovalActivity))]
public class ProcessApprovalActivity : TaskActivity<ProcessApprovalInput, ApprovalResult>
{
private readonly ILogger<ProcessApprovalActivity> _logger;
public ProcessApprovalActivity(ILogger<ProcessApprovalActivity> logger)
{
_logger = logger;
}
public override Task<ApprovalResult> RunAsync(
TaskActivityContext context, ProcessApprovalInput input)
{
string status = input.IsApproved ? "Approved" : "Rejected";
_logger.LogInformation(
"Processing {Status} request {RequestId} by {Approver}",
status, input.RequestId, input.Approver);
// In a real system, this would update a database, trigger workflows, etc.
var result = new ApprovalResult
{
RequestId = input.RequestId,
Status = status,
ProcessedAt = DateTime.UtcNow.ToString("o"),
Approver = input.Approver
};
return Task.FromResult(result);
}
}
// Data classes
public class ApprovalRequestData
{
public string RequestId { get; set; } = string.Empty;
public string Requester { get; set; } = string.Empty;
public string Item { get; set; } = string.Empty;
public double TimeoutHours { get; set; } = 24.0;
}
public class ApprovalResponseData
{
public bool IsApproved { get; set; }
public string Approver { get; set; } = string.Empty;
}
public class SubmissionResult
{
public string RequestId { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public string SubmittedAt { get; set; } = string.Empty;
public string ApprovalUrl { get; set; } = string.Empty;
}
public class ProcessApprovalInput
{
public string RequestId { get; set; } = string.Empty;
public bool IsApproved { get; set; }
public string Approver { get; set; } = string.Empty;
}
public class ApprovalResult
{
public string RequestId { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public string ProcessedAt { get; set; } = string.Empty;
public string? Approver { get; set; }
}
Eseguire l'esempio di interazione umana
Usare le funzioni attivate da HTTP nell'esempio per avviare l'orchestrazione inviando la richiesta HTTP POST seguente:
POST http://{host}/orchestrators/E4_SmsPhoneVerification
Content-Length: 14
Content-Type: application/json
"+1425XXXXXXX"
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
{"id":"741c65651d4c40cea29acdd5bb47baf1",
"sendEventPostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/{eventName}?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}",
"statusQueryGetUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=...&code={systemKey}",
"terminatePostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/terminate?reason={text}&taskHub=...&code={systemKey}"}
La funzione dell'agente di orchestrazione riceve il numero di telefono e invia immediatamente un messaggio SMS a tale numero con un codice di verifica a 4 cifre generato in modo casuale, 2168ad esempio . La funzione attende quindi 90 secondi per ricevere una risposta.
Per rispondere con il codice, usare RaiseEventAsync (.NET) o raiseEvent (JavaScript e TypeScript) in un'altra funzione oppure chiamare sendEventPostUri endpoint HTTP POST nella risposta 202. Sostituire {eventName} con SmsChallengeResponse:
POST http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/SmsChallengeResponse?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
Content-Length: 4
Content-Type: application/json
2168
Se si invia l'evento prima della scadenza del timer, l'orchestrazione viene completata e il campo output viene impostato su true, che indica un esito positivo della verifica.
GET http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
HTTP/1.1 200 OK
Content-Length: 144
Content-Type: application/json; charset=utf-8
{"runtimeStatus":"Completed","input":"+1425XXXXXXX","output":true,"createdTime":"2026-04-23T19:10:49Z","lastUpdatedTime":"2026-04-23T19:12:23Z"}
Se il timer scade o si inserisce il codice errato quattro volte, eseguire una query di stato per vedere output impostato su false, che indica che la verifica del telefono non è riuscita.
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 145
{"runtimeStatus":"Completed","input":"+1425XXXXXXX","output":false,"createdTime":"2026-04-23T19:20:49Z","lastUpdatedTime":"2026-04-23T19:22:23Z"}
Per eseguire l'esempio:
Avvia l'emulatore del Durable Task Scheduler per lo sviluppo locale. Docker deve essere installato.
docker run -d -p 8080:8080 -p 8082:8082 --name dts-emulator mcr.microsoft.com/dts/dts-emulator:latestAvviare il processo di lavoro per registrare l'agente di orchestrazione e le attività.
Eseguire il client per pianificare un flusso di lavoro di approvazione e inviare eventi.
using System;
using System.Threading.Tasks;
var client = DurableTaskClientBuilder.UseDurableTaskScheduler(connectionString).Build();
// Schedule the approval workflow
var input = new ApprovalRequestData
{
RequestId = "request-" + Guid.NewGuid().ToString(),
Requester = "john.doe@example.com",
Item = "Vacation Request - 5 days",
TimeoutHours = 24
};
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(ApprovalOrchestration), input);
Console.WriteLine($"Started approval workflow: {instanceId}");
// Simulate human approving the request
Console.WriteLine("Simulating approval...");
await Task.Delay(2000);
// Raise the approval event
var approvalResponse = new ApprovalResponseData
{
IsApproved = true,
Approver = "manager@example.com"
};
await client.RaiseEventAsync(instanceId, "approval_response", approvalResponse);
// Wait for completion
var result = await client.WaitForInstanceCompletionAsync(instanceId, getInputsAndOutputs: true);
Console.WriteLine($"Result: {result.ReadOutputAs<ApprovalResult>().Status}");
Passaggi successivi
Questo esempio illustra le funzionalità avanzate di Durable Functions, incluse le API /> /Task.WhenAny (C#), context.df.Task.any (JavaScript e TypeScript) o context.task_any (Python) per implementare un modello di timeout affidabile per i flussi di lavoro che attendono la risposta degli utenti.
Questo esempio illustra come usare gli SDK di Durable Task per implementare flussi di lavoro che attendono che gli utenti rispondano, con timeout configurabili. Concetti chiave:
-
Eventi esterni: uso di
WaitForExternalEventper attendere l'input -
Timer durevoli: utilizzo di
CreateTimerper implementare i timeout -
Attività in corso: uso di
WhenAny,when_anyoanyOfper gestire qualsiasi attività venga completata per prima