İnsan etkileşimi örüntüsü

İnsan etkileşimi düzeni, devam etmeden önce bir kişinin girişini duraklatan ve bekleyen iş akışlarını açıklar. Desen onay iş akışları, çok faktörlü kimlik doğrulaması ve bir kişinin zaman sınırı içinde yanıt verdiği tüm senaryolar için kullanışlıdır.

Desen yüksek düzeyde aşağıdaki gibi çalışır:

  1. Orkestratör, bir kişiyi bilgilendirmek için bir etkinlik çağırır (SMS kodu göndererek, onaylayıcıya e-posta göndererek vb.).
  2. Orchestrator dayanıklı bir zamanlayıcı başlatır ve aynı anda bir kişiden gelen dış olayı bekler.
  3. Zamanlayıcı tetiklemeden önce kişi yanıt verirse, düzenleyici yanıtı işler.
  4. Zamanlayıcı ilk önce tetiklenirse, düzenleyici zaman aşımını işler (örneğin, isteği reddederek).

Bu makalede:

Uyarı

Dayanıklı İşlevler ve Dayanıklı Görev SDK'sı özetleri farklı senaryolarla aynı deseni gösterir: Dayanıklı İşlevler SMS telefon doğrulaması örneği kullanırken Dayanıklı Görev SDK'ları bir uygulama iş akışı örneği kullanır.

Bu örnek, insan etkileşimi içeren bir Dayanıklı İşlevler düzenlemesi oluşturmayı gösterir. Örnek, SMS tabanlı bir telefon doğrulama sistemi uygular. Telefon numarası doğrulama ve çok faktörlü kimlik doğrulaması (MFA) akışlarında yaygındır.

Uyarı

C#, JavaScript ve Python için tam kod örnekleri sağlanır. PowerShell ve Java örnekleri şu anda kullanılamıyor.

Uyarı

Azure İşlevleri için Node.js programlama modelinin 4. sürümü genel olarak kullanılabilir. v4 modeli, JavaScript ve TypeScript geliştiricileri için daha esnek ve sezgisel bir deneyim sağlamak üzere tasarlanmıştır. v3 ile v4 arasındaki farklar hakkında daha fazla bilgi için geçiş kılavuzuna bakın.

Aşağıdaki kod parçacıklarında JavaScript (PM4), yeni deneyim olan v4 programlama modelini belirtir.

Önkoşullar

Bu makalede, Dayanıklı Görev SDK'larını kullanarak insan etkileşimi deseninin nasıl uygulandığı gösterilmektedir. Örnek, bir orkestrasyonun devam etmeden önce bir kişinin bir isteği onaylamasını veya reddetmesini beklediği bir onay iş akışı uygular.

İnsan etkileşimi senaryosuna genel bakış

Telefon doğrulaması, uygulamanızı kullanan kişilerin spam gönderen olmadığını ve sağladıkları telefon numarasını denetlediklerini doğrulamaya yardımcı olur. Çok faktörlü kimlik doğrulaması, hesapları korumanın yaygın bir yoludur. Kendi telefon doğrulamanızı oluşturmak için bir kişiyle durumlu etkileşim gerekir. Kullanıcı genellikle bir kod (örneğin, dört basamaklı bir sayı) alır ve makul bir süre içinde yanıt vermesi gerekir.

Standart Azure İşlevleri durum bilgisi yoktur (diğer birçok bulut uç noktası gibi), bu tür etkileşimler durumu bir veritabanında veya başka bir kalıcı depoda depolamanızı gerektirir. Ayrıca etkileşimi birden çok işleve böler ve eşgüdümlü olarak düzenlersiniz. Örneğin, bir işlev bir kod oluşturur, depolar ve kullanıcının telefonuna gönderir. Başka bir işlev kullanıcının yanıtını alır ve kodu doğrulamak için özgün istekle eşler. Güvenliği korumaya yardımcı olmak için zaman aşımı ekleyin. Bu iş akışı hızla karmaşıklaşır.

Dayanıklı İşlevler bu senaryonun karmaşıklığını azaltır. Bu örnekte, bir düzenleyici işlev, durum bilgili etkileşimi dış veri deposu kullanmaksızın yönetir. Orchestrator işlevleri dayanıklı olduğundan, bu etkileşimli akışlar son derece güvenilirdir.

Onay iş akışları, devam etmeden önce bir isteğin bir insan tarafından gözden geçirilmesi gereken iş uygulamalarında yaygındır. İş akışı gereksinimleri şunlardır:

  • Bir insan yanıtı için süresiz veya zaman aşımına kadar bekleyin
  • Hem onay hem de reddetme sonuçlarını işleme
  • Yanıt alınmadığında destek zaman aşımları
  • İstek sahibinin ilerlemeyi kontrol edebilmesi için durum takibi yapın

Dayanıklı Görev SDK'ları aşağıdakilerle bu senaryoyu basitleştirir:

  • Dış olaylar: Düzenleme, bir dış sistem veya kullanıcı tarafından tetiklenen bir olayı duraklatabilir ve bekleyebilir
  • Dayanıklı zamanlayıcılar: Yanıt alınamazsa devreye giren bir zaman aşımı ayarlamak
  • Özel durum: Geçerli iş akışı durumunu izleme ve istemcilere sunma

Twilio entegrasyonunu yapılandırma

Bu örnek, cep telefonuna SMS mesajları göndermek için Twilio hizmetini kullanmayı içerir. Azure İşlevleri zaten Twilio bağlaması aracılığıyla Twilio desteğine sahiptir ve örnek bu özelliği kullanır.

İhtiyacınız olan ilk şey bir Twilio hesabıdır. adresinde https://www.twilio.com/try-twilioücretsiz bir tane oluşturabilirsiniz. Hesabınız olduktan sonra aşağıdaki üç uygulama ayarlarını işlev uygulamanıza ekleyin.

Uygulama ayarı adı Değer açıklaması
TwilioAccountSid Twilio hesabınızın SID değeri
TwilioAuthToken Twilio hesabınız için Kimlik Doğrulama belirteci
TwilioPhoneNumber Twilio hesabınızla ilişkili telefon numarası. Bu, SMS iletileri göndermek için kullanılır.

Düzenleyiciyi tanımlama

E4_SmsPhoneVerification orchestrator işlevi

[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;
    }
}

Uyarı

İlk başta belirgin olmayabilir, ancak bu düzenleyici belirlenimci düzenleme kısıtlamasını ihlal etmez. Bu belirleyicidir çünkü CurrentUtcDateTime özelliği zamanlayıcı süre sonu süresini hesaplar ve orchestrator kodunda bu noktadaki her yeniden yürütmede aynı değeri döndürür. Bu davranış, winner öğesinin Task.WhenAny işlevine yapılan her yinelenen çağrıda aynı olmasını garanti eder.

Bu düzenleyici işlevi başlatıldıktan sonra aşağıdakileri yapar:

  1. SMS bildirimi göndermek için bir telefon numarası alır.
  2. Kullanıcıya SMS iletisi göndermek için E4_SendSmsChallenge çağırır ve beklenen dört basamaklı sınama kodunu döndürür.
  3. Geçerli saatten 90 saniye sonra tetikleyen dayanıklı bir zamanlayıcı oluşturur.
  4. Zamanlayıcıya paralel olarak, kullanıcıdan gelen bir SmsChallengeResponse olayını bekler.

Kullanıcı dört basamaklı kod içeren bir SMS iletisi alır. Doğrulamayı tamamlamak için orchestrator örneğine aynı kodu göndermek için 90 saniyeleri vardır. Yanlış kodu gönderirlerse, aynı 90 saniyelik pencerede üç deneme daha yaparlar.

Uyarı

Artık ihtiyacınız olmayan zamanlayıcıları iptal edin. Yukarıdaki örnekte düzenleme, bir sınama yanıtı kabul ettiğinde zamanlayıcıyı iptal eder.

Orkestratör bir onay isteği gönderir, ardından bir insan yanıtı veya zaman aşımı bekler.

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

Bu düzenleyici aşağıdaki eylemleri gerçekleştirir:

  1. Onaylayıcıyı bilgilendiren bir etkinliği çağırarak onay isteğini gönderir.
  2. İstemcilerin ilerleme durumunu izleyebilmesi için özel durumu ayarlar.
  3. Zaman aşımı süresi için dayanıklı bir zamanlayıcı oluşturur.
  4. Onaylayanın tetiklediği bir dış olayı (approval_response) bekler.
  5. İlk tamamlananı beklemek için WhenAny, when_any veya anyOf kullanır: onaylama veya zaman aşımı.
  6. Hangi görevin tamamlandığına bağlı olarak sonucu işler.

Uyarı

Artık ihtiyacınız olmayan zamanlayıcıları iptal edin. C# örneğinde düzenleme, onay aldığında zaman aşımı zamanlayıcısını iptal eder.

Etkinlikleri tanımlama

E4_SendSmsChallenge etkinlik işlevi

E4_SendSmsChallenge işlevi, kullanıcıya dört basamaklı bir kod içeren bir SMS iletisi göndermek için Twilio bağlamasını kullanır.

[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;
}

Uyarı

Örneği çalıştırmak için Microsoft.Azure.WebJobs.Extensions.Twilio NuGet paketini yükleyin. Sürüm çakışmasına ve derleme hatasına neden olabileceğinden ana Twilio NuGet paketini yüklemeyin.

Etkinlikler onay isteğini gönderir ve yanıtı işler.

Onay isteği etkinliğini gönderme

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

İşlem onayı etkinliği

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

İnsan etkileşimi örneğini çalıştırma

Aşağıdaki HTTP POST isteğini göndererek düzenlemeyi başlatmak için örnekteki HTTP ile tetiklenen işlevleri kullanın:

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

Orchestrator işlevi telefon numarasını alır ve rastgele oluşturulan 4 basamaklı doğrulama koduyla bu numaraya hemen bir SMS iletisi gönderir. Örneğin, 2168. İşlev daha sonra yanıt için 90 saniye bekler.

Kodu yanıtlamak için başka bir işlevde RaiseEventAsync (.NET) veya raiseEvent (JavaScript ve TypeScript) kullanın veya 202 yanıtında sendEventPostUri HTTP POST uç noktasını çağırın. {eventName} değerini SmsChallengeResponse ile değiştirin.

POST http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/SmsChallengeResponse?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}
Content-Length: 4
Content-Type: application/json

2168

Zamanlayıcının süresi dolmadan önce olayı gönderirseniz, orkestrasyon tamamlanır ve output alanı true olarak ayarlanır, bu da doğrulamanın başarılı olduğunu gösterir.

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

Süreölçerin süresi dolarsa veya dört kez yanlış kod girerseniz, durumunu sorgulayın. output değeri false olarak ayarlanmışsa, bu telefon doğrulamasının başarısız olduğunu gösterir.

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

Örneği çalıştırmak için:

  1. Yerel geliştirme için Dayanıklı Görev Zamanlayıcı öykünücüsü'ne başlayın. Docker yüklü olmalıdır.

    docker run -d -p 8080:8080 -p 8082:8082 --name dts-emulator mcr.microsoft.com/dts/dts-emulator:latest
    
  2. Düzenleyiciyi ve etkinlikleri kaydetmek için çalışanı başlatın.

  3. Bir onay iş akışı zamanlamak ve olayları göndermek için istemciyi çalıştırın.

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

Sonraki Adımlar

Bu örnek, WaitForExternalEvent ve CreateTimer API'leri de dahil olmak üzere gelişmiş Dayanıklı İşlevler özelliklerini gösterir. Task.WhenAny (C#), context.df.Task.any (JavaScript ve TypeScript) veya context.task_any (Python) birleştirerek kişilerin yanıt vermesini bekleyen iş akışları için güvenilir bir zaman aşımı deseni uygulamayı gösterir.

Bu örnek, yapılandırılabilir zaman aşımlarıyla kişilerin yanıt vermesini bekleyen iş akışlarını uygulamak için Dayanıklı Görev SDK'larının nasıl kullanılacağını gösterir. Temel kavramlar:

  • Dış olaylar: Girişi beklemek için kullanma WaitForExternalEvent
  • Dayanıklı zamanlayıcılar: Zaman aşımı gerçekleştirmek için CreateTimer kullanma
  • Yarış görevleri: İlk tamamlanan görevi işlemek için WhenAny, when_any, veya anyOf kullanma