分享方式:


教學課程:使用 Azure Functions 和 Azure Web PubSub 服務建立無伺服器即時聊天應用程式

Azure Web PubSub 服務可協助您輕鬆地使用 WebSocket 和發佈-訂閱模式來建置即時傳訊 Web 應用程式。 Azure Functions 是無伺服器平臺,可讓您執行程序代碼,而不需要管理任何基礎結構。 在本教學課程中,您將瞭解如何使用 Azure Web PubSub 服務和 Azure Functions 來建置具有即時傳訊和發佈訂閱模式的無伺服器應用程式。

在本教學課程中,您會了解如何:

  • 建置無伺服器即時聊天應用程式
  • 使用 Web PubSub 函式觸發程式系結和輸出系結
  • 將函式部署至 Azure 函式應用程式
  • 設定 Azure 驗證
  • 設定 Web PubSub 事件處理程式,將事件和訊息路由傳送至應用程式

必要條件

如果您沒有 Azure 訂閱,請在開始之前,先建立 Azure 免費帳戶

登入 Azure

請使用您的 Azure 帳戶登入 Azure 入口網站 (https://portal.azure.com/)。

建立 Azure Web PubSub 服務執行個體

您的應用程式將會連線至 Azure 中的 Web PubSub 服務執行個體。

  1. 選取 Azure 入口網站左上角的 [新增] 按鈕。 在 [新增] 畫面的搜尋方塊中輸入 Web PubSub 並按 Enter。 (您也可以從 Web 類別搜尋 Azure Web PubSub。)

    在入口網站中搜尋 Azure Web PubSub 的螢幕快照。

  2. 從搜尋結果中選取 [Web PubSub],然後選取 [建立]

  3. 輸入下列設定。

    設定 建議的值 描述
    資源名稱 全域唯一的名稱 識別新 Web PubSub 服務執行個體的全域唯一名稱。 有效字元:a-zA-Z0-9-
    訂用帳戶 您的訂用帳戶 將在其下建立這個新 Web PubSub 服務執行個體的 Azure 訂用帳戶。
    資源群組 myResourceGroup 要在其中建立 Web PubSub 服務執行個體之新資源群組的名稱。
    地點 美國西部 選擇您附近的區域
    定價層 免費 您可以先免費試用 Azure Web PubSub 服務。 深入了解 Azure Web PubSub 服務定價層
    單位計數 - 單位計數會指出您的 Web PubSub 服務執行個體可接受的連線數目。 每個單位支援最多 1,000 個同時連線。 它只能在標準層中設定。

    在入口網站中建立 Azure Web PubSub 實例的螢幕快照。

  4. 選取 [建立] 以開始部署 Web PubSub 服務執行個體。

建立函式

  1. 請確定您已安裝 Azure Functions Core Tools 。 然後為專案建立空的目錄。 在此工作目錄下執行命令。

    func init --worker-runtime javascript --model V4
    
  2. 安裝 Microsoft.Azure.WebJobs.Extensions.WebPubSub

    確認並更新 host.json's extensionBundle 至 4.* 版或更新版本,以取得 Web PubSub 支援。

    {
      "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[4.*, 5.0.0)"
      }
    }
    
  3. 建立函 index 式來讀取和裝載用戶端的靜態網頁。

    func new -n index -t HttpTrigger
    
    • 更新 src/functions/index.js 並複製下列程序代碼。
      const { app } = require('@azure/functions');
      const { readFile } = require('fs/promises');
      
      app.http('index', {
          methods: ['GET', 'POST'],
          authLevel: 'anonymous',
          handler: async (context) => {
              const content = await readFile('index.html', 'utf8', (err, data) => {
                  if (err) {
                      context.err(err)
                      return
                  }
              });
      
              return { 
                  status: 200,
                  headers: { 
                      'Content-Type': 'text/html'
                  }, 
                  body: content, 
              };
          }
      });
      
  4. 建立函 negotiate 式,以協助用戶端使用存取令牌取得服務連線 URL。

    func new -n negotiate -t HttpTrigger
    

    注意

    在此範例中,我們使用 Microsoft Entra ID 使用者身分識別標頭 x-ms-client-principal-name 來擷取 userId。 這不適用於本機函式。 您可以將它設為空白或變更為在本機播放時取得或產生 userId 的其他方式。 例如,讓用戶端輸入用戶名稱,並在查詢中傳遞它,例如 ?user={$username} 呼叫 negotiate 函式以取得服務連線 URL。 在函式中negotiate,使用 值 {query.user}進行設定userId

    • 更新 src/functions/negotiate 並複製下列程序代碼。
      const { app, input } = require('@azure/functions');
      
      const connection = input.generic({
          type: 'webPubSubConnection',
          name: 'connection',
          userId: '{headers.x-ms-client-principal-name}',
          hub: 'simplechat'
      });
      
      app.http('negotiate', {
          methods: ['GET', 'POST'],
          authLevel: 'anonymous',
          extraInputs: [connection],
          handler: async (request, context) => {
              return { body: JSON.stringify(context.extraInputs.get('connection')) };
          },
      });
      
  5. 建立函 message 式以透過服務廣播用戶端訊息。

    func new -n message -t HttpTrigger
    
    • 更新 src/functions/message.js 並複製下列程序代碼。
      const { app, output, trigger } = require('@azure/functions');
      
      const wpsMsg = output.generic({
          type: 'webPubSub',
          name: 'actions',
          hub: 'simplechat',
      });
      
      const wpsTrigger = trigger.generic({
          type: 'webPubSubTrigger',
          name: 'request',
          hub: 'simplechat',
          eventName: 'message',
          eventType: 'user'
      });
      
      app.generic('message', {
          trigger: wpsTrigger,
          extraOutputs: [wpsMsg],
          handler: async (request, context) => {
              context.extraOutputs.set(wpsMsg, [{
                  "actionName": "sendToAll",
                  "data": `[${context.triggerMetadata.connectionContext.userId}] ${request.data}`,
                  "dataType": request.dataType
              }]);
      
              return {
                  data: "[SYSTEM] ack.",
                  dataType: "text",
              };
          }
      });
      
  6. 在專案根資料夾中新增用戶端單一頁面 index.html ,並複製內容。

    <html>
      <body>
        <h1>Azure Web PubSub Serverless Chat App</h1>
        <div id="login"></div>
        <p></p>
        <input id="message" placeholder="Type to chat..." />
        <div id="messages"></div>
        <script>
          (async function () {
            let authenticated = window.location.href.includes(
              "?authenticated=true"
            );
            if (!authenticated) {
              // auth
              let login = document.querySelector("#login");
              let link = document.createElement("a");
              link.href = `${window.location.origin}/.auth/login/aad?post_login_redirect_url=/api/index?authenticated=true`;
              link.text = "login";
              login.appendChild(link);
            } else {
              // negotiate
              let messages = document.querySelector("#messages");
              let res = await fetch(`${window.location.origin}/api/negotiate`, {
                credentials: "include",
              });
              let url = await res.json();
              // connect
              let ws = new WebSocket(url.url);
              ws.onopen = () => console.log("connected");
              ws.onmessage = (event) => {
                let m = document.createElement("p");
                m.innerText = event.data;
                messages.appendChild(m);
              };
              let message = document.querySelector("#message");
              message.addEventListener("keypress", (e) => {
                if (e.charCode !== 13) return;
                ws.send(message.value);
                message.value = "";
              });
            }
          })();
        </script>
      </body>
    </html>
    

建立及部署 Azure 函式應用程式

若要將函式程式碼部署至 Azure,您必須先建立三個資源:

  • 資源群組,這是相關資源的邏輯容器。
  • 記憶體帳戶,用來維護函式的狀態和其他資訊。
  • 函數應用程式,其提供執行函式程式碼的環境。 函式應用程式會對應至本機函式專案,並可讓您將函式分組為邏輯單元,以便更輕鬆地管理、部署和共用資源。

請使用下列命令來建立這些項目。

  1. 如果您尚未登入 Azure,請於此時登入:

    az login
    
  2. 建立資源群組,或者您可以重複使用其中一個 Azure Web PubSub 服務來略過:

    az group create -n WebPubSubFunction -l <REGION>
    
  3. 在您的資源群組和區域中建立一般用途的儲存體帳戶:

    az storage account create -n <STORAGE_NAME> -l <REGION> -g WebPubSubFunction
    
  4. 在 Azure 中建立函式應用程式:

    az functionapp create --resource-group WebPubSubFunction --consumption-plan-location <REGION> --runtime node --runtime-version 18 --functions-version 4 --name <FUNCIONAPP_NAME> --storage-account <STORAGE_NAME>
    

    注意

    檢查 Azure Functions 執行時間版本檔 ,將參數設定 --runtime-version 為支援的值。

  5. 將函式專案部署至 Azure:

    在 Azure 中成功建立函式應用程式之後,您現在已準備好使用 func azure functionapp publish 命令來部署本機函式專案。

    func azure functionapp publish <FUNCIONAPP_NAME>
    
  6. WebPubSubConnectionString設定函式應用程式的 :

    首先,從 Azure 入口網站尋找您的 Web PubSub 資源,並在 [金鑰] 底下複製 連接字串。 然後,流覽至 Azure 入口網站 ->設定 ->Configuration 中的函式應用程式設定。 然後在 [應用程式設定] 底下新增新專案,名稱等於 WebPubSubConnectionString 且值為您的 Web PubSub 資源 連接字串。

設定 Web PubSub 服務 Event Handler

在此範例中,我們使用 WebPubSubTrigger 來接聽服務上游要求。 因此,Web PubSub 必須知道函式的端點資訊,才能傳送目標用戶端要求。 Azure 函式應用程式需要系統密鑰,以取得擴充功能特定 Webhook 方法的安全性。 在上一個步驟中,我們在使用函式部署函式應用程式 message 之後,就能取得系統密鑰。

移至 Azure 入口網站 - 尋找您的函式應用程式資源 -應用程式金鑰 ->系統金鑰 -。webpubsub_extension>>> 複製值複製為 <APP_KEY>

取得函式系統金鑰的螢幕快照。

在 Azure Web PubSub 服務中設定 Event Handler 。 移至 Azure 入口網站 -> 尋找您的 Web PubSub 資源 ->設定。 將新的中樞設定對應新增至使用中的一個函式。 <FUNCTIONAPP_NAME>將與 <APP_KEY> 取代您的 。

  • 中樞名稱: simplechat
  • URL 範本: https://< FUNCTIONAPP_NAME.azurewebsites.net/runtime/webhooks/webpubsub?code>=<APP_KEY>
  • 使用者事件模式: *
  • 系統事件:-(不需要在此範例中設定)

設定事件處理程式的螢幕快照。

設定以啟用客戶端驗證

移至 Azure 入口網站 -> 尋找您的函式應用程式資源 ->Authentication。 按一下 Add identity provider。 將 App Service 驗證設定 設為 [允許未經驗證的存取],以便匿名使用者瀏覽您的用戶端索引頁面,再重新導向至驗證。 然後選取存檔

在這裡,我們選擇Microsoft做為識別提供者,其會在 x-ms-client-principal-name 函式中使用 negotiateuserId 此外,您可以在鏈接之後設定其他識別提供者,並別忘了據此更新 userId 函式中的 negotiate 值。

試用應用程式

現在,您可以從函式應用程式測試頁面: https://<FUNCTIONAPP_NAME>.azurewebsites.net/api/index。 請參閱快照集。

  1. 按兩下 login 即可自行驗證。
  2. 在要聊天的輸入方塊中輸入訊息。

在訊息函式中,我們會將呼叫端的訊息廣播給所有用戶端,並使用訊息 [SYSTEM] ack傳回呼叫端。 因此,我們可以在範例聊天快照中知道,前四則訊息來自目前的客戶端,最後兩則訊息來自另一個用戶端。

聊天範例的螢幕快照。

清除資源

如果您不打算繼續使用此應用程式,請使用下列步驟刪除此檔所建立的所有資源,因此不會產生任何費用:

  1. 在 Azure 入口網站中選取最左側的 [資源群組],然後選取您所建立的資源群組。 您可以使用搜尋方塊,改為依其名稱尋找資源群組。

  2. 在開啟的視窗中,選取資源群組,然後選取 [ 刪除資源群組]。

  3. 在新視窗中,輸入要刪除的資源群組名稱,然後選取 [ 刪除]。

下一步

在本快速入門中,您已瞭解如何執行無伺服器聊天應用程式。 現在,您可以開始建置自己的應用程式。