Megosztás a következőn keresztül:


Emberi interakció a Durable Functionsben – Telefonos ellenőrzési minta

Ez a minta bemutatja, hogyan hozhat létre olyan Durable Functions-vezénylést , amely emberi interakciót is magában foglal. Amikor egy valós személy részt vesz egy automatizált folyamatban, a folyamatnak képesnek kell lennie értesítéseket küldeni a személynek, és aszinkron módon fogadni a válaszokat. Lehetővé kell tennie azt is, hogy a személy nem érhető el. (Ez az utolsó rész, ahol fontossá válnak az időtúllépések.)

Ez a minta egy SMS-alapú telefonos ellenőrző rendszert implementál. Az ilyen típusú folyamatokat gyakran használják az ügyfél telefonszámának ellenőrzésekor vagy többtényezős hitelesítéshez (MFA). Ez egy hatékony példa, mert a teljes implementáció néhány kis függvény használatával történik. Nincs szükség külső adattárra, például adatbázisra.

Feljegyzés

Az Azure Functions Node.js programozási modelljének 4- es verziója általánosan elérhető. Az új v4-modell úgy lett kialakítva, hogy rugalmasabb és intuitívabb felhasználói élményt nyújtson JavaScript- és TypeScript-fejlesztők számára. A migrálási útmutatóban további információt olvashat a v3 és a v4 közötti különbségekről.

A következő kódrészletekben a JavaScript (PM4) a V4 programozási modellt, az új felületet jelöli.

Előfeltételek

Forgatókönyv áttekintése

A telefonos ellenőrzéssel ellenőrizheti, hogy az alkalmazás végfelhasználói nem levélszemétküldők-e, és hogy kik azok, akiknek mondják magukat. A többtényezős hitelesítés gyakori használati eset a felhasználói fiókok hackerekkel szembeni védelméhez. A saját telefonos ellenőrzés megvalósításának kihívása, hogy állapotalapú interakciót igényel egy emberrel. A végfelhasználók általában valamilyen kódot (például 4 jegyű számot) biztosítanak, és ésszerű időn belül válaszolniuk kell.

A szokásos Azure Functions állapot nélküli (más platformokon sok más felhővégponthoz hasonlóan), ezért az ilyen típusú interakciókhoz az állapot külsőleg, egy adatbázisban vagy egy másik állandó tárolóban történő explicit kezelése szükséges. Emellett az interakciót több, egymással koordinálható függvényre kell bontani. Szükség van például legalább egy függvényre a kód kiválasztásához, valahol való megőrzéséhez és a felhasználó telefonjára való elküldéséhez. Emellett legalább egy másik függvényre is szüksége van ahhoz, hogy választ kapjon a felhasználótól, és valahogy visszaképezhesse azt az eredeti függvényhívásba a kódérvényesítés elvégzéséhez. Az időtúllépés szintén fontos szempont a biztonság biztosításához. Elég összetett lehet gyorsan.

A Durable Functions használatakor a forgatókönyv összetettsége jelentősen csökken. Ahogy ebben a mintában látni fogja, a vezénylő függvények egyszerűen és külső adattárak bevonása nélkül kezelhetik az állapotalapú interakciót. Mivel a vezénylő függvények tartósak, ezek az interaktív folyamatok szintén rendkívül megbízhatóak.

A Twilio-integráció konfigurálása

Ez a minta magában foglalja a Twilio szolgáltatás használatát SMS-üzenetek mobiltelefonra való küldéséhez. Az Azure Functions már támogatja a Twilio-t a Twilio-kötésen keresztül, és a minta ezt a funkciót használja.

Az első dolog, amire szüksége van, egy Twilio-fiók. Létrehozhat egyet ingyenesen a .. címen https://www.twilio.com/try-twilio. Miután rendelkezik fiókkal, adja hozzá az alábbi három alkalmazásbeállítást a függvényalkalmazáshoz.

Alkalmazásbeállítás neve Érték leírása
TwilioAccountSid A Twilio-fiók SID-azonosítója
TwilioAuthToken A Twilio-fiók hitelesítési jogkivonata
TwilioPhoneNumber A Twilio-fiókhoz társított telefonszám. Ez SMS-üzenetek küldésére szolgál.

A függvények

Ez a cikk a mintaalkalmazás alábbi funkcióit ismerteti:

Feljegyzés

A HttpStart mintaalkalmazás és a rövid útmutató függvénye vezénylési ügyfélként működik, amely elindítja a vezénylő függvényt.

E4_SmsPhoneVerification vezénylő függvény

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

Feljegyzés

Elsőre talán nem nyilvánvaló, de ez a vezénylő nem sérti a determinisztikus vezénylési kényszert. Ez determinisztikus, mert a tulajdonság az CurrentUtcDateTime időzítő lejárati idejének kiszámítására szolgál, és ugyanazt az értéket adja vissza minden visszajátszáskor a vezénylési kódban. Ez a viselkedés fontos annak biztosítása érdekében, hogy a hívás minden ismétlődő hívása Task.WhenAnyugyanazt winner az eredményt kapja.

A kezdés után ez a vezénylő függvény a következőket végzi el:

  1. Lekéri azt a telefonszámot, amelyre az SMS-értesítést küldi .
  2. Felhívja E4_SendSmsChallenge , hogy küldjön SMS-üzenetet a felhasználónak, és visszaadja a várt 4 jegyű kihíváskódot.
  3. Tartós időzítőt hoz létre, amely az aktuális időtől 90 másodpercet aktivál.
  4. Az időzítővel párhuzamosan egy SmsChallengeResponse eseményt vár a felhasználótól.

A felhasználó egy négyjegyű kóddal rendelkező SMS-üzenetet kap. 90 másodpercük van arra, hogy ugyanazt a négyjegyű kódot visszaküldjék a vezénylő függvénypéldánynak az ellenőrzési folyamat befejezéséhez. Ha nem a megfelelő kódot küldik el, további három kísérletet kapnak a helyes beolvasásra (ugyanabban a 90 másodperces ablakban).

Figyelmeztetés

Fontos, hogy megszakítsa az időzítőket , ha már nincs szüksége rájuk, mint a fenti példában, amikor egy kihívásra adott választ elfogadnak.

E4_SendSmsChallenge tevékenységfüggvény

A E4_SendSmsChallenge függvény a Twilio-kötéssel küldi el az SMS-üzenetet a négyjegyű kóddal a végfelhasználónak.

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

Feljegyzés

Először telepítenie kell a Microsoft.Azure.WebJobs.Extensions.Twilio Nuget-csomagot a Functionshez a mintakód futtatásához. Ne telepítse a fő Twilio nuget-csomagot sem, mert ez verziószámozási problémákat okozhat, amelyek buildelési hibákat eredményeznek.

Minta futtatása

A mintában szereplő HTTP-aktivált függvények használatával elindíthatja a vezénylést a következő HTTP POST-kérés elküldésével:

POST http://{host}/orchestrators/E4_SmsPhoneVerification
Content-Length: 14
Content-Type: application/json

"+1425XXXXXXX"
HTTP/1.1 202 Accepted
Content-Length: 695
Content-Type: application/json; charset=utf-8
Location: http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}

{"id":"741c65651d4c40cea29acdd5bb47baf1","statusQueryGetUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}","sendEventPostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/raiseEvent/{eventName}?taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}","terminatePostUri":"http://{host}/runtime/webhooks/durabletask/instances/741c65651d4c40cea29acdd5bb47baf1/terminate?reason={text}&taskHub=DurableFunctionsHub&connection=Storage&code={systemKey}"}

A vezénylő függvény megkapja a megadott telefonszámot, és azonnal sms-t küld neki egy véletlenszerűen generált 4 jegyű ellenőrző kóddal – például 2168- val. A függvény ezután 90 másodpercet vár a válaszig.

A kóddal való válaszadáshoz használhatja a (.NET) vagy raiseEvent a (JavaScript/TypeScript) függvényt egy másik függvényben, vagy meghívhatja a fenti 202-es válaszban hivatkozott sendEventPostUri HTTP POST webhookot az esemény nevére cserélve{eventName}: SmsChallengeResponseRaiseEventAsync

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

2168

Ha ezt az időzítő lejárta előtt küldi el, a vezénylés befejeződik, és a output mező értéke truea sikeres ellenőrzést jelzi.

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":"2017-06-29T19:10:49Z","lastUpdatedTime":"2017-06-29T19:12:23Z"}

Ha hagyja, hogy az időzítő lejárjon, vagy ha négyszer adja meg a hibás kódot, lekérdezheti az állapotot, és megtekintheti a false vezénylési függvény kimenetét, ami azt jelzi, hogy a telefonos ellenőrzés sikertelen volt.

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 145

{"runtimeStatus":"Completed","input":"+1425XXXXXXX","output":false,"createdTime":"2017-06-29T19:20:49Z","lastUpdatedTime":"2017-06-29T19:22:23Z"}

Következő lépések

Ez a minta bemutatta a Durable Functions speciális funkcióit, nevezetesen WaitForExternalEvent az CreateTimer API-kat. Láthatta, hogyan kombinálhatók ezek a (C#)/context.df.Task.any (JavaScript/TypeScript)/context.task_any (Python) használatával Task.WaitAny egy megbízható időtúllépési rendszer implementálásához, amely gyakran hasznos a valós emberekkel való interakcióhoz. A Durable Functions használatával kapcsolatos további információkért olvassa el azokat a cikkeket, amelyek részletesen ismertetik az adott témaköröket.