Tutorial: Introducción a ASP.NET Core SignalR mediante TypeScript y Webpack
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 8 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulte la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión .NET 8 de este artículo.
En este tutorial se describe el uso de Webpack en una aplicación web ASP.NET Core SignalR para unir y crear un cliente escrito en TypeScript. Webpack permite a los desarrolladores agrupar y compilar los recursos del lado cliente de una aplicación web.
En este tutorial aprenderá a:
- Crear una aplicación SignalR de ASP.NET Core
- Configurar el servidor SignalR
- Configuración de una canalización de compilación mediante Webpack
- Configurar el cliente de TypeScript para SignalR
- Habilitar la comunicación entre el cliente y el servidor
Vea o descargue el código de ejemplo (cómo descargarlo)
Requisitos previos
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Creación de la aplicación web ASP.NET Core
De forma predeterminada, Visual Studio usa la versión de npm que se encuentra en su directorio de instalación. Para configurar Visual Studio a fin de buscar npm en la variable de entorno PATH
:
Inicie Visual Studio. En la ventana de inicio, seleccione Continuar sin código.
Vaya a Herramientas>Opciones>Proyectos y soluciones>Administración de paquetes web>Herramientas web externas.
Seleccione la entrada
$(PATH)
en la lista. Seleccione la flecha arriba para mover la entrada a la segunda posición de la lista y seleccione Aceptar:.
Para crear una aplicación web de ASP.NET Core:
- Use la opción de menú Archivo>Nuevo>Proyecto y seleccione la plantilla ASP.NET Core vacío. Seleccione Next (Siguiente).
- Asigne el nombre
SignalRWebpack
al proyecto y seleccione Crear. - Seleccione .NET 8.0 (Compatibilidad a largo plazo) en la lista desplegable Framework. Seleccione Crear.
Agregue el paquete NuGet Microsoft.TypeScript.MSBuild al proyecto:
- En el Explorador de soluciones, haga clic con el botón derecho en el nodo del proyecto y seleccione Administrar paquetes NuGet. En la pestaña Examinar, busque
Microsoft.TypeScript.MSBuild
y, luego, seleccione Instalar a la derecha para instalar el paquete.
Visual Studio agrega el paquete NuGet en el nodo Dependencias del Explorador de soluciones, que habilita la compilación de TypeScript en el proyecto.
Configuración del servidor
En esta sección, configurará la aplicación web ASP.NET Core para enviar y recibir mensajes SignalR.
En
Program.cs
, llame a AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();
De nuevo, en
Program.cs
, llame a UseDefaultFiles y UseStaticFiles:var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles();
El código anterior permite que el servidor busque y entregue el archivo
index.html
. El archivo se entrega si el usuario escribe su dirección URL completa o la dirección URL raíz de la aplicación web.Cree un directorio denominado
Hubs
en la raíz del proyecto,SignalRWebpack/
, para la clase de centro SignalR.Cree un archivo.
Hubs/ChatHub.cs
, con el código siguiente: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); }
El código anterior difunde los mensajes recibidos a todos los usuarios conectados, una vez que el servidor los recibe. No es necesario tener un método
on
genérico para recibir todos los mensajes. Basta con un método que tenga el nombre del mensaje.En este ejemplo:
- El cliente de TypeScript envía un mensaje que se identifica como
newMessage
. - El método
NewMessage
de C# espera los datos enviados por el cliente. - Se realiza una llamada a SendAsync en Clients.All.
- Los mensajes recibidos se envían a todos los clientes conectados al concentrador.
- El cliente de TypeScript envía un mensaje que se identifica como
Agregue la instrucción
using
siguiente en la parte superior deProgram.cs
para resolver la referencia aChatHub
:using SignalRWebpack.Hubs;
En
Program.cs
, asigne la ruta/hub
al centroChatHub
. Reemplace el código que muestraHello World!
por el siguiente:app.MapHub<ChatHub>("/hub");
Configurar el cliente
En esta sección, creará un proyecto de Node.js para convertir TypeScript en JavaScript y agrupar recursos del lado cliente, incluidos HTML y CSS, mediante Webpack.
Ejecute el comando siguiente en la raíz del proyecto para crear un archivo
package.json
:npm init -y
Agregue la propiedad resaltada al archivo
package.json
y guarde los cambios efectuados en el archivo:{ "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" }
Si establece la propiedad
private
entrue
, evitará las advertencias de la instalación de paquetes en el paso siguiente.Instale los paquetes npm necesarios. Ejecute el comando siguiente desde la raíz del proyecto:
npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
La opción
-E
deshabilita el comportamiento predeterminado de npm de escribir operadores de intervalo de control de versiones semántico enpackage.json
. Por ejemplo, se usa"webpack": "5.76.1"
en lugar de"webpack": "^5.76.1"
. Esta opción impide actualizaciones no deseadas a versiones más recientes del paquete.Para más información, vea la documentación de npm-install.
Reemplace la propiedad
scripts
del archivopackage.json
por el código siguiente:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },
Se definen los siguientes scripts:
build
: agrupa los recursos del lado cliente en modo de desarrollo y supervisa los cambios del archivo. El monitor de archivos hace que la agrupación se vuelva a generar cada vez que cambia un archivo del proyecto. La opciónmode
deshabilita las optimizaciones de producción, como la agitación del árbol y la minificación. Usebuild
únicamente durante el desarrollo.release
: agrupa los recursos del lado cliente en modo de producción.publish
: ejecuta el scriptrelease
para agrupar los recursos del lado cliente en modo de producción. Llama al comando publish de la CLI de .NET para publicar la aplicación.
Cree un archivo denominado
webpack.config.js
, en la raíz del proyecto, con el código siguiente: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", }), ], };
El archivo anterior configura el proceso de compilación de Webpack:
- La propiedad
output
invalida el valor predeterminado dedist
. En su lugar, la agrupación se genera en el directoriowwwroot
. - La matriz
resolve.extensions
incluye.js
para importar el código JavaScript del cliente de SignalR.
- La propiedad
Cree un directorio denominado
src
en la raíz del proyecto,SignalRWebpack/
, para el código de cliente.Copie el directorio
src
y sus contenidos del proyecto de ejemplo en la raíz del proyecto. El directoriosrc
contiene los siguientes archivos:index.html
, que define el marcado reutilizable de la página principal:<!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 proporciona estilos CSS para la página principal:*, *::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 el compilador de TypeScript para generar JavaScript compatible con 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 = "")); }
En el código anterior se recuperan las referencias a elementos DOM y se adjuntan dos controladores de eventos:
keyup
: se desencadena cuando el usuario escribe en el cuadro de textotbMessage
y llama a la funciónsend
cuando el usuario presiona la tecla Entrar.click
: se desencadena cuando el usuario selecciona el botón Enviar y se llama a la funciónsend
.
La clase
HubConnectionBuilder
crea un generador para configurar la conexión al servidor. La funciónwithUrl
configura la dirección URL del concentrador.SignalR permite el intercambio de mensajes entre un cliente y un servidor. Cada mensaje tiene un nombre específico. Por ejemplo, puede haber mensajes con el nombre
messageReceived
que pueden ejecutar la lógica responsable de mostrar el nuevo mensaje en la zona de mensajes. La escucha a un mensaje concreto se puede realizar mediante la funciónon
. Se puede escuchar cualquier número de nombres de mensaje. También se pueden pasar parámetros al mensaje, como el nombre del autor y el contenido del mensaje recibido. Una vez que el cliente recibe un mensaje, se crea un elementodiv
con el nombre del autor y el contenido del mensaje en su atributoinnerHTML
. Se agrega al elementodiv
principal que muestra los mensajes.El envío de mensajes a través de la conexión de WebSockets requiere llamar al método
send
. El primer parámetro del método es el nombre del mensaje. Los datos del mensaje se encuentran en los otros parámetros. En este ejemplo, se envía al servidor un mensaje identificado comonewMessage
. El mensaje está formado por el nombre de usuario y la entrada del usuario desde un cuadro de texto. Si el envío funciona, se borra el valor del cuadro de texto.
Ejecute el comando siguiente en la raíz del proyecto:
npm i @microsoft/signalr @types/node
El comando anterior instala lo siguiente:
- El cliente TypeScript de SignalR, que permite al cliente enviar mensajes al servidor.
- Las definiciones de tipo de TypeScript para Node.js, que habilita la comprobación en tiempo de compilación de los tipos de Node.js.
Prueba de la aplicación
Confirme que la aplicación funciona con los pasos siguientes:
Ejecute Webpack en modo
release
. Desde la ventana Consola del Administrador de paquetes, ejecute el comando siguiente en la raíz del proyecto.npm run release
Este comando genera la entrega de los recursos del lado cliente al ejecutar la aplicación. Los recursos se colocan en la carpeta
wwwroot
.Webpack ha completado las tareas siguientes:
- Purgar el contenido del directorio
wwwroot
. - Convertir el lenguaje TypeScript en JavaScript, proceso conocido como transpilación.
- Alterar el código JavaScript generado para reducir el tamaño del archivo, proceso conocido como minificación.
- Copiar los archivos JavaScript, CSS y HTML procesados desde
src
en el directoriowwwroot
. - Se han insertado los elementos siguientes en el archivo
wwwroot/index.html
:- Una etiqueta
<link>
, que hace referencia al archivowwwroot/main.<hash>.css
. Esta etiqueta se coloca inmediatamente antes de la etiqueta</head>
de cierre. - Una etiqueta
<script>
, que hace referencia al archivowwwroot/main.<hash>.js
minificado. Esta etiqueta se coloca inmediatamente después de la etiqueta</title>
de cierre.
- Una etiqueta
- Purgar el contenido del directorio
Seleccione Depurar>Iniciar sin depurar para iniciar la aplicación en un explorador sin adjuntar el depurador. El archivo
wwwroot/index.html
se entrega enhttps://localhost:<port>
.Si hay errores de compilación, intente cerrar y volver a abrir la solución.
Abra otra instancia del explorador (cualquier explorador) y pegue la dirección URL en la barra de direcciones.
Elija un explorador, escriba algo en el cuadro de texto Mensaje y seleccione el botón Enviar. El nombre de usuario único y el mensaje se muestran en las dos páginas al instante.
Pasos siguientes
- Concentradores fuertemente tipados
- Autenticación y autorización en ASP.NET Core SignalR
- Protocolo de concentrador MessagePack de SignalR para ASP.NET Core
Recursos adicionales
En este tutorial se describe el uso de Webpack en una aplicación web ASP.NET Core SignalR para unir y crear un cliente escrito en TypeScript. Webpack permite a los desarrolladores agrupar y compilar los recursos del lado cliente de una aplicación web.
En este tutorial aprenderá a:
- Crear una aplicación SignalR de ASP.NET Core
- Configurar el servidor SignalR
- Configuración de una canalización de compilación mediante Webpack
- Configurar el cliente de TypeScript para SignalR
- Habilitar la comunicación entre el cliente y el servidor
Vea o descargue el código de ejemplo (cómo descargarlo)
Requisitos previos
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
Creación de la aplicación web ASP.NET Core
De forma predeterminada, Visual Studio usa la versión de npm que se encuentra en su directorio de instalación. Para configurar Visual Studio a fin de buscar npm en la variable de entorno PATH
:
Inicie Visual Studio. En la ventana de inicio, seleccione Continuar sin código.
Vaya a Herramientas>Opciones>Proyectos y soluciones>Administración de paquetes web>Herramientas web externas.
Seleccione la entrada
$(PATH)
en la lista. Seleccione la flecha arriba para mover la entrada a la segunda posición de la lista y seleccione Aceptar:.
Para crear una aplicación web de ASP.NET Core:
- Use la opción de menú Archivo>Nuevo>Proyecto y seleccione la plantilla ASP.NET Core vacío. Seleccione Next (Siguiente).
- Asigne el nombre
SignalRWebpack
al proyecto y seleccione Crear. - Seleccione
.NET 7.0 (Standard Term Support)
en la lista desplegable Marco. Seleccione Crear.
Agregue el paquete NuGet Microsoft.TypeScript.MSBuild al proyecto:
- En el Explorador de soluciones, haga clic con el botón derecho en el nodo del proyecto y seleccione Administrar paquetes NuGet. En la pestaña Examinar, busque
Microsoft.TypeScript.MSBuild
y, luego, seleccione Instalar a la derecha para instalar el paquete.
Visual Studio agrega el paquete NuGet en el nodo Dependencias del Explorador de soluciones, que habilita la compilación de TypeScript en el proyecto.
Configuración del servidor
En esta sección, configurará la aplicación web ASP.NET Core para enviar y recibir mensajes SignalR.
En
Program.cs
, llame a AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();
De nuevo, en
Program.cs
, llame a UseDefaultFiles y UseStaticFiles:var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles();
El código anterior permite que el servidor busque y entregue el archivo
index.html
. El archivo se entrega si el usuario escribe su dirección URL completa o la dirección URL raíz de la aplicación web.Cree un directorio denominado
Hubs
en la raíz del proyecto,SignalRWebpack/
, para la clase de centro SignalR.Cree un archivo.
Hubs/ChatHub.cs
, con el código siguiente: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); }
El código anterior difunde los mensajes recibidos a todos los usuarios conectados, una vez que el servidor los recibe. No es necesario tener un método
on
genérico para recibir todos los mensajes. Basta con un método que tenga el nombre del mensaje.En este ejemplo:
- El cliente de TypeScript envía un mensaje que se identifica como
newMessage
. - El método
NewMessage
de C# espera los datos enviados por el cliente. - Se realiza una llamada a SendAsync en Clients.All.
- Los mensajes recibidos se envían a todos los clientes conectados al concentrador.
- El cliente de TypeScript envía un mensaje que se identifica como
Agregue la instrucción
using
siguiente en la parte superior deProgram.cs
para resolver la referencia aChatHub
:using SignalRWebpack.Hubs;
En
Program.cs
, asigne la ruta/hub
al centroChatHub
. Reemplace el código que muestraHello World!
por el siguiente:app.MapHub<ChatHub>("/hub");
Configurar el cliente
En esta sección, creará un proyecto de Node.js para convertir TypeScript en JavaScript y agrupar recursos del lado cliente, incluidos HTML y CSS, mediante Webpack.
Ejecute el comando siguiente en la raíz del proyecto para crear un archivo
package.json
:npm init -y
Agregue la propiedad resaltada al archivo
package.json
y guarde los cambios efectuados en el archivo:{ "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" }
Si establece la propiedad
private
entrue
, evitará las advertencias de la instalación de paquetes en el paso siguiente.Instale los paquetes npm necesarios. Ejecute el comando siguiente desde la raíz del proyecto:
npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
La opción
-E
deshabilita el comportamiento predeterminado de npm de escribir operadores de intervalo de control de versiones semántico enpackage.json
. Por ejemplo, se usa"webpack": "5.76.1"
en lugar de"webpack": "^5.76.1"
. Esta opción impide actualizaciones no deseadas a versiones más recientes del paquete.Para más información, vea la documentación de npm-install.
Reemplace la propiedad
scripts
del archivopackage.json
por el código siguiente:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },
Se definen los siguientes scripts:
build
: agrupa los recursos del lado cliente en modo de desarrollo y supervisa los cambios del archivo. El monitor de archivos hace que la agrupación se vuelva a generar cada vez que cambia un archivo del proyecto. La opciónmode
deshabilita las optimizaciones de producción, como la agitación del árbol y la minificación. Usebuild
únicamente durante el desarrollo.release
: agrupa los recursos del lado cliente en modo de producción.publish
: ejecuta el scriptrelease
para agrupar los recursos del lado cliente en modo de producción. Llama al comando publish de la CLI de .NET para publicar la aplicación.
Cree un archivo denominado
webpack.config.js
, en la raíz del proyecto, con el código siguiente: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", }), ], };
El archivo anterior configura el proceso de compilación de Webpack:
- La propiedad
output
invalida el valor predeterminado dedist
. En su lugar, la agrupación se genera en el directoriowwwroot
. - La matriz
resolve.extensions
incluye.js
para importar el código JavaScript del cliente de SignalR.
- La propiedad
Copie el directorio
src
y sus contenidos del proyecto de ejemplo en la raíz del proyecto. El directoriosrc
contiene los siguientes archivos:index.html
, que define el marcado reutilizable de la página principal:<!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 proporciona estilos CSS para la página principal:*, *::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 el compilador de TypeScript para generar JavaScript compatible con 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 = "")); }
En el código anterior se recuperan las referencias a elementos DOM y se adjuntan dos controladores de eventos:
keyup
: se desencadena cuando el usuario escribe en el cuadro de textotbMessage
y llama a la funciónsend
cuando el usuario presiona la tecla Entrar.click
: se desencadena cuando el usuario selecciona el botón Enviar y se llama a la funciónsend
.
La clase
HubConnectionBuilder
crea un generador para configurar la conexión al servidor. La funciónwithUrl
configura la dirección URL del concentrador.SignalR permite el intercambio de mensajes entre un cliente y un servidor. Cada mensaje tiene un nombre específico. Por ejemplo, puede haber mensajes con el nombre
messageReceived
que pueden ejecutar la lógica responsable de mostrar el nuevo mensaje en la zona de mensajes. La escucha a un mensaje concreto se puede realizar mediante la funciónon
. Se puede escuchar cualquier número de nombres de mensaje. También se pueden pasar parámetros al mensaje, como el nombre del autor y el contenido del mensaje recibido. Una vez que el cliente recibe un mensaje, se crea un elementodiv
con el nombre del autor y el contenido del mensaje en su atributoinnerHTML
. Se agrega al elementodiv
principal que muestra los mensajes.El envío de mensajes a través de la conexión de WebSockets requiere llamar al método
send
. El primer parámetro del método es el nombre del mensaje. Los datos del mensaje se encuentran en los otros parámetros. En este ejemplo, se envía al servidor un mensaje identificado comonewMessage
. El mensaje está formado por el nombre de usuario y la entrada del usuario desde un cuadro de texto. Si el envío funciona, se borra el valor del cuadro de texto.
Ejecute el comando siguiente en la raíz del proyecto:
npm i @microsoft/signalr @types/node
El comando anterior instala lo siguiente:
- El cliente TypeScript de SignalR, que permite al cliente enviar mensajes al servidor.
- Las definiciones de tipo de TypeScript para Node.js, que habilita la comprobación en tiempo de compilación de los tipos de Node.js.
Prueba de la aplicación
Confirme que la aplicación funciona con los pasos siguientes:
Ejecute Webpack en modo
release
. Desde la ventana Consola del Administrador de paquetes, ejecute el comando siguiente en la raíz del proyecto.npm run release
Este comando genera la entrega de los recursos del lado cliente al ejecutar la aplicación. Los recursos se colocan en la carpeta
wwwroot
.Webpack ha completado las tareas siguientes:
- Purgar el contenido del directorio
wwwroot
. - Convertir el lenguaje TypeScript en JavaScript, proceso conocido como transpilación.
- Alterar el código JavaScript generado para reducir el tamaño del archivo, proceso conocido como minificación.
- Copiar los archivos JavaScript, CSS y HTML procesados desde
src
en el directoriowwwroot
. - Se han insertado los elementos siguientes en el archivo
wwwroot/index.html
:- Una etiqueta
<link>
, que hace referencia al archivowwwroot/main.<hash>.css
. Esta etiqueta se coloca inmediatamente antes de la etiqueta</head>
de cierre. - Una etiqueta
<script>
, que hace referencia al archivowwwroot/main.<hash>.js
minificado. Esta etiqueta se coloca inmediatamente después de la etiqueta</title>
de cierre.
- Una etiqueta
- Purgar el contenido del directorio
Seleccione Depurar>Iniciar sin depurar para iniciar la aplicación en un explorador sin adjuntar el depurador. El archivo
wwwroot/index.html
se entrega enhttps://localhost:<port>
.Si hay errores de compilación, intente cerrar y volver a abrir la solución.
Abra otra instancia del explorador (cualquier explorador) y pegue la dirección URL en la barra de direcciones.
Elija un explorador, escriba algo en el cuadro de texto Mensaje y seleccione el botón Enviar. El nombre de usuario único y el mensaje se muestran en las dos páginas al instante.
Pasos siguientes
- Concentradores fuertemente tipados
- Autenticación y autorización en ASP.NET Core SignalR
- Protocolo de concentrador MessagePack de SignalR para ASP.NET Core
Recursos adicionales
En este tutorial se describe el uso de Webpack en una aplicación web ASP.NET Core SignalR para unir y crear un cliente escrito en TypeScript. Webpack permite a los desarrolladores agrupar y compilar los recursos del lado cliente de una aplicación web.
En este tutorial aprenderá a:
- Crear una aplicación SignalR de ASP.NET Core
- Configurar el servidor SignalR
- Configuración de una canalización de compilación mediante Webpack
- Configurar el cliente de TypeScript para SignalR
- Habilitar la comunicación entre el cliente y el servidor
Vea o descargue el código de ejemplo (cómo descargarlo)
Requisitos previos
- Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
- SDK de .NET 6.0
Creación de la aplicación web ASP.NET Core
De forma predeterminada, Visual Studio usa la versión de npm que se encuentra en su directorio de instalación. Para configurar Visual Studio a fin de buscar npm en la variable de entorno PATH
:
Inicie Visual Studio. En la ventana de inicio, seleccione Continuar sin código.
Vaya a Herramientas>Opciones>Proyectos y soluciones>Administración de paquetes web>Herramientas web externas.
Seleccione la entrada
$(PATH)
en la lista. Seleccione la flecha arriba para mover la entrada a la segunda posición de la lista y seleccione Aceptar:.
Para crear una aplicación web de ASP.NET Core:
- Use la opción de menú Archivo>Nuevo>Proyecto y seleccione la plantilla ASP.NET Core vacío. Seleccione Next (Siguiente).
- Asigne el nombre
SignalRWebpack
al proyecto y seleccione Crear. - Seleccione
.NET 6.0 (Long Term Support)
en la lista desplegable Marco. Seleccione Crear.
Agregue el paquete NuGet Microsoft.TypeScript.MSBuild al proyecto:
- En el Explorador de soluciones, haga clic con el botón derecho en el nodo del proyecto y seleccione Administrar paquetes NuGet. En la pestaña Examinar, busque
Microsoft.TypeScript.MSBuild
y, luego, seleccione Instalar a la derecha para instalar el paquete.
Visual Studio agrega el paquete NuGet en el nodo Dependencias del Explorador de soluciones, que habilita la compilación de TypeScript en el proyecto.
Configuración del servidor
En esta sección, configurará la aplicación web ASP.NET Core para enviar y recibir mensajes SignalR.
En
Program.cs
, llame a AddSignalR:var builder = WebApplication.CreateBuilder(args); builder.Services.AddSignalR();
De nuevo, en
Program.cs
, llame a UseDefaultFiles y UseStaticFiles:var app = builder.Build(); app.UseDefaultFiles(); app.UseStaticFiles();
El código anterior permite que el servidor busque y entregue el archivo
index.html
. El archivo se entrega si el usuario escribe su dirección URL completa o la dirección URL raíz de la aplicación web.Cree un directorio denominado
Hubs
en la raíz del proyecto,SignalRWebpack/
, para la clase de centro SignalR.Cree un archivo.
Hubs/ChatHub.cs
, con el código siguiente: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); }
El código anterior difunde los mensajes recibidos a todos los usuarios conectados, una vez que el servidor los recibe. No es necesario tener un método
on
genérico para recibir todos los mensajes. Basta con un método que tenga el nombre del mensaje.En este ejemplo, el cliente de TypeScript envía un mensaje que se identifica como
newMessage
. El métodoNewMessage
de C# espera los datos enviados por el cliente. Se realiza una llamada a SendAsync en Clients.All. Los mensajes recibidos se envían a todos los clientes conectados al concentrador.Agregue la instrucción
using
siguiente en la parte superior deProgram.cs
para resolver la referencia aChatHub
:using SignalRWebpack.Hubs;
En
Program.cs
, asigne la ruta/hub
al centroChatHub
. Reemplace el código que muestraHello World!
por el siguiente:app.MapHub<ChatHub>("/hub");
Configurar el cliente
En esta sección, creará un proyecto de Node.js para convertir TypeScript en JavaScript y agrupar recursos del lado cliente, incluidos HTML y CSS, mediante Webpack.
Ejecute el comando siguiente en la raíz del proyecto para crear un archivo
package.json
:npm init -y
Agregue la propiedad resaltada al archivo
package.json
y guarde los cambios efectuados en el archivo:{ "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" }
Si establece la propiedad
private
entrue
, evitará las advertencias de la instalación de paquetes en el paso siguiente.Instale los paquetes npm necesarios. Ejecute el comando siguiente desde la raíz del proyecto:
npm i -D -E clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin ts-loader typescript webpack webpack-cli
La opción
-E
deshabilita el comportamiento predeterminado de npm de escribir operadores de intervalo de control de versiones semántico enpackage.json
. Por ejemplo, se usa"webpack": "5.70.0"
en lugar de"webpack": "^5.70.0"
. Esta opción impide actualizaciones no deseadas a versiones más recientes del paquete.Para más información, vea la documentación de npm-install.
Reemplace la propiedad
scripts
del archivopackage.json
por el código siguiente:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },
Se definen los siguientes scripts:
build
: agrupa los recursos del lado cliente en modo de desarrollo y supervisa los cambios del archivo. El monitor de archivos hace que la agrupación se vuelva a generar cada vez que cambia un archivo del proyecto. La opciónmode
deshabilita las optimizaciones de producción, como la agitación del árbol y la minificación. Usebuild
únicamente durante el desarrollo.release
: agrupa los recursos del lado cliente en modo de producción.publish
: ejecuta el scriptrelease
para agrupar los recursos del lado cliente en modo de producción. Llama al comando publish de la CLI de .NET para publicar la aplicación.
Cree un archivo denominado
webpack.config.js
, en la raíz del proyecto, con el código siguiente: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", }), ], };
El archivo anterior configura el proceso de compilación de Webpack:
- La propiedad
output
invalida el valor predeterminado dedist
. En su lugar, la agrupación se genera en el directoriowwwroot
. - La matriz
resolve.extensions
incluye.js
para importar el código JavaScript del cliente de SignalR.
- La propiedad
Copie el directorio
src
y sus contenidos del proyecto de ejemplo en la raíz del proyecto. El directoriosrc
contiene los siguientes archivos:index.html
, que define el marcado reutilizable de la página principal:<!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 proporciona estilos CSS para la página principal:*, *::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 el compilador de TypeScript para generar JavaScript compatible con 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 = "")); }
En el código anterior se recuperan las referencias a elementos DOM y se adjuntan dos controladores de eventos:
keyup
: se desencadena cuando el usuario escribe en el cuadro de textotbMessage
y llama a la funciónsend
cuando el usuario presiona la tecla Entrar.click
: se desencadena cuando el usuario selecciona el botón Enviar y se llama a la funciónsend
.
La clase
HubConnectionBuilder
crea un generador para configurar la conexión al servidor. La funciónwithUrl
configura la dirección URL del concentrador.SignalR permite el intercambio de mensajes entre un cliente y un servidor. Cada mensaje tiene un nombre específico. Por ejemplo, puede haber mensajes con el nombre
messageReceived
que pueden ejecutar la lógica responsable de mostrar el nuevo mensaje en la zona de mensajes. La escucha a un mensaje concreto se puede realizar mediante la funciónon
. Se puede escuchar cualquier número de nombres de mensaje. También se pueden pasar parámetros al mensaje, como el nombre del autor y el contenido del mensaje recibido. Una vez que el cliente recibe un mensaje, se crea un elementodiv
con el nombre del autor y el contenido del mensaje en su atributoinnerHTML
. Se agrega al elementodiv
principal que muestra los mensajes.El envío de mensajes a través de la conexión de WebSockets requiere llamar al método
send
. El primer parámetro del método es el nombre del mensaje. Los datos del mensaje se encuentran en los otros parámetros. En este ejemplo, se envía al servidor un mensaje identificado comonewMessage
. El mensaje está formado por el nombre de usuario y la entrada del usuario desde un cuadro de texto. Si el envío funciona, se borra el valor del cuadro de texto.Ejecute el comando siguiente en la raíz del proyecto:
npm i @microsoft/signalr @types/node
El comando anterior instala lo siguiente:
- El cliente TypeScript de SignalR, que permite al cliente enviar mensajes al servidor.
- Las definiciones de tipo de TypeScript para Node.js, que habilita la comprobación en tiempo de compilación de los tipos de Node.js.
Prueba de la aplicación
Confirme que la aplicación funciona con los pasos siguientes:
Ejecute Webpack en modo
release
. Desde la ventana Consola del Administrador de paquetes, ejecute el comando siguiente en la raíz del proyecto. Si no está en la raíz del proyecto, escribacd SignalRWebpack
antes de introducir el comando.npm run release
Este comando genera la entrega de los recursos del lado cliente al ejecutar la aplicación. Los recursos se colocan en la carpeta
wwwroot
.Webpack ha completado las tareas siguientes:
- Purgar el contenido del directorio
wwwroot
. - Convertir el lenguaje TypeScript en JavaScript, proceso conocido como transpilación.
- Alterar el código JavaScript generado para reducir el tamaño del archivo, proceso conocido como minificación.
- Copiar los archivos JavaScript, CSS y HTML procesados desde
src
en el directoriowwwroot
. - Se han insertado los elementos siguientes en el archivo
wwwroot/index.html
:- Una etiqueta
<link>
, que hace referencia al archivowwwroot/main.<hash>.css
. Esta etiqueta se coloca inmediatamente antes de la etiqueta</head>
de cierre. - Una etiqueta
<script>
, que hace referencia al archivowwwroot/main.<hash>.js
minificado. Esta etiqueta se coloca inmediatamente después de la etiqueta</title>
de cierre.
- Una etiqueta
- Purgar el contenido del directorio
Seleccione Depurar>Iniciar sin depurar para iniciar la aplicación en un explorador sin adjuntar el depurador. El archivo
wwwroot/index.html
se entrega enhttps://localhost:<port>
.Si obtiene errores de compilación, intente cerrar y volver a abrir la solución.
Abra otra instancia del explorador (cualquier explorador) y pegue la dirección URL en la barra de direcciones.
Elija un explorador, escriba algo en el cuadro de texto Mensaje y seleccione el botón Enviar. El nombre de usuario único y el mensaje se muestran en las dos páginas al instante.
Pasos siguientes
- Concentradores fuertemente tipados
- Autenticación y autorización en ASP.NET Core SignalR
- Protocolo de concentrador MessagePack de SignalR para ASP.NET Core
Recursos adicionales
En este tutorial se describe el uso de Webpack en una aplicación web ASP.NET Core SignalR para unir y crear un cliente escrito en TypeScript. Webpack permite a los desarrolladores agrupar y compilar los recursos del lado cliente de una aplicación web.
En este tutorial aprenderá a:
- Agregar scaffold a una aplicación ASP.NET Core SignalR de inicio
- Configurar el cliente de TypeScript para SignalR
- Configuración de una canalización de compilación mediante Webpack
- Configurar el servidor SignalR
- Habilitar la comunicación entre cliente y servidor
Vea o descargue el código de ejemplo (cómo descargarlo)
Requisitos previos
- Visual Studio 2019 con la carga de trabajo ASP.NET y desarrollo web
- .NET Core SDK 3.0 o posterior
- Node.js con npm
Creación de la aplicación web ASP.NET Core
Configure Visual Studio para buscar npm en la variable de entorno PATH. De forma predeterminada, Visual Studio usa la versión de npm que se encuentra en su directorio de instalación. Siga estas instrucciones en Visual Studio:
Inicie Visual Studio. En la ventana de inicio, seleccione Continuar sin código.
Vaya a Herramientas>Opciones>Proyectos y soluciones>Administración de paquetes web>Herramientas web externas.
Seleccione la entrada $(PATH) en la lista. Haga clic en la flecha arriba para mover la entrada a la segunda posición de la lista y seleccione Aceptar.
.
Se ha completado la configuración de Visual Studio.
- Use la opción de menú Archivo>Nuevo>Proyecto y seleccione la plantilla Aplicación web ASP.NET Core. Seleccione Next (Siguiente).
- Asigne el nombre *SignalRWebPac`` al proyecto y seleccione Crear.
- Seleccione .NET Core en la lista desplegable de plataformas de destino y ASP.NET Core 3.1 en la lista desplegable del selector de plataformas. Seleccione la plantilla Vacía y Crear.
Agregue el paquete Microsoft.TypeScript.MSBuild
al proyecto:
- En el Explorador de soluciones (panel derecho), haga clic con el botón derecho en el nodo del proyecto y seleccione Administrar paquetes NuGet. En la pestaña Examinar, busque
Microsoft.TypeScript.MSBuild
y luego haga clic en Instalar a la derecha para instalar el paquete.
Visual Studio agrega el paquete NuGet en el nodo Dependencias del Explorador de soluciones, que habilita la compilación de TypeScript en el proyecto.
Configuración de Webpack y TypeScript
Los pasos siguientes permiten configurar la conversión de TypeScript a JavaScript y la agrupación de los recursos del lado cliente.
Ejecute el comando siguiente en la raíz del proyecto para crear un archivo
package.json
:npm init -y
Agregue la propiedad resaltada al archivo
package.json
y guarde los cambios efectuados en el archivo:{ "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" }
Si establece la propiedad
private
entrue
, evitará las advertencias de la instalación de paquetes en el paso siguiente.Instale los paquetes npm necesarios. Ejecute el comando siguiente desde la raíz del proyecto:
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
Algunos detalles del comando para tener en cuenta:
- En cada nombre de paquete, un número de versión sigue al signo
@
. npm instala esas versiones de paquete específicas. - La opción
-E
deshabilita el comportamiento predeterminado de npm de escribir operadores de intervalo de control de versiones semántico en *packagejson
. Por ejemplo, se usa"webpack": "4.41.5"
en lugar de"webpack": "^4.41.5"
. Esta opción impide actualizaciones no deseadas a versiones más recientes del paquete.
Consulte la documentación de npm-install para obtener más detalles.
- En cada nombre de paquete, un número de versión sigue al signo
Reemplace la propiedad
scripts
del archivopackage.json
por el código siguiente:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },
Más detalles sobre los scripts:
build
: agrupa los recursos del lado cliente en modo de desarrollo y supervisa los cambios del archivo. El monitor de archivos hace que la agrupación se vuelva a generar cada vez que cambia un archivo del proyecto. La opciónmode
deshabilita las optimizaciones de producción, como la agitación del árbol y la minificación. Usebuild
únicamente durante el desarrollo.release
: agrupa los recursos del lado cliente en modo de producción.publish
: ejecuta el scriptrelease
para agrupar los recursos del lado cliente en modo de producción. Llama al comando publish de la CLI de .NET para publicar la aplicación.
Cree un archivo denominado
webpack.config.js
, en la raíz del proyecto, con el código siguiente: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" }) ] };
El archivo anterior configura la compilación de Webpack. Algunos detalles de configuración para tener en cuenta:
- La propiedad
output
invalida el valor predeterminado dedist
. En su lugar, la agrupación se genera en el directoriowwwroot
. - La matriz
resolve.extensions
incluye.js
para importar el código JavaScript del cliente de SignalR.
- La propiedad
Cree un src directorio en la raíz del proyecto para almacenar los recursos del lado cliente del proyecto.
Cree
src/index.html
con el marcado siguiente.<!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>
El código HTML anterior define el marcado reutilizable de la página principal.
Cree un directorio src/css. Su objetivo es almacenar los archivos
.css
del proyecto.Cree
src/css/main.css
con la especificación CSS siguiente:*, *::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; }
El archivo
main.css
anterior aplica estilo a la aplicación.Cree
src/tsconfig.json
con el siguiente JSON:{ "compilerOptions": { "target": "es5" } }
El código anterior configura el compilador de TypeScript para generar JavaScript compatible con ECMAScript 5.
Cree el archivo
src/index.ts
con el siguiente 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() { }
El elemento TypeScript anterior recupera las referencias a elementos DOM y adjunta dos controladores de eventos:
keyup
: este evento se desencadena cuando el usuario escribe en el cuadro de textotbMessage
. La funciónsend
se llama cuando el usuario presiona la tecla Entrar.click
: este evento se desencadena cuando el usuario selecciona el botón Enviar. Se llama a la funciónsend
.
Configuración de la aplicación
En
Startup.Configure
, agregue llamadas a UseDefaultFiles(IApplicationBuilder) y 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"); }); }
El código anterior permite que el servidor busque y entregue el archivo
index.html
. El archivo se entrega si el usuario escribe su dirección URL completa o la dirección URL raíz de la aplicación web.Al final de
Startup.Configure
, asigne una ruta /hub al centroChatHub
. Reemplace el código que muestra Hola mundo por la línea siguiente:app.UseEndpoints(endpoints => { endpoints.MapHub<ChatHub>("/hub"); });
En
Startup.ConfigureServices
, llame a AddSignalR.services.AddSignalR();
Cree un directorio denominado Hubs en la raíz del proyecto SignalRWebPack/ para almacenar el centro SignalR.
Cree el concentrador
Hubs/ChatHub.cs
con el código siguiente:using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRWebPack.Hubs { public class ChatHub : Hub { } }
Agregue la instrucción
using
siguiente en la parte superior del archivoStartup.cs
para resolver la referencia aChatHub
:using SignalRWebPack.Hubs;
Habilitar la comunicación entre cliente y servidor
La aplicación actualmente muestra un formulario básico para enviar mensajes, pero aún no es funcional. El servidor está escuchando en una ruta específica, pero no hace nada con los mensajes enviados.
Ejecute el comando siguiente en la raíz del proyecto:
npm i @microsoft/signalr @types/node
El comando anterior instala lo siguiente:
- El cliente TypeScript de SignalR, que permite al cliente enviar mensajes al servidor.
- Las definiciones de tipo de TypeScript para Node.js, que habilita la comprobación en tiempo de compilación de los tipos de Node.js.
Agregue el código resaltado al archivo
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() { }
El código anterior admite la recepción de mensajes desde el servidor. La clase
HubConnectionBuilder
crea un generador para configurar la conexión al servidor. La funciónwithUrl
configura la dirección URL del concentrador.SignalR permite el intercambio de mensajes entre un cliente y un servidor. Cada mensaje tiene un nombre específico. Por ejemplo, puede haber mensajes con el nombre
messageReceived
que pueden ejecutar la lógica responsable de mostrar el nuevo mensaje en la zona de mensajes. La escucha a un mensaje concreto se puede realizar mediante la funciónon
. Se puede escuchar cualquier número de nombres de mensaje. También se pueden pasar parámetros al mensaje, como el nombre del autor y el contenido del mensaje recibido. Una vez que el cliente recibe un mensaje, se crea un elementodiv
con el nombre del autor y el contenido del mensaje en su atributoinnerHTML
. Se agrega al elementodiv
principal que muestra los mensajes.Ahora que el cliente puede recibir mensajes, debe configurarlo para poder enviarlos. Agregue el código resaltado al archivo
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 = ""); }
El envío de mensajes a través de la conexión de WebSockets requiere llamar al método
send
. El primer parámetro del método es el nombre del mensaje. Los datos del mensaje se encuentran en los otros parámetros. En este ejemplo, se envía al servidor un mensaje identificado comonewMessage
. El mensaje está formado por el nombre de usuario y la entrada del usuario desde un cuadro de texto. Si el envío funciona, se borra el valor del cuadro de texto.Agregue el método
NewMessage
a la claseChatHub
: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); } } }
El código anterior difunde los mensajes recibidos a todos los usuarios conectados, una vez que el servidor los recibe. No es necesario tener un método
on
genérico para recibir todos los mensajes. Basta con un método que tenga el nombre del mensaje.En este ejemplo, el cliente de TypeScript envía un mensaje que se identifica como
newMessage
. El métodoNewMessage
de C# espera los datos enviados por el cliente. Se realiza una llamada a SendAsync en Clients.All. Los mensajes recibidos se envían a todos los clientes conectados al concentrador.
Prueba de la aplicación
Confirme que la aplicación funciona con los pasos siguientes.
Ejecute Webpack en modo release. Desde la ventana Consola del Administrador de paquetes, ejecute el comando siguiente en la raíz del proyecto. Si no está en la raíz del proyecto, escriba
cd SignalRWebPack
antes de introducir el comando.npm run release
Este comando genera la entrega de los recursos del lado cliente al ejecutar la aplicación. Los recursos se colocan en la carpeta
wwwroot
.Webpack ha completado las tareas siguientes:
- Purgar el contenido del directorio
wwwroot
. - Convertir el lenguaje TypeScript en JavaScript, proceso conocido como transpilación.
- Alterar el código JavaScript generado para reducir el tamaño del archivo, proceso conocido como minificación.
- Copiar los archivos JavaScript, CSS y HTML procesados desde
src
en el directoriowwwroot
. - Se han insertado los elementos siguientes en el archivo
wwwroot/index.html
:- Una etiqueta
<link>
, que hace referencia al archivowwwroot/main.<hash>.css
. Esta etiqueta se coloca inmediatamente antes de la etiqueta</head>
de cierre. - Una etiqueta
<script>
, que hace referencia al archivowwwroot/main.<hash>.js
minificado. Esta etiqueta se coloca inmediatamente después de la etiqueta</title>
de cierre.
- Una etiqueta
- Purgar el contenido del directorio
Seleccione Depurar>Iniciar sin depurar para iniciar la aplicación en un explorador sin adjuntar el depurador. El archivo
wwwroot/index.html
se entrega enhttp://localhost:<port_number>
.Si obtiene errores de compilación, intente cerrar y volver a abrir la solución.
Abra otra instancia del explorador (sirve cualquiera). Pegue la dirección URL en la barra de direcciones.
Elija un explorador, escriba algo en el cuadro de texto Mensaje y seleccione el botón Enviar. El nombre de usuario único y el mensaje se muestran en las dos páginas al instante.
Recursos adicionales
En este tutorial se describe el uso de Webpack en una aplicación web ASP.NET Core SignalR para unir y crear un cliente escrito en TypeScript. Webpack permite a los desarrolladores agrupar y compilar los recursos del lado cliente de una aplicación web.
En este tutorial aprenderá a:
- Agregar scaffold a una aplicación ASP.NET Core SignalR de inicio
- Configurar el cliente de TypeScript para SignalR
- Configuración de una canalización de compilación mediante Webpack
- Configurar el servidor SignalR
- Habilitar la comunicación entre cliente y servidor
Vea o descargue el código de ejemplo (cómo descargarlo)
Requisitos previos
- Visual Studio 2019 con la carga de trabajo ASP.NET y desarrollo web
- .NET Core SDK 2.2 o posterior
- Node.js con npm
Creación de la aplicación web ASP.NET Core
Configure Visual Studio para buscar npm en la variable de entorno PATH. De forma predeterminada, Visual Studio usa la versión de npm que se encuentra en su directorio de instalación. Siga estas instrucciones en Visual Studio:
Vaya a Herramientas>Opciones>Proyectos y soluciones>Administración de paquetes web>Herramientas web externas.
Seleccione la entrada $(PATH) en la lista. Seleccione la flecha arriba para mover la entrada a la segunda posición de la lista.
Se ha completado la configuración de Visual Studio. Es el momento de crear el proyecto.
- Use la opción de menú Archivo>Nuevo>Proyecto y seleccione la plantilla Aplicación web ASP.NET Core.
- Asigne el nombre *SignalRWebPack` al proyecto y seleccione Crear.
- Seleccione .NET Core en la lista desplegable de plataforma de destino y ASP.NET Core 2.2 en la lista desplegable del selector de plataforma. Seleccione la plantilla Vacía y Crear.
Configuración de Webpack y TypeScript
Los pasos siguientes permiten configurar la conversión de TypeScript a JavaScript y la agrupación de los recursos del lado cliente.
Ejecute el comando siguiente en la raíz del proyecto para crear un archivo
package.json
:npm init -y
Agregue la propiedad resaltada al archivo
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" }
Si establece la propiedad
private
entrue
, evitará las advertencias de la instalación de paquetes en el paso siguiente.Instale los paquetes npm necesarios. Ejecute el comando siguiente desde la raíz del proyecto:
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
Algunos detalles del comando para tener en cuenta:
- En cada nombre de paquete, un número de versión sigue al signo
@
. npm instala esas versiones de paquete específicas. - La opción
-E
deshabilita el comportamiento predeterminado de npm de escribir operadores de intervalo de control de versiones semántico en *packagejson
. Por ejemplo, se usa"webpack": "4.29.3"
en lugar de"webpack": "^4.29.3"
. Esta opción impide actualizaciones no deseadas a versiones más recientes del paquete.
Consulte la documentación de npm-install para obtener más detalles.
- En cada nombre de paquete, un número de versión sigue al signo
Reemplace la propiedad
scripts
del archivopackage.json
por el código siguiente:"scripts": { "build": "webpack --mode=development --watch", "release": "webpack --mode=production", "publish": "npm run release && dotnet publish -c Release" },
Más detalles sobre los scripts:
build
: agrupa los recursos del lado cliente en modo de desarrollo y supervisa los cambios del archivo. El monitor de archivos hace que la agrupación se vuelva a generar cada vez que cambia un archivo del proyecto. La opciónmode
deshabilita las optimizaciones de producción, como la agitación del árbol y la minificación. Usebuild
únicamente durante el desarrollo.release
: agrupa los recursos del lado cliente en modo de producción.publish
: ejecuta el scriptrelease
para agrupar los recursos del lado cliente en modo de producción. Llama al comando publish de la CLI de .NET para publicar la aplicación.
Cree un archivo denominado
*webpack.config.js
, en la raíz del proyecto, con el código siguiente: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" }) ] };
El archivo anterior configura la compilación de Webpack. Algunos detalles de configuración para tener en cuenta:
- La propiedad
output
invalida el valor predeterminado dedist
. En su lugar, la agrupación se genera en el directoriowwwroot
. - La matriz
resolve.extensions
incluye.js
para importar el código JavaScript del cliente de SignalR.
- La propiedad
Cree un src directorio en la raíz del proyecto para almacenar los recursos del lado cliente del proyecto.
Cree
src/index.html
con el marcado siguiente.<!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>
El código HTML anterior define el marcado reutilizable de la página principal.
Cree un directorio src/css. Su objetivo es almacenar los archivos
.css
del proyecto.Cree
src/css/main.css
con el marcado siguiente:*, *::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; }
El archivo
main.css
anterior aplica estilo a la aplicación.Cree
src/tsconfig.json
con el siguiente JSON:{ "compilerOptions": { "target": "es5" } }
El código anterior configura el compilador de TypeScript para generar JavaScript compatible con ECMAScript 5.
Cree el archivo
src/index.ts
con el siguiente 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() { }
El elemento TypeScript anterior recupera las referencias a elementos DOM y adjunta dos controladores de eventos:
keyup
: este evento se desencadena cuando el usuario escribe en el cuadro de textotbMessage
. La funciónsend
se llama cuando el usuario presiona la tecla Entrar.click
: este evento se desencadena cuando el usuario selecciona el botón Enviar. Se llama a la funciónsend
.
Configuración de la aplicación ASP.NET Core
El código proporcionado en el método
Startup.Configure
muestra Hello World! . Reemplace la llamada de métodoapp.Run
por llamadas a UseDefaultFiles(IApplicationBuilder) y UseStaticFiles(IApplicationBuilder).app.UseDefaultFiles(); app.UseStaticFiles();
El código anterior permite que el servidor busque y proporcione el archivo
index.html
, con independencia de que el usuario escriba su dirección URL completa o la dirección URL raíz de la aplicación web.Llame a AddSignalR en
Startup.ConfigureServices
. Esta acción agrega los servicios SignalR al proyecto.services.AddSignalR();
Asigne una ruta /hub al concentrador
ChatHub
. Agregue la línea siguiente al final deStartup.Configure
:app.UseSignalR(options => { options.MapHub<ChatHub>("/hub"); });
Cree un directorio denominado Hubs en la raíz del proyecto. Su objetivo es almacenar el concentrador de SignalR, que se crea en el paso siguiente.
Cree el concentrador
Hubs/ChatHub.cs
con el código siguiente:using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRWebPack.Hubs { public class ChatHub : Hub { } }
Agregue el código siguiente en la parte superior del archivo
Startup.cs
para resolver la referencia aChatHub
:using SignalRWebPack.Hubs;
Habilitar la comunicación entre cliente y servidor
Actualmente, en la aplicación se muestra un formulario simple para enviar mensajes. Al intentar hacer algo no sucede nada. El servidor está escuchando en una ruta específica, pero no hace nada con los mensajes enviados.
Ejecute el comando siguiente en la raíz del proyecto:
npm install @aspnet/signalr
El comando anterior instala el cliente TypeScript de SignalR, que permite al cliente enviar mensajes al servidor.
Agregue el código resaltado al archivo
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() { }
El código anterior admite la recepción de mensajes desde el servidor. La clase
HubConnectionBuilder
crea un generador para configurar la conexión al servidor. La funciónwithUrl
configura la dirección URL del concentrador.SignalR permite el intercambio de mensajes entre un cliente y un servidor. Cada mensaje tiene un nombre específico. Por ejemplo, puede haber mensajes con el nombre
messageReceived
que pueden ejecutar la lógica responsable de mostrar el nuevo mensaje en la zona de mensajes. La escucha a un mensaje concreto se puede realizar mediante la funciónon
. Puede escuchar a cualquier número de nombres de mensaje. También se pueden pasar parámetros al mensaje, como el nombre del autor y el contenido del mensaje recibido. Una vez que el cliente recibe un mensaje, se crea un elementodiv
con el nombre del autor y el contenido del mensaje en su atributoinnerHTML
. El nuevo mensaje se agrega al elementodiv
principal que muestra los mensajes.Ahora que el cliente puede recibir mensajes, debe configurarlo para poder enviarlos. Agregue el código resaltado al archivo
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 = ""); }
El envío de mensajes a través de la conexión de WebSockets requiere llamar al método
send
. El primer parámetro del método es el nombre del mensaje. Los datos del mensaje se encuentran en los otros parámetros. En este ejemplo, se envía al servidor un mensaje identificado comonewMessage
. El mensaje está formado por el nombre de usuario y la entrada del usuario desde un cuadro de texto. Si el envío funciona, se borra el valor del cuadro de texto.Agregue el método
NewMessage
a la claseChatHub
: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); } } }
El código anterior difunde los mensajes recibidos a todos los usuarios conectados, una vez que el servidor los recibe. No es necesario tener un método
on
genérico para recibir todos los mensajes. Basta con un método que tenga el nombre del mensaje.En este ejemplo, el cliente de TypeScript envía un mensaje que se identifica como
newMessage
. El métodoNewMessage
de C# espera los datos enviados por el cliente. Se realiza una llamada a SendAsync en Clients.All. Los mensajes recibidos se envían a todos los clientes conectados al concentrador.
Prueba de la aplicación
Confirme que la aplicación funciona con los pasos siguientes.
Ejecute Webpack en modo release. Desde la ventana Consola del Administrador de paquetes, ejecute el comando siguiente en la raíz del proyecto. Si no está en la raíz del proyecto, escriba
cd SignalRWebPack
antes de introducir el comando.npm run release
Este comando genera la entrega de los recursos del lado cliente al ejecutar la aplicación. Los recursos se colocan en la carpeta
wwwroot
.Webpack ha completado las tareas siguientes:
- Purgar el contenido del directorio
wwwroot
. - Convertir el lenguaje TypeScript en JavaScript, proceso conocido como transpilación.
- Alterar el código JavaScript generado para reducir el tamaño del archivo, proceso conocido como minificación.
- Copiar los archivos JavaScript, CSS y HTML procesados desde
src
en el directoriowwwroot
. - Se han insertado los elementos siguientes en el archivo
wwwroot/index.html
:- Una etiqueta
<link>
, que hace referencia al archivowwwroot/main.<hash>.css
. Esta etiqueta se coloca inmediatamente antes de la etiqueta</head>
de cierre. - Una etiqueta
<script>
, que hace referencia al archivowwwroot/main.<hash>.js
minificado. Esta etiqueta se coloca inmediatamente después de la etiqueta</title>
de cierre.
- Una etiqueta
- Purgar el contenido del directorio
Seleccione Depurar>Iniciar sin depurar para iniciar la aplicación en un explorador sin adjuntar el depurador. El archivo
wwwroot/index.html
se entrega enhttp://localhost:<port_number>
.Abra otra instancia del explorador (sirve cualquiera). Pegue la dirección URL en la barra de direcciones.
Elija un explorador, escriba algo en el cuadro de texto Mensaje y seleccione el botón Enviar. El nombre de usuario único y el mensaje se muestran en las dos páginas al instante.
Recursos adicionales
Comentarios
https://aka.ms/ContentUserFeedback.
Próximamente: A lo largo de 2024 iremos eliminando gradualmente las Cuestiones de GitHub como mecanismo de retroalimentación para el contenido y lo sustituiremos por un nuevo sistema de retroalimentación. Para más información, consulta:Enviar y ver comentarios de