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.
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
Visual Studio 2022 com a carga de trabalho de desenvolvimento Web e do ASP.NET.
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.
Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.
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:.
Para criar um aplicativo Web ASP.NET Core:
- Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo ASP.NET Core Vazio. Selecione Avançar.
- Dê ao projeto o nome de
SignalRWebpack
e selecione Criar. - Selecione .NET 8.0 (suporte a longo prazo) na lista suspensa do Framework. Selecione Criar.
Adicione o pacote NuGet Microsoft.TypeScript.MSBuild ao projeto:
- 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.
Em
Program.cs
, chame AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();
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.Crie um novo diretório chamado de
Hubs
na raiz do projeto,SignalRWebpack/
, para a classe de hub SignalR.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.
- O cliente TypeScript envia uma mensagem identificada como
Adicione a instrução
using
a seguir ao início doProgram.cs
para resolver a referência aChatHub
:using SignalRWebpack.Hubs;
No
Program.cs
, mapeie a rota/hub
para o hubChatHub
. Substitua o código que exibeHello 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.
Execute o seguinte comando na raiz do projeto para criar um arquivo
package.json
:npm init -y
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
comotrue
evita avisos de instalação de pacote na próxima etapa.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 parapackage.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.
Substitua a propriedade
scripts
do arquivopackage.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çãomode
desabilita otimizações de produção, como tree shaking e minificação. usebuild
somente no desenvolvimento.release
: agrupa recursos do lado do cliente no modo de produção.publish
: executa o scriptrelease
para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET para publicar o aplicativo.
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 dodist
. Em vez disso, o lote é emitido no diretóriowwwroot
. - A matriz
resolve.extensions
inclui.js
para importar o JavaScript do cliente SignalR.
- A propriedade
Crie um novo diretório chamado de
src
na raiz do projeto,SignalRWebpack/
, para a código do cliente.Copie o diretório
src
e seu conteúdo do projeto de exemplo na raiz do projeto. O diretóriosrc
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 digitatbMessage
na caixa de texto e chama a funçãosend
quando o usuário pressiona a tecla Enter.click
: é acionado quando o usuário seleciona o botão Enviar e a função de chamadassend
é chamada.
A classe
HubConnectionBuilder
cria um novo construtor para configurar a conexão do servidor. A funçãowithUrl
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çãoon
. 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 elementodiv
é criado com o nome do autor e o conteúdo da mensagem em seu atributoinnerHTML
. Ele é adicionado ao elemento principaldiv
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 comonewMessage
é 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.
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:
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óriowwwroot
. - Injetou os seguintes elementos no arquivo
wwwroot/index.html
:- Uma marca
<link>
, fazendo referência ao arquivowwwroot/main.<hash>.css
. Essa marca é colocada imediatamente antes do fim da marca</head>
. - Uma marca
<script>
, fazendo referência ao arquivowwwroot/main.<hash>.js
minimizado. Essa marca é colocada imediatamente após a marca</title>
de fechamento.
- Uma marca
- Limpou o conteúdo do diretório
Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo
wwwroot/index.html
é fornecido emhttps://localhost:<port>
.Se houver erros de compilação, tente fechar e reabrir a solução.
Abra outra instância de navegador (qualquer navegador) e cole a URL na barra de endereços.
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.
Próximas etapas
- Hubs fortemente tipados
- Autenticação e autorização no ASP.NET Core SignalR
- Protocolo do Hub MessagePack no SignalR para ASP.NET Core
Recursos adicionais
- Cliente ASP.NET Core JavaScriptSignalR
- Usar hubs no ASP.NET Core SignalR
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
Visual Studio 2022 com a carga de trabalho de desenvolvimento Web e do ASP.NET.
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.
Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.
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:.
Para criar um aplicativo Web ASP.NET Core:
- Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo ASP.NET Core Vazio. Selecione Avançar.
- Dê ao projeto o nome de
SignalRWebpack
e selecione Criar. - Selecione
.NET 7.0 (Standard Term Support)
na lista suspensa do Framework. Selecione Criar.
Adicione o pacote NuGet Microsoft.TypeScript.MSBuild ao projeto:
- 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.
Em
Program.cs
, chame AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();
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.Crie um novo diretório chamado de
Hubs
na raiz do projeto,SignalRWebpack/
, para a classe de hub SignalR.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.
- O cliente TypeScript envia uma mensagem identificada como
Adicione a instrução
using
a seguir ao início doProgram.cs
para resolver a referência aChatHub
:using SignalRWebpack.Hubs;
No
Program.cs
, mapeie a rota/hub
para o hubChatHub
. Substitua o código que exibeHello 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.
Execute o seguinte comando na raiz do projeto para criar um arquivo
package.json
:npm init -y
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
comotrue
evita avisos de instalação de pacote na próxima etapa.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 parapackage.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.
Substitua a propriedade
scripts
do arquivopackage.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çãomode
desabilita otimizações de produção, como tree shaking e minificação. usebuild
somente no desenvolvimento.release
: agrupa recursos do lado do cliente no modo de produção.publish
: executa o scriptrelease
para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET para publicar o aplicativo.
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 dodist
. Em vez disso, o lote é emitido no diretóriowwwroot
. - A matriz
resolve.extensions
inclui.js
para importar o JavaScript do cliente SignalR.
- A propriedade
Copie o diretório
src
e seu conteúdo do projeto de exemplo na raiz do projeto. O diretóriosrc
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 digitatbMessage
na caixa de texto e chama a funçãosend
quando o usuário pressiona a tecla Enter.click
: é acionado quando o usuário seleciona o botão Enviar e a função de chamadassend
é chamada.
A classe
HubConnectionBuilder
cria um novo construtor para configurar a conexão do servidor. A funçãowithUrl
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çãoon
. 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 elementodiv
é criado com o nome do autor e o conteúdo da mensagem em seu atributoinnerHTML
. Ele é adicionado ao elemento principaldiv
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 comonewMessage
é 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.
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:
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óriowwwroot
. - Injetou os seguintes elementos no arquivo
wwwroot/index.html
:- Uma marca
<link>
, fazendo referência ao arquivowwwroot/main.<hash>.css
. Essa marca é colocada imediatamente antes do fim da marca</head>
. - Uma marca
<script>
, fazendo referência ao arquivowwwroot/main.<hash>.js
minimizado. Essa marca é colocada imediatamente após a marca</title>
de fechamento.
- Uma marca
- Limpou o conteúdo do diretório
Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo
wwwroot/index.html
é fornecido emhttps://localhost:<port>
.Se houver erros de compilação, tente fechar e reabrir a solução.
Abra outra instância de navegador (qualquer navegador) e cole a URL na barra de endereços.
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.
Próximas etapas
- Hubs fortemente tipados
- Autenticação e autorização no ASP.NET Core SignalR
- Protocolo do Hub MessagePack no SignalR para ASP.NET Core
Recursos adicionais
- Cliente ASP.NET Core JavaScriptSignalR
- Usar hubs no ASP.NET Core SignalR
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
- Visual Studio 2022 com a carga de trabalho do ASP.NET e desenvolvimento Web.
- SDK do .NET 6.0
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.
Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.
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:.
Para criar um aplicativo Web ASP.NET Core:
- Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo ASP.NET Core Vazio. Selecione Avançar.
- Dê ao projeto o nome de
SignalRWebpack
e selecione Criar. - Selecione
.NET 6.0 (Long Term Support)
na lista suspensa do Framework. Selecione Criar.
Adicione o pacote NuGet Microsoft.TypeScript.MSBuild ao projeto:
- 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.
Em
Program.cs
, chame AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();
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.Crie um novo diretório chamado de
Hubs
na raiz do projeto,SignalRWebpack/
, para a classe de hub SignalR.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étodoNewMessage
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.Adicione a instrução
using
a seguir ao início doProgram.cs
para resolver a referência aChatHub
:using SignalRWebpack.Hubs;
No
Program.cs
, mapeie a rota/hub
para o hubChatHub
. Substitua o código que exibeHello 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.
Execute o seguinte comando na raiz do projeto para criar um arquivo
package.json
:npm init -y
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
comotrue
evita avisos de instalação de pacote na próxima etapa.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 parapackage.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.
Substitua a propriedade
scripts
do arquivopackage.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çãomode
desabilita otimizações de produção, como tree shaking e minificação. usebuild
somente no desenvolvimento.release
: agrupa recursos do lado do cliente no modo de produção.publish
: executa o scriptrelease
para agrupar recursos do lado do cliente no modo de produção. Chama o comando publicar da CLI do .NET para publicar o aplicativo.
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 dodist
. Em vez disso, o lote é emitido no diretóriowwwroot
. - A matriz
resolve.extensions
inclui.js
para importar o JavaScript do cliente SignalR.
- A propriedade
Copie o diretório
src
e seu conteúdo do projeto de exemplo na raiz do projeto. O diretóriosrc
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 digitatbMessage
na caixa de texto e chama a funçãosend
quando o usuário pressiona a tecla Enter.click
: é acionado quando o usuário seleciona o botão Enviar e a função de chamadassend
é chamada.
A classe
HubConnectionBuilder
cria um novo construtor para configurar a conexão do servidor. A funçãowithUrl
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çãoon
. 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 elementodiv
é criado com o nome do autor e o conteúdo da mensagem em seu atributoinnerHTML
. Ele é adicionado ao elemento principaldiv
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 comonewMessage
é 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.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:
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, insiracd 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óriowwwroot
. - Injetou os seguintes elementos no arquivo
wwwroot/index.html
:- Uma marca
<link>
, fazendo referência ao arquivowwwroot/main.<hash>.css
. Essa marca é colocada imediatamente antes do fim da marca</head>
. - Uma marca
<script>
, fazendo referência ao arquivowwwroot/main.<hash>.js
minimizado. Essa marca é colocada imediatamente após a marca</title>
de fechamento.
- Uma marca
- Limpou o conteúdo do diretório
Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo
wwwroot/index.html
é fornecido emhttps://localhost:<port>
.Se houver erros de compilação, tente fechar e reabrir a solução.
Abra outra instância de navegador (qualquer navegador) e cole a URL na barra de endereços.
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.
Próximas etapas
- Hubs fortemente tipados
- Autenticação e autorização no ASP.NET Core SignalR
- Protocolo do Hub MessagePack no SignalR para ASP.NET Core
Recursos adicionais
- Cliente ASP.NET Core JavaScriptSignalR
- Usar hubs no ASP.NET Core SignalR
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
- Visual Studio 2019 com carga de trabalho ASP.NET e desenvolvimento Web
- SDK do .NET Core 3.0 ou posterior
- Node.js com npm
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:
Inicie o Visual Studio. Na janela Iniciar, selecione Continuar sem código.
Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.
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.
.
A configuração do Visual Studio foi concluída.
- Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo Aplicativo Web do ASP.NET Core. Selecione Avançar.
- Dê ao projeto o nome de *SignalRWebPac`` e selecione Criar.
- 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:
- 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.
Execute o seguinte comando na raiz do projeto para criar um arquivo
package.json
:npm init -y
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
comotrue
evita avisos de instalação de pacote na próxima etapa.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.
- Um número de versão segue o sinal
Substitua a propriedade
scripts
do arquivopackage.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çãomode
desabilita otimizações de produção, como tree shaking e minificação. Use somentebuild
no desenvolvimento.release
: agrupa recursos do lado do cliente no modo de produção.publish
: executa o scriptrelease
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.
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 dodist
. Em vez disso, o lote é emitido no diretóriowwwroot
. - A matriz
resolve.extensions
inclui.js
para importar o JavaScript do cliente SignalR.
- A propriedade
Crie um diretório src na raiz do projeto para armazenar os ativos do lado do cliente do projeto.
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.
Crie um novo diretório src/css. Seu propósito é armazenar os arquivos do projeto
.css
.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.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.
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 textotbMessage
. A funçãosend
é chamada quando o usuário pressionar a tecla Enter.click
: esse evento é acionado quando o usuário clica no botão Enviar. A funçãosend
é chamada.
Configurar o aplicativo
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.No final do
Startup.Configure
, mapeie uma rota /hub para o hubChatHub
. Substitua o código que exibe Olá, Mundo! pela linha a seguir:app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/hub"); });
Em
Startup.ConfigureServices
, chame AddSignalR.services.AddSignalR();
Crie um novo diretório chamado Hubs no SignalRWebPack/ raiz do projeto para armazenar o hub SignalR.
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 { } }
Adicione a instrução
using
a seguir ao início do arquivoStartup.cs
para resolver a referência aChatHub
: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.
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.
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çãowithUrl
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çãoon
. 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 elementodiv
é criado com o nome do autor e o conteúdo da mensagem em seu atributoinnerHTML
. Ele é adicionado ao elemento principaldiv
que exibe as mensagens.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 comonewMessage
é 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.Adicione o método
NewMessage
à classeChatHub
: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étodoNewMessage
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.
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óriowwwroot
. - Injetou os seguintes elementos no arquivo
wwwroot/index.html
:- Uma marca
<link>
, fazendo referência ao arquivowwwroot/main.<hash>.css
. Essa marca é colocada imediatamente antes do fim da marca</head>
. - Uma marca
<script>
, fazendo referência ao arquivowwwroot/main.<hash>.js
minimizado. Essa marca é colocada imediatamente após a marca</title>
de fechamento.
- Uma marca
- Limpou o conteúdo do diretório
Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo
wwwroot/index.html
é fornecido emhttp://localhost:<port_number>
.Se houver erros de compilação, tente fechar e reabrir a solução.
Abra outra instância do navegador (qualquer navegador). Cole a URL na barra de endereços.
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.
Recursos adicionais
- Cliente ASP.NET Core JavaScriptSignalR
- Usar hubs no ASP.NET Core SignalR
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
- Visual Studio 2019 com carga de trabalho ASP.NET e desenvolvimento Web
- SDK 2.2 ou posterior do .NET Core
- Node.js com npm
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:
Navegue para Ferramentas>Opções>Projetos e Soluções>Gerenciamento de Pacotes da Web>Ferramentas da Web Externas.
Selecione a entrada $(PATH) na lista. Selecione a seta para cima para mover a entrada para a segunda posição da lista.
A configuração do Visual Studio foi concluída. É hora de criar o projeto.
- Use a opção do menu Arquivo>Novo>Projeto e escolha o modelo Aplicativo Web do ASP.NET Core.
- Dê ao projeto o nome de *SignalRWebPack` e selecione Criar.
- 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.
Execute o seguinte comando na raiz do projeto para criar um arquivo
package.json
:npm init -y
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
comotrue
evita avisos de instalação de pacote na próxima etapa.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.
- Um número de versão segue o sinal
Substitua a propriedade
scripts
do arquivopackage.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çãomode
desabilita otimizações de produção, como tree shaking e minificação. Use somentebuild
no desenvolvimento.release
: agrupa recursos do lado do cliente no modo de produção.publish
: executa o scriptrelease
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.
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 dodist
. Em vez disso, o lote é emitido no diretóriowwwroot
. - A matriz
resolve.extensions
inclui.js
para importar o JavaScript do cliente SignalR.
- A propriedade
Crie um diretório src na raiz do projeto para armazenar os ativos do lado do cliente do projeto.
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.
Crie um novo diretório src/css. Seu propósito é armazenar os arquivos do projeto
.css
.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.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.
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 textotbMessage
. A funçãosend
é chamada quando o usuário pressionar a tecla Enter.click
: esse evento é acionado quando o usuário clica no botão Enviar. A funçãosend
é chamada.
Configurar o aplicativo do ASP.NET Core
O código fornecido no método
Startup.Configure
exibe Olá, Mundo. Substitua a chamada de métodoapp.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.Chame AddSignalR em
Startup.ConfigureServices
. Ela adiciona os serviços SignalR ao projeto.services.AddSignalR();
Mapeie uma rota /hub para o hub
ChatHub
. Adicione as linhas a seguir ao final do métodoStartup.Configure
:app.UseSignalR(options => { options.MapHub<ChatHub>("/hub"); });
Crie um novo diretório, chamado Hubs, na raiz do projeto. A finalidade é armazenar o hub SignalR, que é criado na próxima etapa.
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 { } }
Adicione o código a seguir ao topo do arquivo
Startup.cs
para resolver a referênciaChatHub
: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.
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.
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çãowithUrl
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çãoon
. 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 elementodiv
é criado com o nome do autor e o conteúdo da mensagem em seu atributoinnerHTML
. A nova mensagem é adicionada ao elemento principaldiv
que exibe as mensagens.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 comonewMessage
é 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.Adicione o método
NewMessage
à classeChatHub
: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étodoNewMessage
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.
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óriowwwroot
. - Injetou os seguintes elementos no arquivo
wwwroot/index.html
:- Uma marca
<link>
, fazendo referência ao arquivowwwroot/main.<hash>.css
. Essa marca é colocada imediatamente antes do fim da marca</head>
. - Uma marca
<script>
, fazendo referência ao arquivowwwroot/main.<hash>.js
minimizado. Essa marca é colocada imediatamente após a marca</title>
de fechamento.
- Uma marca
- Limpou o conteúdo do diretório
Selecione Debug>Iniciar sem depuração para iniciar o aplicativo em um navegador sem anexar o depurador. O arquivo
wwwroot/index.html
é fornecido emhttp://localhost:<port_number>
.Abra outra instância do navegador (qualquer navegador). Cole a URL na barra de endereços.
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.
Recursos adicionais
- Cliente ASP.NET Core JavaScriptSignalR
- Usar hubs no ASP.NET Core SignalR
Comentários
https://aka.ms/ContentUserFeedback.
Em breve: Ao longo de 2024, eliminaremos os problemas do GitHub como o mecanismo de comentários para conteúdo e o substituiremos por um novo sistema de comentários. Para obter mais informações, consulteEnviar e exibir comentários de