Tutorial: Introdução ao ASP.NET Core SignalR usando TypeScript e Webpack

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Por Sébastien Sougnez

Este tutorial demonstra como usar o Webpack em um aplicativo Web SignalR do ASP.NET Core para empacotar e compilar um cliente escrito em TypeScript. O Webpack habilita os desenvolvedores a agrupar e criar recursos de um aplicativo Web do lado do cliente.

Neste tutorial, você aprenderá a:

  • Criar um aplicativo SignalR ASP.NET Core
  • Configurar o servidor SignalR
  • Configurar um pipeline de build usando o Webpack
  • Configurar o cliente TypeScript do SignalR
  • Habilitar a comunicação entre o cliente e o servidor

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Criar o aplicativo Web do ASP.NET Core

Por padrão, o Visual Studio usa a versão do npm encontrada no diretório de instalação. Para configurar o Visual Studio para pesquisar o npm na variável de ambiente PATH:

Inicie o Visual Studio. Na janela Iniciar, selecione Continuar sem código.

  1. Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.

  2. Selecione a entrada $(PATH) na lista. Selecione aa seta para cima para mover a entrada para a segunda posição da lista e selecione OK:

    Configuração do Visual Studio.

Para criar um aplicativo Web ASP.NET Core:

  1. Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo ASP.NET Core Vazio. Selecione Avançar.
  2. Dê ao projeto o nome de SignalRWebpack e selecione Criar.
  3. Selecione .NET 8.0 (suporte a longo prazo) na lista suspensa do Framework. Selecione Criar.

Adicione o pacote NuGet Microsoft.TypeScript.MSBuild ao projeto:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó do projeto e selecione Gerenciar Pacotes NuGet. Na guia Procurar, pesquise Microsoft.TypeScript.MSBuild e selecione Instalar à direita para instalar o pacote.

O Visual Studio adiciona o pacote NuGet no nó Dependências no Gerenciador de Soluções, habilitando a compilação do TypeScript no projeto.

Configurar o servidor

Nesta seção, você configurará o aplicativo Web ASP.NET Core para enviar e receber mensagens SignalR.

  1. Em Program.cs, chame AddSignalR:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddSignalR();
    
  2. Novamente, em Program.cs, chame UseDefaultFiles e UseStaticFiles:

    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    O código anterior permite que o servidor localize e forneça o arquivo index.html. O arquivo é fornecido se o usuário insere sua URL completa ou a URL raiz do aplicativo Web.

  3. Crie um novo diretório chamado de Hubs na raiz do projeto, SignalRWebpack/, para a classe de hub SignalR.

  4. Crie um arquivo Hubs/ChatHub.cs com o código a seguir:

    using Microsoft.AspNetCore.SignalR;
    
    namespace SignalRWebpack.Hubs;
    
    public class ChatHub : Hub
    {
        public async Task NewMessage(long username, string message) =>
            await Clients.All.SendAsync("messageReceived", username, message);
    }
    

    O código precedente transmite as mensagens recebidas para todos os usuários conectados quando o servidor as recebe. Não é necessário ter um método genérico on para receber todas as mensagens. Um método nomeado com o nome da mensagem é suficiente.

    Neste exemplo:

    • O cliente TypeScript envia uma mensagem identificada como newMessage.
    • O método NewMessage de C# espera os dados enviados pelo cliente.
    • Uma chamada é feita para SendAsync em Clients.All.
    • As mensagens recebidas são enviadas a todos os clientes conectados ao hub.
  5. Adicione a instrução using a seguir ao início do Program.cs para resolver a referência a ChatHub:

    using SignalRWebpack.Hubs;
    
  6. No Program.cs, mapeie a rota /hub para o hub ChatHub. Substitua o código que exibe Hello World! pelo código a seguir:

    app.MapHub<ChatHub>("/hub");
    

Configurar o cliente

Nesta seção, você criará um projeto Node.js para converter o TypeScript em JavaScript e agrupar recursos do lado do cliente, incluindo HTML e CSS, usando o Webpack.

  1. Execute o seguinte comando na raiz do projeto para criar um arquivo package.json:

    npm init -y
    
  2. Adicione a propriedade realçada ao arquivo package.json e salve as alterações de arquivo:

    {
      "name": "SignalRWebpack",
      "version": "1.0.0",
      "private": true,
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    

    Definir a propriedade private como true evita avisos de instalação de pacote na próxima etapa.

  3. Instalar os pacotes de npm exigidos. Execute o seguinte comando na raiz do projeto:

    npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
    

    A opção -E desabilita o comportamento padrão do npm de escrever os operadores de intervalo de versão semântica para package.json. Por exemplo, "webpack": "5.76.1" é usado em vez de "webpack": "^5.76.1". Essa opção evita atualizações não intencionais para versões de pacote mais recentes.

    Para obter mais informações, confira a documentação do npm-install.

  4. Substitua a propriedade scripts do arquivo package.json pelo código a seguir:

    "scripts": {
      "build": "webpack --mode=development --watch",
      "release": "webpack --mode=production",
      "publish": "npm run release && dotnet publish -c Release"
    },
    

    Os seguintes scripts são definidos:

    • build: agrupa os recursos do lado do cliente no modo de desenvolvimento e busca alterações no arquivo. O observador de arquivos faz com que o lote se regenere toda vez que um arquivo de projeto é alterado. A opção mode desabilita otimizações de produção, como tree shaking e minificação. use build somente no desenvolvimento.
    • release: agrupa recursos do lado do cliente no modo de produção.
    • publish: executa o script release para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET para publicar o aplicativo.
  5. Crie um arquivo chamado webpack.config.js na raiz do projeto, com o seguinte código:

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
    module.exports = {
        entry: "./src/index.ts",
        output: {
            path: path.resolve(__dirname, "wwwroot"),
            filename: "[name].[chunkhash].js",
            publicPath: "/",
        },
        resolve: {
            extensions: [".js", ".ts"],
        },
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    use: "ts-loader",
                },
                {
                    test: /\.css$/,
                    use: [MiniCssExtractPlugin.loader, "css-loader"],
                },
            ],
        },
        plugins: [
            new CleanWebpackPlugin(),
            new HtmlWebpackPlugin({
                template: "./src/index.html",
            }),
            new MiniCssExtractPlugin({
                filename: "css/[name].[chunkhash].css",
            }),
        ],
    };
    

    O arquivo anterior configura o processo de compilação Webpack:

    • A propriedade output substitui o valor padrão do dist. Em vez disso, o lote é emitido no diretório wwwroot.
    • A matriz resolve.extensions inclui .js para importar o JavaScript do cliente SignalR.
  6. Crie um novo diretório chamado de src na raiz do projeto, SignalRWebpack/, para a código do cliente.

  7. Copie o diretório src e seu conteúdo do projeto de exemplo na raiz do projeto. O diretório src contém os seguintes arquivos:

    • index.html, que define a marcação de texto clichê da home page:

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="utf-8" />
          <title>ASP.NET Core SignalR with TypeScript and Webpack</title>
        </head>
        <body>
          <div id="divMessages" class="messages"></div>
          <div class="input-zone">
            <label id="lblMessage" for="tbMessage">Message:</label>
            <input id="tbMessage" class="input-zone-input" type="text" />
            <button id="btnSend">Send</button>
          </div>
        </body>
      </html>
      
    • css/main.css, que fornece estilos CSS para a home page:

      *,
      *::before,
      *::after {
        box-sizing: border-box;
      }
      
      html,
      body {
        margin: 0;
        padding: 0;
      }
      
      .input-zone {
        align-items: center;
        display: flex;
        flex-direction: row;
        margin: 10px;
      }
      
      .input-zone-input {
        flex: 1;
        margin-right: 10px;
      }
      
      .message-author {
        font-weight: bold;
      }
      
      .messages {
        border: 1px solid #000;
        margin: 10px;
        max-height: 300px;
        min-height: 300px;
        overflow-y: auto;
        padding: 5px;
      }
      
    • tsconfig.json, que configura o compilador TypeScript para produzir um JavaScript compatível com ECMAScript 5:

      {
        "compilerOptions": {
          "target": "es5"
        }
      }
      
    • index.ts:

      import * as signalR from "@microsoft/signalr";
      import "./css/main.css";
      
      const divMessages: HTMLDivElement = document.querySelector("#divMessages");
      const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
      const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
      const username = new Date().getTime();
      
      const connection = new signalR.HubConnectionBuilder()
          .withUrl("/hub")
          .build();
      
      connection.on("messageReceived", (username: string, message: string) => {
        const m = document.createElement("div");
      
        m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`;
      
        divMessages.appendChild(m);
        divMessages.scrollTop = divMessages.scrollHeight;
      });
      
      connection.start().catch((err) => document.write(err));
      
      tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.key === "Enter") {
          send();
        }
      });
      
      btnSend.addEventListener("click", send);
      
      function send() {
        connection.send("newMessage", username, tbMessage.value)
          .then(() => (tbMessage.value = ""));
      }
      

      O código anterior recupera referências a elementos DOM e anexa dois manipuladores de eventos:

      • keyup: é acionado quando o usuário digita tbMessage na caixa de texto e chama a função send quando o usuário pressiona a tecla Enter.
      • click: é acionado quando o usuário seleciona o botão Enviar e a função de chamadas send é chamada.

      A classe HubConnectionBuilder cria um novo construtor para configurar a conexão do servidor. A função withUrl configura a URL do hub.

      O SignalR habilita a troca de mensagens entre um cliente e um servidor. Cada mensagem tem um nome específico. Por exemplo, mensagens com o nome messageReceived podem executar a lógica responsável por exibir a nova mensagem na zona de mensagens. É possível escutar uma mensagem específica por meio da função on. Qualquer número de nomes de mensagem pode ser ouvido. Também é possível passar parâmetros para a mensagem, como o nome do autor e o conteúdo da mensagem recebida. Quando o cliente recebe a mensagem, um novo elemento div é criado com o nome do autor e o conteúdo da mensagem em seu atributo innerHTML. Ele é adicionado ao elemento principal div que exibe as mensagens.

      Enviar uma mensagem por meio da conexão WebSockets exige uma chamada para o método send. O primeiro parâmetro do método é o nome da mensagem. Os dados da mensagem residem nos outros parâmetros. Neste exemplo, uma mensagem identificada como newMessage é enviada ao servidor. A mensagem é composta do nome de usuário e da entrada em uma caixa de texto. Se o envio for bem-sucedido, o valor da caixa de texto será limpo.

  8. Execute o comando a seguir na raiz do projeto:

    npm i @microsoft/signalr @types/node
    

    O comando anterior instala:

    • O cliente TypeScript do SignalR, que permite ao cliente enviar mensagens para o servidor.
    • As definições de tipo TypeScript para Node.js, que permite a verificação em tempo de compilação de tipos de Node.js.

Testar o aplicativo

Confirme que o aplicativo funciona com as seguintes etapas:

  1. Execute o Webpack no modo release. Usando a janela Console do Gerenciador de Pacotes, execute o comando a seguir na raiz do projeto.

    npm run release
    

    Este comando gera o fornecimento dos ativos do lado do cliente ao executar o aplicativo. Os ativos são colocados na pasta wwwroot.

    O Webpack concluiu as seguintes tarefas:

    • Limpou o conteúdo do diretório wwwroot.
    • Converteu o TypeScript para JavaScript, um processo conhecido como transpilação.
    • Reduziu o tamanho do arquivo JavaScript gerado, um processo conhecido como minificação.
    • Copiou os arquivos JavaScript, CSS e HTML processados do src para o diretório wwwroot.
    • Injetou os seguintes elementos no arquivo wwwroot/index.html:
      • Uma marca <link>, fazendo referência ao arquivo wwwroot/main.<hash>.css. Essa marca é colocada imediatamente antes do fim da marca </head>.
      • Uma marca <script>, fazendo referência ao arquivo wwwroot/main.<hash>.js minimizado. Essa marca é colocada imediatamente após a marca </title> de fechamento.
  2. Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo wwwroot/index.html é fornecido em https://localhost:<port>.

    Se houver erros de compilação, tente fechar e reabrir a solução.

  3. Abra outra instância de navegador (qualquer navegador) e cole a URL na barra de endereços.

  4. Escolha qualquer navegador, digite algo na caixa de texto Mensagem e selecione o botão Enviar. O nome de usuário exclusivo e a mensagem são exibidas em ambas as páginas instantaneamente.

Mensagem exibida em ambas as janelas do navegador

Próximas etapas

Recursos adicionais

Este tutorial demonstra como usar o Webpack em um aplicativo Web SignalR do ASP.NET Core para empacotar e compilar um cliente escrito em TypeScript. O Webpack habilita os desenvolvedores a agrupar e criar recursos de um aplicativo Web do lado do cliente.

Neste tutorial, você aprenderá a:

  • Criar um aplicativo SignalR ASP.NET Core
  • Configurar o servidor SignalR
  • Configurar um pipeline de build usando o Webpack
  • Configurar o cliente TypeScript do SignalR
  • Habilitar a comunicação entre o cliente e o servidor

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Criar o aplicativo Web do ASP.NET Core

Por padrão, o Visual Studio usa a versão do npm encontrada no diretório de instalação. Para configurar o Visual Studio para pesquisar o npm na variável de ambiente PATH:

Inicie o Visual Studio. Na janela Iniciar, selecione Continuar sem código.

  1. Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.

  2. Selecione a entrada $(PATH) na lista. Selecione aa seta para cima para mover a entrada para a segunda posição da lista e selecione OK:

    Configuração do Visual Studio.

Para criar um aplicativo Web ASP.NET Core:

  1. Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo ASP.NET Core Vazio. Selecione Avançar.
  2. Dê ao projeto o nome de SignalRWebpack e selecione Criar.
  3. Selecione .NET 7.0 (Standard Term Support) na lista suspensa do Framework. Selecione Criar.

Adicione o pacote NuGet Microsoft.TypeScript.MSBuild ao projeto:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó do projeto e selecione Gerenciar Pacotes NuGet. Na guia Procurar, pesquise Microsoft.TypeScript.MSBuild e selecione Instalar à direita para instalar o pacote.

O Visual Studio adiciona o pacote NuGet no nó Dependências no Gerenciador de Soluções, habilitando a compilação do TypeScript no projeto.

Configurar o servidor

Nesta seção, você configurará o aplicativo Web ASP.NET Core para enviar e receber mensagens SignalR.

  1. Em Program.cs, chame AddSignalR:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddSignalR();
    
  2. Novamente, em Program.cs, chame UseDefaultFiles e UseStaticFiles:

    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    O código anterior permite que o servidor localize e forneça o arquivo index.html. O arquivo é fornecido se o usuário insere sua URL completa ou a URL raiz do aplicativo Web.

  3. Crie um novo diretório chamado de Hubs na raiz do projeto, SignalRWebpack/, para a classe de hub SignalR.

  4. Crie um arquivo Hubs/ChatHub.cs com o código a seguir:

    using Microsoft.AspNetCore.SignalR;
    
    namespace SignalRWebpack.Hubs;
    
    public class ChatHub : Hub
    {
        public async Task NewMessage(long username, string message) =>
            await Clients.All.SendAsync("messageReceived", username, message);
    }
    

    O código precedente transmite as mensagens recebidas para todos os usuários conectados quando o servidor as recebe. Não é necessário ter um método genérico on para receber todas as mensagens. Um método nomeado com o nome da mensagem é suficiente.

    Neste exemplo:

    • O cliente TypeScript envia uma mensagem identificada como newMessage.
    • O método NewMessage de C# espera os dados enviados pelo cliente.
    • Uma chamada é feita para SendAsync em Clients.All.
    • As mensagens recebidas são enviadas a todos os clientes conectados ao hub.
  5. Adicione a instrução using a seguir ao início do Program.cs para resolver a referência a ChatHub:

    using SignalRWebpack.Hubs;
    
  6. No Program.cs, mapeie a rota /hub para o hub ChatHub. Substitua o código que exibe Hello World! pelo código a seguir:

    app.MapHub<ChatHub>("/hub");
    

Configurar o cliente

Nesta seção, você criará um projeto Node.js para converter o TypeScript em JavaScript e agrupar recursos do lado do cliente, incluindo HTML e CSS, usando o Webpack.

  1. Execute o seguinte comando na raiz do projeto para criar um arquivo package.json:

    npm init -y
    
  2. Adicione a propriedade realçada ao arquivo package.json e salve as alterações de arquivo:

    {
      "name": "SignalRWebpack",
      "version": "1.0.0",
      "private": true,
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    

    Definir a propriedade private como true evita avisos de instalação de pacote na próxima etapa.

  3. Instalar os pacotes de npm exigidos. Execute o seguinte comando na raiz do projeto:

    npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
    

    A opção -E desabilita o comportamento padrão do npm de escrever os operadores de intervalo de versão semântica para package.json. Por exemplo, "webpack": "5.76.1" é usado em vez de "webpack": "^5.76.1". Essa opção evita atualizações não intencionais para versões de pacote mais recentes.

    Para obter mais informações, confira a documentação do npm-install.

  4. Substitua a propriedade scripts do arquivo package.json pelo código a seguir:

    "scripts": {
      "build": "webpack --mode=development --watch",
      "release": "webpack --mode=production",
      "publish": "npm run release && dotnet publish -c Release"
    },
    

    Os seguintes scripts são definidos:

    • build: agrupa os recursos do lado do cliente no modo de desenvolvimento e busca alterações no arquivo. O observador de arquivos faz com que o lote se regenere toda vez que um arquivo de projeto é alterado. A opção mode desabilita otimizações de produção, como tree shaking e minificação. use build somente no desenvolvimento.
    • release: agrupa recursos do lado do cliente no modo de produção.
    • publish: executa o script release para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET para publicar o aplicativo.
  5. Crie um arquivo chamado webpack.config.js na raiz do projeto, com o seguinte código:

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
    module.exports = {
        entry: "./src/index.ts",
        output: {
            path: path.resolve(__dirname, "wwwroot"),
            filename: "[name].[chunkhash].js",
            publicPath: "/",
        },
        resolve: {
            extensions: [".js", ".ts"],
        },
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    use: "ts-loader",
                },
                {
                    test: /\.css$/,
                    use: [MiniCssExtractPlugin.loader, "css-loader"],
                },
            ],
        },
        plugins: [
            new CleanWebpackPlugin(),
            new HtmlWebpackPlugin({
                template: "./src/index.html",
            }),
            new MiniCssExtractPlugin({
                filename: "css/[name].[chunkhash].css",
            }),
        ],
    };
    

    O arquivo anterior configura o processo de compilação Webpack:

    • A propriedade output substitui o valor padrão do dist. Em vez disso, o lote é emitido no diretório wwwroot.
    • A matriz resolve.extensions inclui .js para importar o JavaScript do cliente SignalR.
  6. Copie o diretório src e seu conteúdo do projeto de exemplo na raiz do projeto. O diretório src contém os seguintes arquivos:

    • index.html, que define a marcação de texto clichê da home page:

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="utf-8" />
          <title>ASP.NET Core SignalR with TypeScript and Webpack</title>
        </head>
        <body>
          <div id="divMessages" class="messages"></div>
          <div class="input-zone">
            <label id="lblMessage" for="tbMessage">Message:</label>
            <input id="tbMessage" class="input-zone-input" type="text" />
            <button id="btnSend">Send</button>
          </div>
        </body>
      </html>
      
    • css/main.css, que fornece estilos CSS para a home page:

      *,
      *::before,
      *::after {
        box-sizing: border-box;
      }
      
      html,
      body {
        margin: 0;
        padding: 0;
      }
      
      .input-zone {
        align-items: center;
        display: flex;
        flex-direction: row;
        margin: 10px;
      }
      
      .input-zone-input {
        flex: 1;
        margin-right: 10px;
      }
      
      .message-author {
        font-weight: bold;
      }
      
      .messages {
        border: 1px solid #000;
        margin: 10px;
        max-height: 300px;
        min-height: 300px;
        overflow-y: auto;
        padding: 5px;
      }
      
    • tsconfig.json, que configura o compilador TypeScript para produzir um JavaScript compatível com ECMAScript 5:

      {
        "compilerOptions": {
          "target": "es5"
        }
      }
      
    • index.ts:

      import * as signalR from "@microsoft/signalr";
      import "./css/main.css";
      
      const divMessages: HTMLDivElement = document.querySelector("#divMessages");
      const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
      const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
      const username = new Date().getTime();
      
      const connection = new signalR.HubConnectionBuilder()
          .withUrl("/hub")
          .build();
      
      connection.on("messageReceived", (username: string, message: string) => {
        const m = document.createElement("div");
      
        m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`;
      
        divMessages.appendChild(m);
        divMessages.scrollTop = divMessages.scrollHeight;
      });
      
      connection.start().catch((err) => document.write(err));
      
      tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.key === "Enter") {
          send();
        }
      });
      
      btnSend.addEventListener("click", send);
      
      function send() {
        connection.send("newMessage", username, tbMessage.value)
          .then(() => (tbMessage.value = ""));
      }
      

      O código anterior recupera referências a elementos DOM e anexa dois manipuladores de eventos:

      • keyup: é acionado quando o usuário digita tbMessage na caixa de texto e chama a função send quando o usuário pressiona a tecla Enter.
      • click: é acionado quando o usuário seleciona o botão Enviar e a função de chamadas send é chamada.

      A classe HubConnectionBuilder cria um novo construtor para configurar a conexão do servidor. A função withUrl configura a URL do hub.

      O SignalR habilita a troca de mensagens entre um cliente e um servidor. Cada mensagem tem um nome específico. Por exemplo, mensagens com o nome messageReceived podem executar a lógica responsável por exibir a nova mensagem na zona de mensagens. É possível escutar uma mensagem específica por meio da função on. Qualquer número de nomes de mensagem pode ser ouvido. Também é possível passar parâmetros para a mensagem, como o nome do autor e o conteúdo da mensagem recebida. Quando o cliente recebe a mensagem, um novo elemento div é criado com o nome do autor e o conteúdo da mensagem em seu atributo innerHTML. Ele é adicionado ao elemento principal div que exibe as mensagens.

      Enviar uma mensagem por meio da conexão WebSockets exige uma chamada para o método send. O primeiro parâmetro do método é o nome da mensagem. Os dados da mensagem residem nos outros parâmetros. Neste exemplo, uma mensagem identificada como newMessage é enviada ao servidor. A mensagem é composta do nome de usuário e da entrada em uma caixa de texto. Se o envio for bem-sucedido, o valor da caixa de texto será limpo.

  7. Execute o comando a seguir na raiz do projeto:

    npm i @microsoft/signalr @types/node
    

    O comando anterior instala:

    • O cliente TypeScript do SignalR, que permite ao cliente enviar mensagens para o servidor.
    • As definições de tipo TypeScript para Node.js, que permite a verificação em tempo de compilação de tipos de Node.js.

Testar o aplicativo

Confirme que o aplicativo funciona com as seguintes etapas:

  1. Execute o Webpack no modo release. Usando a janela Console do Gerenciador de Pacotes, execute o comando a seguir na raiz do projeto.

    npm run release
    

    Este comando gera o fornecimento dos ativos do lado do cliente ao executar o aplicativo. Os ativos são colocados na pasta wwwroot.

    O Webpack concluiu as seguintes tarefas:

    • Limpou o conteúdo do diretório wwwroot.
    • Converteu o TypeScript para JavaScript, um processo conhecido como transpilação.
    • Reduziu o tamanho do arquivo JavaScript gerado, um processo conhecido como minificação.
    • Copiou os arquivos JavaScript, CSS e HTML processados do src para o diretório wwwroot.
    • Injetou os seguintes elementos no arquivo wwwroot/index.html:
      • Uma marca <link>, fazendo referência ao arquivo wwwroot/main.<hash>.css. Essa marca é colocada imediatamente antes do fim da marca </head>.
      • Uma marca <script>, fazendo referência ao arquivo wwwroot/main.<hash>.js minimizado. Essa marca é colocada imediatamente após a marca </title> de fechamento.
  2. Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo wwwroot/index.html é fornecido em https://localhost:<port>.

    Se houver erros de compilação, tente fechar e reabrir a solução.

  3. Abra outra instância de navegador (qualquer navegador) e cole a URL na barra de endereços.

  4. Escolha qualquer navegador, digite algo na caixa de texto Mensagem e selecione o botão Enviar. O nome de usuário exclusivo e a mensagem são exibidas em ambas as páginas instantaneamente.

Mensagem exibida em ambas as janelas do navegador

Próximas etapas

Recursos adicionais

Este tutorial demonstra como usar o Webpack em um aplicativo Web SignalR do ASP.NET Core para empacotar e compilar um cliente escrito em TypeScript. O Webpack habilita os desenvolvedores a agrupar e criar recursos de um aplicativo Web do lado do cliente.

Neste tutorial, você aprenderá a:

  • Criar um aplicativo SignalR ASP.NET Core
  • Configurar o servidor SignalR
  • Configurar um pipeline de build usando o Webpack
  • Configurar o cliente TypeScript do SignalR
  • Habilitar a comunicação entre o cliente e o servidor

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Criar o aplicativo Web do ASP.NET Core

Por padrão, o Visual Studio usa a versão do npm encontrada no diretório de instalação. Para configurar o Visual Studio para pesquisar o npm na variável de ambiente PATH:

  1. Inicie o Visual Studio. Na janela Iniciar, selecione Continuar sem código.

  2. Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.

  3. Selecione a entrada $(PATH) na lista. Selecione aa seta para cima para mover a entrada para a segunda posição da lista e selecione OK:

    Configuração do Visual Studio.

Para criar um aplicativo Web ASP.NET Core:

  1. Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo ASP.NET Core Vazio. Selecione Avançar.
  2. Dê ao projeto o nome de SignalRWebpack e selecione Criar.
  3. Selecione .NET 6.0 (Long Term Support) na lista suspensa do Framework. Selecione Criar.

Adicione o pacote NuGet Microsoft.TypeScript.MSBuild ao projeto:

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó do projeto e selecione Gerenciar Pacotes NuGet. Na guia Procurar, pesquise Microsoft.TypeScript.MSBuild e selecione Instalar à direita para instalar o pacote.

O Visual Studio adiciona o pacote NuGet no nó Dependências no Gerenciador de Soluções, habilitando a compilação do TypeScript no projeto.

Configurar o servidor

Nesta seção, você configurará o aplicativo Web ASP.NET Core para enviar e receber mensagens SignalR.

  1. Em Program.cs, chame AddSignalR:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddSignalR();
    
  2. Novamente, em Program.cs, chame UseDefaultFiles e UseStaticFiles:

    var app = builder.Build();
    
    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    O código anterior permite que o servidor localize e forneça o arquivo index.html. O arquivo é fornecido se o usuário insere sua URL completa ou a URL raiz do aplicativo Web.

  3. Crie um novo diretório chamado de Hubs na raiz do projeto, SignalRWebpack/, para a classe de hub SignalR.

  4. Crie um arquivo Hubs/ChatHub.cs com o código a seguir:

    using Microsoft.AspNetCore.SignalR;
    
    namespace SignalRWebpack.Hubs;
    
    public class ChatHub : Hub
    {
        public async Task NewMessage(long username, string message) =>
            await Clients.All.SendAsync("messageReceived", username, message);
    }
    

    O código precedente transmite as mensagens recebidas para todos os usuários conectados quando o servidor as recebe. Não é necessário ter um método genérico on para receber todas as mensagens. Um método nomeado com o nome da mensagem é suficiente.

    Neste exemplo, o cliente TypeScript envia uma mensagem identificada como newMessage. O método NewMessage de C# espera os dados enviados pelo cliente. Uma chamada é feita para SendAsync em Clients.All. As mensagens recebidas são enviadas a todos os clientes conectados ao hub.

  5. Adicione a instrução using a seguir ao início do Program.cs para resolver a referência a ChatHub:

    using SignalRWebpack.Hubs;
    
  6. No Program.cs, mapeie a rota /hub para o hub ChatHub. Substitua o código que exibe Hello World! pelo código a seguir:

    app.MapHub<ChatHub>("/hub");
    

Configurar o cliente

Nesta seção, você criará um projeto Node.js para converter o TypeScript em JavaScript e agrupar recursos do lado do cliente, incluindo HTML e CSS, usando o Webpack.

  1. Execute o seguinte comando na raiz do projeto para criar um arquivo package.json:

    npm init -y
    
  2. Adicione a propriedade realçada ao arquivo package.json e salve as alterações de arquivo:

    {
      "name": "SignalRWebpack",
      "version": "1.0.0",
      "private": true,
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    

    Definir a propriedade private como true evita avisos de instalação de pacote na próxima etapa.

  3. Instalar os pacotes de npm exigidos. Execute o seguinte comando na raiz do projeto:

    npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
    

    A opção -E desabilita o comportamento padrão do npm de escrever os operadores de intervalo de versão semântica para package.json. Por exemplo, "webpack": "5.70.0" é usado em vez de "webpack": "^5.70.0". Essa opção evita atualizações não intencionais para versões de pacote mais recentes.

    Para obter mais informações, confira a documentação do npm-install.

  4. Substitua a propriedade scripts do arquivo package.json pelo código a seguir:

    "scripts": {
      "build": "webpack --mode=development --watch",
      "release": "webpack --mode=production",
      "publish": "npm run release && dotnet publish -c Release"
    },
    

    Os seguintes scripts são definidos:

    • build: agrupa os recursos do lado do cliente no modo de desenvolvimento e busca alterações no arquivo. O observador de arquivos faz com que o lote se regenere toda vez que um arquivo de projeto é alterado. A opção mode desabilita otimizações de produção, como tree shaking e minificação. use build somente no desenvolvimento.
    • release: agrupa recursos do lado do cliente no modo de produção.
    • publish: executa o script release para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET para publicar o aplicativo.
  5. Crie um arquivo chamado webpack.config.js na raiz do projeto, com o seguinte código:

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
    module.exports = {
      entry: "./src/index.ts",
      output: {
        path: path.resolve(__dirname, "wwwroot"),
        filename: "[name].[chunkhash].js",
        publicPath: "/",
      },
      resolve: {
        extensions: [".js", ".ts"],
      },
      module: {
        rules: [
          {
            test: /\.ts$/,
            use: "ts-loader",
          },
          {
            test: /\.css$/,
            use: [MiniCssExtractPlugin.loader, "css-loader"],
          },
        ],
      },
      plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
          template: "./src/index.html",
        }),
        new MiniCssExtractPlugin({
          filename: "css/[name].[chunkhash].css",
        }),
      ],
    };
    

    O arquivo anterior configura o processo de compilação Webpack:

    • A propriedade output substitui o valor padrão do dist. Em vez disso, o lote é emitido no diretório wwwroot.
    • A matriz resolve.extensions inclui .js para importar o JavaScript do cliente SignalR.
  6. Copie o diretório src e seu conteúdo do projeto de exemplo na raiz do projeto. O diretório src contém os seguintes arquivos:

    • index.html, que define a marcação de texto clichê da home page:

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="utf-8" />
          <title>ASP.NET Core SignalR with TypeScript and Webpack</title>
        </head>
        <body>
          <div id="divMessages" class="messages"></div>
          <div class="input-zone">
            <label id="lblMessage" for="tbMessage">Message:</label>
            <input id="tbMessage" class="input-zone-input" type="text" />
            <button id="btnSend">Send</button>
          </div>
        </body>
      </html>
      
    • css/main.css, que fornece estilos CSS para a home page:

      *,
      *::before,
      *::after {
        box-sizing: border-box;
      }
      
      html,
      body {
        margin: 0;
        padding: 0;
      }
      
      .input-zone {
        align-items: center;
        display: flex;
        flex-direction: row;
        margin: 10px;
      }
      
      .input-zone-input {
        flex: 1;
        margin-right: 10px;
      }
      
      .message-author {
        font-weight: bold;
      }
      
      .messages {
        border: 1px solid #000;
        margin: 10px;
        max-height: 300px;
        min-height: 300px;
        overflow-y: auto;
        padding: 5px;
      }
      
    • tsconfig.json, que configura o compilador TypeScript para produzir um JavaScript compatível com ECMAScript 5:

      {
        "compilerOptions": {
          "target": "es5"
        }
      }
      
    • index.ts:

      import * as signalR from "@microsoft/signalr";
      import "./css/main.css";
      
      const divMessages: HTMLDivElement = document.querySelector("#divMessages");
      const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
      const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
      const username = new Date().getTime();
      
      const connection = new signalR.HubConnectionBuilder()
          .withUrl("/hub")
          .build();
      
      connection.on("messageReceived", (username: string, message: string) => {
        const m = document.createElement("div");
      
        m.innerHTML = `<div class="message-author">${username}</div><div>${message}</div>`;
      
        divMessages.appendChild(m);
        divMessages.scrollTop = divMessages.scrollHeight;
      });
      
      connection.start().catch((err) => document.write(err));
      
      tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.key === "Enter") {
          send();
        }
      });
      
      btnSend.addEventListener("click", send);
      
      function send() {
        connection.send("newMessage", username, tbMessage.value)
          .then(() => (tbMessage.value = ""));
      }
      

    O código anterior recupera referências a elementos DOM e anexa dois manipuladores de eventos:

    • keyup: é acionado quando o usuário digita tbMessage na caixa de texto e chama a função send quando o usuário pressiona a tecla Enter.
    • click: é acionado quando o usuário seleciona o botão Enviar e a função de chamadas send é chamada.

    A classe HubConnectionBuilder cria um novo construtor para configurar a conexão do servidor. A função withUrl configura a URL do hub.

    O SignalR habilita a troca de mensagens entre um cliente e um servidor. Cada mensagem tem um nome específico. Por exemplo, mensagens com o nome messageReceived podem executar a lógica responsável por exibir a nova mensagem na zona de mensagens. É possível escutar uma mensagem específica por meio da função on. Qualquer número de nomes de mensagem pode ser ouvido. Também é possível passar parâmetros para a mensagem, como o nome do autor e o conteúdo da mensagem recebida. Quando o cliente recebe a mensagem, um novo elemento div é criado com o nome do autor e o conteúdo da mensagem em seu atributo innerHTML. Ele é adicionado ao elemento principal div que exibe as mensagens.

    Enviar uma mensagem por meio da conexão WebSockets exige uma chamada para o método send. O primeiro parâmetro do método é o nome da mensagem. Os dados da mensagem residem nos outros parâmetros. Neste exemplo, uma mensagem identificada como newMessage é enviada ao servidor. A mensagem é composta do nome de usuário e da entrada em uma caixa de texto. Se o envio for bem-sucedido, o valor da caixa de texto será limpo.

  7. Execute o comando a seguir na raiz do projeto:

    npm i @microsoft/signalr @types/node
    

    O comando anterior instala:

    • O cliente TypeScript do SignalR, que permite ao cliente enviar mensagens para o servidor.
    • As definições de tipo TypeScript para Node.js, que permite a verificação em tempo de compilação de tipos de Node.js.

Testar o aplicativo

Confirme que o aplicativo funciona com as seguintes etapas:

  1. Execute o Webpack no modo release. Usando a janela Console do Gerenciador de Pacotes, execute o comando a seguir na raiz do projeto. Se você não estiver na raiz do projeto, insira cd SignalRWebpack antes de inserir o comando.

    npm run release
    

    Este comando gera o fornecimento dos ativos do lado do cliente ao executar o aplicativo. Os ativos são colocados na pasta wwwroot.

    O Webpack concluiu as seguintes tarefas:

    • Limpou o conteúdo do diretório wwwroot.
    • Converteu o TypeScript para JavaScript, um processo conhecido como transpilação.
    • Reduziu o tamanho do arquivo JavaScript gerado, um processo conhecido como minificação.
    • Copiou os arquivos JavaScript, CSS e HTML processados do src para o diretório wwwroot.
    • Injetou os seguintes elementos no arquivo wwwroot/index.html:
      • Uma marca <link>, fazendo referência ao arquivo wwwroot/main.<hash>.css. Essa marca é colocada imediatamente antes do fim da marca </head>.
      • Uma marca <script>, fazendo referência ao arquivo wwwroot/main.<hash>.js minimizado. Essa marca é colocada imediatamente após a marca </title> de fechamento.
  2. Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo wwwroot/index.html é fornecido em https://localhost:<port>.

    Se houver erros de compilação, tente fechar e reabrir a solução.

  3. Abra outra instância de navegador (qualquer navegador) e cole a URL na barra de endereços.

  4. Escolha qualquer navegador, digite algo na caixa de texto Mensagem e selecione o botão Enviar. O nome de usuário exclusivo e a mensagem são exibidas em ambas as páginas instantaneamente.

Mensagem exibida em ambas as janelas do navegador

Próximas etapas

Recursos adicionais

Este tutorial demonstra como usar o Webpack em um aplicativo Web SignalR do ASP.NET Core para empacotar e compilar um cliente escrito em TypeScript. O Webpack habilita os desenvolvedores a agrupar e criar recursos de um aplicativo Web do lado do cliente.

Neste tutorial, você aprenderá a:

  • Gerar um aplicativo inicial SignalR do ASP.NET Core por scaffold
  • Configurar o cliente TypeScript do SignalR
  • Configurar um pipeline de build usando o Webpack
  • Configurar o servidor SignalR
  • Habilitar a comunicação entre o cliente e o servidor

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Criar o aplicativo Web do ASP.NET Core

Configure o Visual Studio para pesquisar o npm na variável de ambiente PATH. Por padrão, o Visual Studio usa a versão do npm encontrada no diretório de instalação. Siga estas instruções no Visual Studio:

  1. Inicie o Visual Studio. Na janela Iniciar, selecione Continuar sem código.

  2. Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.

  3. Selecione a entrada $(PATH) na lista. Selecione aa seta para cima para mover a entrada para a segunda posição da lista e selecione OK.

    Configuração do Visual Studio.

A configuração do Visual Studio foi concluída.

  1. Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo Aplicativo Web do ASP.NET Core. Selecione Avançar.
  2. Dê ao projeto o nome de *SignalRWebPac`` e selecione Criar.
  3. Selecione .NET Core no menu suspenso da estrutura de destino e selecione ASP.NET Core 3.1 no menu suspenso do seletor de estrutura. Selecione o modelo Vazio e, em seguida, Criar.

Adicionar o pacote Microsoft.TypeScript.MSBuild ao projeto:

  1. No Gerenciador de Soluções (painel à direita), clique com o botão direito do mouse no nó do projeto e selecione Gerenciar Pacotes NuGet. Na guia Procurar, pesquise Microsoft.TypeScript.MSBuild e clique em Instalar à direita para instalar o pacote.

O Visual Studio adiciona o pacote NuGet no nó Dependências no Gerenciador de Soluções, habilitando a compilação do TypeScript no projeto.

Configurar Webpack e TypeScript

As etapas a seguir configuram a conversão do TypeScript para JavaScript e o agrupamento de recursos no lado do cliente.

  1. Execute o seguinte comando na raiz do projeto para criar um arquivo package.json:

    npm init -y
    
  2. Adicione a propriedade realçada ao arquivo package.json e salve as alterações de arquivo:

    {
      "name": "SignalRWebPack",
      "version": "1.0.0",
      "private": true,
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    

    Definir a propriedade private como true evita avisos de instalação de pacote na próxima etapa.

  3. Instalar os pacotes de npm exigidos. Execute o seguinte comando na raiz do projeto:

    npm i -D -E clean-webpack-plugin@3.0.0 css-loader@3.4.2 html-webpack-plugin@3.2.0 mini-css-extract-plugin@0.9.0 ts-loader@6.2.1 typescript@3.7.5 webpack@4.41.5 webpack-cli@3.3.10
    

    Alguns detalhes de comando a se observar:

    • Um número de versão segue o sinal @ para cada nome de pacote. O npm instala essas versões específicas do pacote.
    • A opção -E desabilita o comportamento padrão do npm de escrever os operadores de intervalo de versão semântica para *packagejson. Por exemplo, "webpack": "4.41.5" é usado em vez de "webpack": "^4.41.5". Essa opção evita atualizações não intencionais para versões de pacote mais recentes.

    Veja os documentos de instalação de npm para saber mais detalhes.

  4. Substitua a propriedade scripts do arquivo package.json pelo código a seguir:

    "scripts": {
      "build": "webpack --mode=development --watch",
      "release": "webpack --mode=production",
      "publish": "npm run release && dotnet publish -c Release"
    },
    

    Uma explicação sobre os scripts:

    • build: agrupa os recursos do lado do cliente no modo de desenvolvimento e busca alterações no arquivo. O observador de arquivos faz com que o lote se regenere toda vez que um arquivo de projeto é alterado. A opção mode desabilita otimizações de produção, como tree shaking e minificação. Use somente build no desenvolvimento.
    • release: agrupa recursos do lado do cliente no modo de produção.
    • publish: executa o script release para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET Core para publicar o aplicativo.
  5. Crie um arquivo chamado webpack.config.js na raiz do projeto, com o seguinte código:

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    module.exports = {
        entry: "./src/index.ts",
        output: {
            path: path.resolve(__dirname, "wwwroot"),
            filename: "[name].[chunkhash].js",
            publicPath: "/"
        },
        resolve: {
            extensions: [".js", ".ts"]
        },
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    use: "ts-loader"
                },
                {
                    test: /\.css$/,
                    use: [MiniCssExtractPlugin.loader, "css-loader"]
                }
            ]
        },
        plugins: [
            new CleanWebpackPlugin(),
            new HtmlWebpackPlugin({
                template: "./src/index.html"
            }),
            new MiniCssExtractPlugin({
                filename: "css/[name].[chunkhash].css"
            })
        ]
    };
    

    O arquivo precedente configura a compilação Webpack. Detalhes de configuração a serem notados:

    • A propriedade output substitui o valor padrão do dist. Em vez disso, o lote é emitido no diretório wwwroot.
    • A matriz resolve.extensions inclui .js para importar o JavaScript do cliente SignalR.
  6. Crie um diretório src na raiz do projeto para armazenar os ativos do lado do cliente do projeto.

  7. Crie src/index.html com a marcação a seguir.

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>ASP.NET Core SignalR</title>
    </head>
    <body>
        <div id="divMessages" class="messages">
        </div>
        <div class="input-zone">
            <label id="lblMessage" for="tbMessage">Message:</label>
            <input id="tbMessage" class="input-zone-input" type="text" />
            <button id="btnSend">Send</button>
        </div>
    </body>
    </html>
    

    A HTML precedente define a marcação clichê da página inicial.

  8. Crie um novo diretório src/css. Seu propósito é armazenar os arquivos do projeto .css.

  9. Crie src/css/main.css com o seguinte CSS:

    *, *::before, *::after {
        box-sizing: border-box;
    }
    
    html, body {
        margin: 0;
        padding: 0;
    }
    
    .input-zone {
        align-items: center;
        display: flex;
        flex-direction: row;
        margin: 10px;
    }
    
    .input-zone-input {
        flex: 1;
        margin-right: 10px;
    }
    
    .message-author {
        font-weight: bold;
    }
    
    .messages {
        border: 1px solid #000;
        margin: 10px;
        max-height: 300px;
        min-height: 300px;
        overflow-y: auto;
        padding: 5px;
    }
    

    O arquivo precedente main.css define o estilo do aplicativo.

  10. Crie src/tsconfig.json com o seguinte JSON:

    {
      "compilerOptions": {
        "target": "es5"
      }
    }
    

    O código precedente configura o compilador TypeScript para produzir um JavaScript compatível com ECMAScript 5.

  11. Crie src/index.ts com o seguinte código:

    import "./css/main.css";
    
    const divMessages: HTMLDivElement = document.querySelector("#divMessages");
    const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
    const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
    const username = new Date().getTime();
    
    tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.key === "Enter") {
            send();
        }
    });
    
    btnSend.addEventListener("click", send);
    
    function send() {
    }
    

    O TypeScript precedente recupera referências a elementos DOM e anexa dois manipuladores de eventos:

    • keyup: esse evento é acionado quando o usuário digita na caixa de texto tbMessage. A função send é chamada quando o usuário pressionar a tecla Enter.
    • click: esse evento é acionado quando o usuário clica no botão Enviar. A função send é chamada.

Configurar o aplicativo

  1. Em Startup.Configure, adicione chamadas para UseDefaultFiles(IApplicationBuilder) e UseStaticFiles(IApplicationBuilder).

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        
        app.UseRouting();
        app.UseDefaultFiles();
        app.UseStaticFiles();
        
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<ChatHub>("/hub");
        });
            
    }
    

    O código anterior permite que o servidor localize e forneça o arquivo index.html. O arquivo é fornecido se o usuário insere sua URL completa ou a URL raiz do aplicativo Web.

  2. No final do Startup.Configure, mapeie uma rota /hub para o hub ChatHub. Substitua o código que exibe Olá, Mundo! pela linha a seguir:

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/hub");
    });
    
  3. Em Startup.ConfigureServices, chame AddSignalR.

    services.AddSignalR();
    
  4. Crie um novo diretório chamado Hubs no SignalRWebPack/ raiz do projeto para armazenar o hub SignalR.

  5. Crie Hubs/ChatHub.cs do hub com o seguinte código:

    using Microsoft.AspNetCore.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRWebPack.Hubs
    {
        public class ChatHub : Hub
        {
        }
    }
    
  6. Adicione a instrução using a seguir ao início do arquivo Startup.cs para resolver a referência a ChatHub:

    using SignalRWebPack.Hubs;
    

Habilitar a comunicação entre o cliente e o servidor

Atualmente, o aplicativo exibe um formulário básico para enviar mensagens, mas ainda não está funcional. O servidor está escutando uma rota específica, mas não faz nada com as mensagens enviadas.

  1. Execute o comando a seguir na raiz do projeto:

    npm i @microsoft/signalr @types/node
    

    O comando anterior instala:

    • O cliente TypeScript do SignalR, que permite ao cliente enviar mensagens para o servidor.
    • As definições de tipo TypeScript para Node.js, que permite a verificação em tempo de compilação de tipos de Node.js.
  2. Adicione o código realçado ao arquivo src/index.ts:

    import "./css/main.css";
    import * as signalR from "@microsoft/signalr";
    
    const divMessages: HTMLDivElement = document.querySelector("#divMessages");
    const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
    const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
    const username = new Date().getTime();
    
    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/hub")
        .build();
    
    connection.on("messageReceived", (username: string, message: string) => {
        let m = document.createElement("div");
    
        m.innerHTML =
            `<div class="message-author">${username}</div><div>${message}</div>`;
    
        divMessages.appendChild(m);
        divMessages.scrollTop = divMessages.scrollHeight;
    });
    
    connection.start().catch(err => document.write(err));
    
    tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.key === "Enter") {
            send();
        }
    });
    
    btnSend.addEventListener("click", send);
    
    function send() {
    }
    

    O código precedente é compatível com o recebimento de mensagens do servidor. A classe HubConnectionBuilder cria um novo construtor para configurar a conexão do servidor. A função withUrl configura a URL do hub.

    O SignalR habilita a troca de mensagens entre um cliente e um servidor. Cada mensagem tem um nome específico. Por exemplo, mensagens com o nome messageReceived podem executar a lógica responsável por exibir a nova mensagem na zona de mensagens. É possível escutar uma mensagem específica por meio da função on. Qualquer número de nomes de mensagem pode ser ouvido. Também é possível passar parâmetros para a mensagem, como o nome do autor e o conteúdo da mensagem recebida. Quando o cliente recebe a mensagem, um novo elemento div é criado com o nome do autor e o conteúdo da mensagem em seu atributo innerHTML. Ele é adicionado ao elemento principal div que exibe as mensagens.

  3. Agora que o cliente pode receber mensagens, configure-o para enviá-las. Adicione o código realçado ao arquivo src/index.ts:

    import "./css/main.css";
    import * as signalR from "@microsoft/signalr";
    
    const divMessages: HTMLDivElement = document.querySelector("#divMessages");
    const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
    const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
    const username = new Date().getTime();
    
    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/hub")
        .build();
    
    connection.on("messageReceived", (username: string, message: string) => {
        let messages = document.createElement("div");
    
        messages.innerHTML =
            `<div class="message-author">${username}</div><div>${message}</div>`;
    
        divMessages.appendChild(messages);
        divMessages.scrollTop = divMessages.scrollHeight;
    });
    
    connection.start().catch(err => document.write(err));
    
    tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.key === "Enter") {
            send();
        }
    });
    
    btnSend.addEventListener("click", send);
    
    function send() {
        connection.send("newMessage", username, tbMessage.value)
            .then(() => tbMessage.value = "");
    }
    

    Enviar uma mensagem por meio da conexão WebSockets exige uma chamada para o método send. O primeiro parâmetro do método é o nome da mensagem. Os dados da mensagem residem nos outros parâmetros. Neste exemplo, uma mensagem identificada como newMessage é enviada ao servidor. A mensagem é composta do nome de usuário e da entrada em uma caixa de texto. Se o envio for bem-sucedido, o valor da caixa de texto será limpo.

  4. Adicione o método NewMessage à classe ChatHub:

    using Microsoft.AspNetCore.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRWebPack.Hubs
    {
        public class ChatHub : Hub
        {
            public async Task NewMessage(long username, string message)
            {
                await Clients.All.SendAsync("messageReceived", username, message);
            }
        }
    }
    

    O código precedente transmite as mensagens recebidas para todos os usuários conectados quando o servidor as recebe. Não é necessário ter um método genérico on para receber todas as mensagens. Um método nomeado com o nome da mensagem é suficiente.

    Neste exemplo, o cliente TypeScript envia uma mensagem identificada como newMessage. O método NewMessage de C# espera os dados enviados pelo cliente. Uma chamada é feita para SendAsync em Clients.All. As mensagens recebidas são enviadas a todos os clientes conectados ao hub.

Testar o aplicativo

Confirme que o aplicativo funciona com as seguintes etapas.

  1. Execute o Webpack no modo de versão. Usando a janela Console do Gerenciador de Pacotes, execute o comando a seguir na raiz do projeto. Se você não estiver na raiz do projeto, insira cd SignalRWebPack antes de inserir o comando.

    npm run release
    

    Este comando gera o fornecimento dos ativos do lado do cliente ao executar o aplicativo. Os ativos são colocados na pasta wwwroot.

    O Webpack concluiu as seguintes tarefas:

    • Limpou o conteúdo do diretório wwwroot.
    • Converteu o TypeScript para JavaScript, um processo conhecido como transpilação.
    • Reduziu o tamanho do arquivo JavaScript gerado, um processo conhecido como minificação.
    • Copiou os arquivos JavaScript, CSS e HTML processados do src para o diretório wwwroot.
    • Injetou os seguintes elementos no arquivo wwwroot/index.html:
      • Uma marca <link>, fazendo referência ao arquivo wwwroot/main.<hash>.css. Essa marca é colocada imediatamente antes do fim da marca </head>.
      • Uma marca <script>, fazendo referência ao arquivo wwwroot/main.<hash>.js minimizado. Essa marca é colocada imediatamente após a marca </title> de fechamento.
  2. Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo wwwroot/index.html é fornecido em http://localhost:<port_number>.

    Se houver erros de compilação, tente fechar e reabrir a solução.

  3. Abra outra instância do navegador (qualquer navegador). Cole a URL na barra de endereços.

  4. Escolha qualquer navegador, digite algo na caixa de texto Mensagem e selecione o botão Enviar. O nome de usuário exclusivo e a mensagem são exibidas em ambas as páginas instantaneamente.

Mensagem exibida em ambas as janelas do navegador

Recursos adicionais

Este tutorial demonstra como usar o Webpack em um aplicativo Web SignalR do ASP.NET Core para empacotar e compilar um cliente escrito em TypeScript. O Webpack habilita os desenvolvedores a agrupar e criar recursos de um aplicativo Web do lado do cliente.

Neste tutorial, você aprenderá a:

  • Gerar um aplicativo inicial SignalR do ASP.NET Core por scaffold
  • Configurar o cliente TypeScript do SignalR
  • Configurar um pipeline de build usando o Webpack
  • Configurar o servidor SignalR
  • Habilitar a comunicação entre o cliente e o servidor

Exibir ou baixar código de exemplo (como baixar)

Pré-requisitos

Criar o aplicativo Web do ASP.NET Core

Configure o Visual Studio para pesquisar o npm na variável de ambiente PATH. Por padrão, o Visual Studio usa a versão do npm encontrada no diretório de instalação. Siga estas instruções no Visual Studio:

  1. Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.

  2. Selecione a entrada $(PATH) na lista. Selecione a seta para cima para mover a entrada para a segunda posição da lista.

    Configuração do Visual Studio

A configuração do Visual Studio foi concluída. É hora de criar o projeto.

  1. Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo Aplicativo Web do ASP.NET Core.
  2. Dê ao projeto o nome de *SignalRWebPack` e selecione Criar.
  3. Selecione .NET Core no menu suspenso da estrutura de destino e selecione ASP.NET Core 2.2 no menu suspenso do seletor de estrutura. Selecione o modelo Vazio e, em seguida, Criar.

Configurar Webpack e TypeScript

As etapas a seguir configuram a conversão do TypeScript para JavaScript e o agrupamento de recursos no lado do cliente.

  1. Execute o seguinte comando na raiz do projeto para criar um arquivo package.json:

    npm init -y
    
  2. Adicione a propriedade destacada ao arquivo package.json:

    {
      "name": "SignalRWebPack",
      "version": "1.0.0",
      "private": true,
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    

    Definir a propriedade private como true evita avisos de instalação de pacote na próxima etapa.

  3. Instalar os pacotes de npm exigidos. Execute o seguinte comando na raiz do projeto:

    npm install -D -E clean-webpack-plugin@1.0.1 css-loader@2.1.0 html-webpack-plugin@4.0.0-beta.5 mini-css-extract-plugin@0.5.0 ts-loader@5.3.3 typescript@3.3.3 webpack@4.29.3 webpack-cli@3.2.3
    

    Alguns detalhes de comando a se observar:

    • Um número de versão segue o sinal @ para cada nome de pacote. O npm instala essas versões específicas do pacote.
    • A opção -E desabilita o comportamento padrão do npm de escrever os operadores de intervalo de versão semântica para *packagejson. Por exemplo, "webpack": "4.29.3" é usado em vez de "webpack": "^4.29.3". Essa opção evita atualizações não intencionais para versões de pacote mais recentes.

    Veja os documentos de instalação de npm para saber mais detalhes.

  4. Substitua a propriedade scripts do arquivo package.json pelo código a seguir:

    "scripts": {
      "build": "webpack --mode=development --watch",
      "release": "webpack --mode=production",
      "publish": "npm run release && dotnet publish -c Release"
    },
    

    Uma explicação sobre os scripts:

    • build: agrupa os recursos do lado do cliente no modo de desenvolvimento e busca alterações no arquivo. O observador de arquivos faz com que o lote se regenere toda vez que um arquivo de projeto é alterado. A opção mode desabilita otimizações de produção, como tree shaking e minificação. Use somente build no desenvolvimento.
    • release: agrupa recursos do lado do cliente no modo de produção.
    • publish: executa o script release para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET Core para publicar o aplicativo.
  5. Crie um arquivo chamado *webpack.config.js na raiz do projeto, com o seguinte código:

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    const CleanWebpackPlugin = require("clean-webpack-plugin");
    const MiniCssExtractPlugin = require("mini-css-extract-plugin");
    
    module.exports = {
        entry: "./src/index.ts",
        output: {
            path: path.resolve(__dirname, "wwwroot"),
            filename: "[name].[chunkhash].js",
            publicPath: "/"
        },
        resolve: {
            extensions: [".js", ".ts"]
        },
        module: {
            rules: [
                {
                    test: /\.ts$/,
                    use: "ts-loader"
                },
                {
                    test: /\.css$/,
                    use: [MiniCssExtractPlugin.loader, "css-loader"]
                }
            ]
        },
        plugins: [
            new CleanWebpackPlugin(["wwwroot/*"]),
            new HtmlWebpackPlugin({
                template: "./src/index.html"
            }),
            new MiniCssExtractPlugin({
                filename: "css/[name].[chunkhash].css"
            })
        ]
    };
    

    O arquivo precedente configura a compilação Webpack. Detalhes de configuração a serem notados:

    • A propriedade output substitui o valor padrão do dist. Em vez disso, o lote é emitido no diretório wwwroot.
    • A matriz resolve.extensions inclui .js para importar o JavaScript do cliente SignalR.
  6. Crie um diretório src na raiz do projeto para armazenar os ativos do lado do cliente do projeto.

  7. Crie src/index.html com a marcação a seguir.

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>ASP.NET Core SignalR</title>
    </head>
    <body>
        <div id="divMessages" class="messages">
        </div>
        <div class="input-zone">
            <label id="lblMessage" for="tbMessage">Message:</label>
            <input id="tbMessage" class="input-zone-input" type="text" />
            <button id="btnSend">Send</button>
        </div>
    </body>
    </html>
    

    A HTML precedente define a marcação clichê da página inicial.

  8. Crie um novo diretório src/css. Seu propósito é armazenar os arquivos do projeto .css.

  9. Crie src/css/main.css com a marcação a seguir:

    *, *::before, *::after {
        box-sizing: border-box;
    }
    
    html, body {
        margin: 0;
        padding: 0;
    }
    
    .input-zone {
        align-items: center;
        display: flex;
        flex-direction: row;
        margin: 10px;
    }
    
    .input-zone-input {
        flex: 1;
        margin-right: 10px;
    }
    
    .message-author {
        font-weight: bold;
    }
    
    .messages {
        border: 1px solid #000;
        margin: 10px;
        max-height: 300px;
        min-height: 300px;
        overflow-y: auto;
        padding: 5px;
    }
    

    O arquivo precedente main.css define o estilo do aplicativo.

  10. Crie src/tsconfig.json com o seguinte JSON:

    {
      "compilerOptions": {
        "target": "es5"
      }
    }
    

    O código precedente configura o compilador TypeScript para produzir um JavaScript compatível com ECMAScript 5.

  11. Crie src/index.ts com o seguinte código:

    import "./css/main.css";
    
    const divMessages: HTMLDivElement = document.querySelector("#divMessages");
    const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
    const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
    const username = new Date().getTime();
    
    tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.keyCode === 13) {
            send();
        }
    });
    
    btnSend.addEventListener("click", send);
    
    function send() {
    }
    

    O TypeScript precedente recupera referências a elementos DOM e anexa dois manipuladores de eventos:

    • keyup: esse evento é acionado quando o usuário digita na caixa de texto tbMessage. A função send é chamada quando o usuário pressionar a tecla Enter.
    • click: esse evento é acionado quando o usuário clica no botão Enviar. A função send é chamada.

Configurar o aplicativo do ASP.NET Core

  1. O código fornecido no método Startup.Configure exibe Olá, Mundo. Substitua a chamada de método app.Run por chamadas para UseDefaultFiles(IApplicationBuilder) e UseStaticFiles(IApplicationBuilder).

    app.UseDefaultFiles();
    app.UseStaticFiles();
    

    O código anterior permite que o servidor localize e forneça o arquivo index.html, se o usuário inserir a URL completa ou a URL raiz do aplicativo Web.

  2. Chame AddSignalR em Startup.ConfigureServices. Ela adiciona os serviços SignalR ao projeto.

    services.AddSignalR();
    
  3. Mapeie uma rota /hub para o hub ChatHub. Adicione as linhas a seguir ao final do método Startup.Configure:

    app.UseSignalR(options =>
    {
        options.MapHub<ChatHub>("/hub");
    });
    
  4. Crie um novo diretório, chamado Hubs, na raiz do projeto. A finalidade é armazenar o hub SignalR, que é criado na próxima etapa.

  5. Crie Hubs/ChatHub.cs do hub com o seguinte código:

    using Microsoft.AspNetCore.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRWebPack.Hubs
    {
        public class ChatHub : Hub
        {
        }
    }
    
  6. Adicione o código a seguir ao topo do arquivo Startup.cs para resolver a referência ChatHub:

    using SignalRWebPack.Hubs;
    

Habilitar a comunicação entre o cliente e o servidor

Atualmente, o aplicativo exibe um formulário simples para enviar mensagens. Nada acontece quando você tenta fazer alguma coisa. O servidor está escutando uma rota específica, mas não faz nada com as mensagens enviadas.

  1. Execute o comando a seguir na raiz do projeto:

    npm install @aspnet/signalr
    

    O comando anterior instala o cliente TypeScript do SignalR, que permite ao cliente enviar mensagens para o servidor.

  2. Adicione o código realçado ao arquivo src/index.ts:

    import "./css/main.css";
    import * as signalR from "@aspnet/signalr";
    
    const divMessages: HTMLDivElement = document.querySelector("#divMessages");
    const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
    const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
    const username = new Date().getTime();
    
    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/hub")
        .build();
    
    connection.on("messageReceived", (username: string, message: string) => {
        let m = document.createElement("div");
    
        m.innerHTML =
            `<div class="message-author">${username}</div><div>${message}</div>`;
    
        divMessages.appendChild(m);
        divMessages.scrollTop = divMessages.scrollHeight;
    });
    
    connection.start().catch(err => document.write(err));
    
    tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.keyCode === 13) {
            send();
        }
    });
    
    btnSend.addEventListener("click", send);
    
    function send() {
    }
    

    O código precedente é compatível com o recebimento de mensagens do servidor. A classe HubConnectionBuilder cria um novo construtor para configurar a conexão do servidor. A função withUrl configura a URL do hub.

    O SignalR habilita a troca de mensagens entre um cliente e um servidor. Cada mensagem tem um nome específico. Por exemplo, mensagens com o nome messageReceived podem executar a lógica responsável por exibir a nova mensagem na zona de mensagens. É possível escutar uma mensagem específica por meio da função on. Você pode escutar qualquer número de nomes de mensagem. Também é possível passar parâmetros para a mensagem, como o nome do autor e o conteúdo da mensagem recebida. Quando o cliente recebe a mensagem, um novo elemento div é criado com o nome do autor e o conteúdo da mensagem em seu atributo innerHTML. A nova mensagem é adicionada ao elemento principal div que exibe as mensagens.

  3. Agora que o cliente pode receber mensagens, configure-o para enviá-las. Adicione o código realçado ao arquivo src/index.ts:

    import "./css/main.css";
    import * as signalR from "@aspnet/signalr";
    
    const divMessages: HTMLDivElement = document.querySelector("#divMessages");
    const tbMessage: HTMLInputElement = document.querySelector("#tbMessage");
    const btnSend: HTMLButtonElement = document.querySelector("#btnSend");
    const username = new Date().getTime();
    
    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/hub")
        .build();
    
    connection.on("messageReceived", (username: string, message: string) => {
        let messageContainer = document.createElement("div");
    
        messageContainer.innerHTML =
            `<div class="message-author">${username}</div><div>${message}</div>`;
    
        divMessages.appendChild(messageContainer);
        divMessages.scrollTop = divMessages.scrollHeight;
    });
    
    connection.start().catch(err => document.write(err));
    
    tbMessage.addEventListener("keyup", (e: KeyboardEvent) => {
        if (e.keyCode === 13) {
            send();
        }
    });
    
    btnSend.addEventListener("click", send);
    
    function send() {
        connection.send("newMessage", username, tbMessage.value)
                  .then(() => tbMessage.value = "");
    }
    

    Enviar uma mensagem por meio da conexão WebSockets exige uma chamada para o método send. O primeiro parâmetro do método é o nome da mensagem. Os dados da mensagem residem nos outros parâmetros. Neste exemplo, uma mensagem identificada como newMessage é enviada ao servidor. A mensagem é composta do nome de usuário e da entrada em uma caixa de texto. Se o envio for bem-sucedido, o valor da caixa de texto será limpo.

  4. Adicione o método NewMessage à classe ChatHub:

    using Microsoft.AspNetCore.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRWebPack.Hubs
    {
        public class ChatHub : Hub
        {
            public async Task NewMessage(long username, string message)
            {
                await Clients.All.SendAsync("messageReceived", username, message);
            }
        }
    }
    

    O código precedente transmite as mensagens recebidas para todos os usuários conectados quando o servidor as recebe. Não é necessário ter um método genérico on para receber todas as mensagens. Um método nomeado com o nome da mensagem é suficiente.

    Neste exemplo, o cliente TypeScript envia uma mensagem identificada como newMessage. O método NewMessage de C# espera os dados enviados pelo cliente. Uma chamada é feita para SendAsync em Clients.All. As mensagens recebidas são enviadas a todos os clientes conectados ao hub.

Testar o aplicativo

Confirme que o aplicativo funciona com as seguintes etapas.

  1. Execute o Webpack no modo de versão. Usando a janela Console do Gerenciador de Pacotes, execute o comando a seguir na raiz do projeto. Se você não estiver na raiz do projeto, insira cd SignalRWebPack antes de inserir o comando.

    npm run release
    

    Este comando gera o fornecimento dos ativos do lado do cliente ao executar o aplicativo. Os ativos são colocados na pasta wwwroot.

    O Webpack concluiu as seguintes tarefas:

    • Limpou o conteúdo do diretório wwwroot.
    • Converteu o TypeScript para JavaScript, um processo conhecido como transpilação.
    • Reduziu o tamanho do arquivo JavaScript gerado, um processo conhecido como minificação.
    • Copiou os arquivos JavaScript, CSS e HTML processados do src para o diretório wwwroot.
    • Injetou os seguintes elementos no arquivo wwwroot/index.html:
      • Uma marca <link>, fazendo referência ao arquivo wwwroot/main.<hash>.css. Essa marca é colocada imediatamente antes do fim da marca </head>.
      • Uma marca <script>, fazendo referência ao arquivo wwwroot/main.<hash>.js minimizado. Essa marca é colocada imediatamente após a marca </title> de fechamento.
  2. Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo wwwroot/index.html é fornecido em http://localhost:<port_number>.

  3. Abra outra instância do navegador (qualquer navegador). Cole a URL na barra de endereços.

  4. Escolha qualquer navegador, digite algo na caixa de texto Mensagem e selecione o botão Enviar. O nome de usuário exclusivo e a mensagem são exibidas em ambas as páginas instantaneamente.

Mensagem exibida em ambas as janelas do navegador

Recursos adicionais