Partilhar via


Tutorial: Criar aplicativo de chat com a Função do Azure no Modo Sem Servidor (Visualização)

Este tutorial orienta você sobre como criar um Web PubSub para Socket.IO serviço no Modo sem Servidor e criar um aplicativo de chat integrado ao Azure Function.

Encontre exemplos de código completo que são usados neste tutorial:

Importante

O Modo Padrão precisa de um servidor persistente, não é possível integrar o Web PubSub para Socket.IO no modo padrão com a Função do Azure.

Importante

As cadeias de conexão brutas aparecem neste artigo apenas para fins de demonstração.

Uma cadeia de conexão inclui as informações de autorização necessárias para seu aplicativo acessar o serviço Azure Web PubSub. A chave de acesso dentro da cadeia de conexão é semelhante a uma senha de root para o seu serviço. Em ambientes de produção, proteja sempre as suas chaves de acesso. Use o Azure Key Vault para gerenciar e girar suas chaves com segurança e proteger sua conexão com WebPubSubServiceCliento .

Evite distribuir chaves de acesso para outros usuários, codificá-las ou salvá-las em qualquer lugar em texto simples acessível a outras pessoas. Rode as suas chaves se acreditar que podem ter sido comprometidas.

Pré-requisitos

Criar um recurso Web PubSub para Socket.IO no modo sem servidor

Para criar um Web PubSub para Socket.IO, você pode usar o seguinte comando da CLI do Azure:

az webpubsub create -g <resource-group> -n <resource-name>--kind socketio --service-mode serverless --sku Premium_P1

Criar um projeto do Azure Function localmente

Você deve seguir as etapas para iniciar um projeto local do Azure Function.

  1. Siga a etapa para instalar a ferramenta principal mais recente do Azure Function

  2. Na janela do terminal ou em um prompt de comando, execute o seguinte comando para criar um projeto na SocketIOProject pasta:

    func init SocketIOProject --worker-runtime javascript --model V4
    

    Este comando cria um projeto JavaScript. E digite a pasta SocketIOProject para executar os seguintes comandos.

  3. Atualmente, o Pacote de Funções não inclui Vinculação de Função Socket.IO, pelo que é necessário adicionar o pacote manualmente.

    1. Para eliminar a referência do pacote de funções, edite o arquivo host.json e remova as seguintes linhas.

      "extensionBundle": {
          "id": "Microsoft.Azure.Functions.ExtensionBundle",
          "version": "[4.*, 5.0.0)"
      }
      
    2. Executar o comando:

      func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
      
  4. Crie uma função para negociação. A função de negociação utilizada para gerar endpoints e tokens que permitem ao cliente aceder ao serviço.

    func new --template "Http Trigger" --name negotiate
    

    Abra o arquivo em src/functions/negotiate.js e substitua pelo seguinte código:

    const { app, input } = require('@azure/functions');
    
    const socketIONegotiate = input.generic({
        type: 'socketionegotiation',
        direction: 'in',
        name: 'result',
        hub: 'hub'
    });
    
    async function negotiate(request, context) {
        let result = context.extraInputs.get(socketIONegotiate);
        return { jsonBody: result };
    };
    
    // Negotiation
    app.http('negotiate', {
        methods: ['GET', 'POST'],
        authLevel: 'anonymous',
        extraInputs: [socketIONegotiate],
        handler: negotiate
    });
    

    Esta etapa cria uma função negotiate com Disparador HTTP e SocketIONegotiation associação de saída, o que significa que pode usar uma chamada HTTP para acionar a função e retornar um resultado de negociação gerado pela SocketIONegotiation associação.

  5. Crie uma função para entregar mensagens.

    func new --template "Http Trigger" --name message
    

    Abra o arquivo src/functions/message.js e substitua pelo seguinte código:

    const { app, output, trigger } = require('@azure/functions');
    
    const socketio = output.generic({
    type: 'socketio',
    hub: 'hub',
    })
    
    async function chat(request, context) {
        context.extraOutputs.set(socketio, {
        actionName: 'sendToNamespace',
        namespace: '/',
        eventName: 'new message',
        parameters: [
            context.triggerMetadata.socketId,
            context.triggerMetadata.message
        ],
        });
    }
    
    // Trigger for new message
    app.generic('chat', {
        trigger: trigger.generic({
            type: 'socketiotrigger',
            hub: 'hub',
            eventName: 'chat',
            parameterNames: ['message'],
        }),
        extraOutputs: [socketio],
        handler: chat
    });
    

    Isso utiliza SocketIOTrigger para ser acionado por uma mensagem de cliente Socket.IO e utiliza a associação SocketIO para transmitir mensagens para o espaço de nomes.

  6. Crie uma função para retornar um índice html para visitar.

    1. Crie uma pasta public em src/.

    2. Crie um arquivo index.html html com o seguinte conteúdo.

      <html>
      
      <body>
      <h1>Socket.IO Serverless Sample</h1>
      <div id="chatPage" class="chat-container">
          <div class="chat-input">
              <input type="text" id="chatInput" placeholder="Type your message here...">
              <button onclick="sendMessage()">Send</button>
          </div>
          <div id="chatMessages" class="chat-messages"></div>
      </div>
      <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
      <script>
          function appendMessage(message) {
          const chatMessages = document.getElementById('chatMessages');
          const messageElement = document.createElement('div');
          messageElement.innerText = message;
          chatMessages.appendChild(messageElement);
          hatMessages.scrollTop = chatMessages.scrollHeight;
          }
      
          function sendMessage() {
          const message = document.getElementById('chatInput').value;
          if (message) {
              document.getElementById('chatInput').value = '';
              socket.emit('chat', message);
          }
          }
      
          async function initializeSocket() {
          const negotiateResponse = await fetch(`/api/negotiate`);
          if (!negotiateResponse.ok) {
              console.log("Failed to negotiate, status code =", negotiateResponse.status);
              return;
          }
          const negotiateJson = await negotiateResponse.json();
          socket = io(negotiateJson.endpoint, {
              path: negotiateJson.path,
              query: { access_token: negotiateJson.token }
          });
      
          socket.on('new message', (socketId, message) => {
              appendMessage(`${socketId.substring(0,5)}: ${message}`);
          })
          }
      
          initializeSocket();
      </script>
      </body>
      
      </html>
      
    3. Para retornar a página HTML, crie uma função e copie códigos:

      func new --template "Http Trigger" --name index
      
    4. Abra o arquivo src/functions/index.js e substitua pelo seguinte código:

      const { app } = require('@azure/functions');
      
      const fs = require('fs').promises;
      const path = require('path')
      
      async function index(request, context) {
          try {
              context.log(`HTTP function processed request for url "${request.url}"`);
      
              const filePath = path.join(__dirname,'../public/index.html');
              const html = await fs.readFile(filePath);
              return {
                  body: html,
                  headers: {
                      'Content-Type': 'text/html'
                  }
              };
          } catch (error) {
              context.log(error);
              return {
                  status: 500,
                  jsonBody: error
              }
          }
      };
      
      app.http('index', {
          methods: ['GET', 'POST'],
          authLevel: 'anonymous',
          handler: index
      });
      
      

Como executar o aplicativo localmente

Depois que o código é preparado, siga as instruções para executar o exemplo.

Configurar o Armazenamento do Azure para o Azure Function

O Azure Functions requer uma conta de armazenamento para funcionar mesmo em execução local. Escolha uma das duas opções a seguir:

  • Execute o emulador gratuito do Azurite.
  • Use o serviço de Armazenamento do Azure. Este poderá implicar custos se continuar a utilizá-lo.
  1. Instalar o Azurite
npm install -g azurite
  1. Inicie o emulador de armazenamento Azurite:
azurite -l azurite -d azurite\debug.log
  1. Certifique-se de que o AzureWebJobsStorage in local.settings.json definido como UseDevelopmentStorage=true.

Configurar a configuração do Web PubSub para Socket.IO

  1. Adicione a cadeia de conexão à aplicação de função:
func settings add WebPubSubForSocketIOConnectionString "<connection string>"
  1. Adicionar configurações de hub ao Web PubSub para Socket.IO
az webpubsub hub create -n <resource name> -g <resource group> --hub-name hub --event-handler url-template="tunnel:///runtime/webhooks/socketio" user-event-pattern="*"

A cadeia de conexão pode ser obtida pelo comando CLI do Azure

az webpubsub key show -g <resource group> -n <resource name>

A saída contém primaryConnectionString e secondaryConnectionString, e qualquer um deles está disponível.

Configurar túnel

No modo sem servidor, o serviço usa webhooks para acionar a função. Quando tentas executar a aplicação localmente, um problema crucial é garantir que o serviço possa aceder ao teu endpoint de função local.

Uma maneira mais fácil de conseguir isso é usar a Tunnel Tool

  1. Instale a Ferramenta Tunnel:

    npm install -g @azure/web-pubsub-tunnel-tool
    
  2. Operar o túnel

    awps-tunnel run --hub hub --connection "<connection string>" --upstream http://127.0.0.1:7071
    

    A --upstream é a URL que a Função do Azure local expõe. A porta pode ser diferente e você pode verificar a saída ao iniciar a função na próxima etapa.

Executar aplicativo de exemplo

Depois que a ferramenta de túnel estiver em execução, você poderá executar o aplicativo de função localmente:

func start

E visite a página em http://localhost:7071/api/index.

Captura de ecrã da aplicação de chat sem servidor.

Próximos passos

Em seguida, você pode tentar usar o Bicep para implantar o aplicativo online com autenticação baseada em identidade: