Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Note
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 10 de este artículo.
Warning
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 10 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)
Prerequisites
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 Siguiente.
- Asigne el nombre
SignalRWebpackal proyecto y seleccione Crear. - Seleccione .NET 8.0 (Compatibilidad a largo plazo) en la lista desplegable Framework. Selecciona 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.MSBuildy, 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
Hubsen 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
ongené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
NewMessagede 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
usingsiguiente en la parte superior deProgram.cspara resolver la referencia aChatHub:using SignalRWebpack.Hubs;En
Program.cs, asigne la ruta/hubal 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 -yAgregue la propiedad resaltada al archivo
package.jsony 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
privateentrue, 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-cliLa opción
-Edeshabilita 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
scriptsdel archivopackage.jsonpor 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ónmodedeshabilita 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 scriptreleasepara 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
outputinvalida el valor predeterminado dedist. En su lugar, la agrupación se genera en el directoriowwwroot. - La matriz
resolve.extensionsincluye.jspara importar el código JavaScript del cliente de SignalR.
- La propiedad
Cree un directorio denominado
srcen la raíz del proyecto,SignalRWebpack/, para el código de cliente.Copie el directorio
srcy sus contenidos del proyecto de ejemplo en la raíz del proyecto. El directoriosrccontiene 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 textotbMessagey llama a la funciónsendcuando 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
HubConnectionBuildercrea un generador para configurar la conexión al servidor. La funciónwithUrlconfigura 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
messageReceivedque 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 elementodivcon el nombre del autor y el contenido del mensaje en su atributoinnerHTML. Se agrega al elementodivprincipal 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/nodeEl 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.
Pruebas 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 releaseEste 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
srcen 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>.jsminificado. 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.htmlse 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)
Prerequisites
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 Siguiente.
- Asigne el nombre
SignalRWebpackal proyecto y seleccione Crear. - Seleccione .NET 7.0 (Compatibilidad con términos estándar) en la lista desplegable Marco . Selecciona 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.MSBuildy, 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
Hubsen 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
ongené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
NewMessagede 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
usingsiguiente en la parte superior deProgram.cspara resolver la referencia aChatHub:using SignalRWebpack.Hubs;En
Program.cs, asigne la ruta/hubal 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 -yAgregue la propiedad resaltada al archivo
package.jsony 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
privateentrue, 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-cliLa opción
-Edeshabilita 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
scriptsdel archivopackage.jsonpor 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ónmodedeshabilita 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 scriptreleasepara 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
outputinvalida el valor predeterminado dedist. En su lugar, la agrupación se genera en el directoriowwwroot. - La matriz
resolve.extensionsincluye.jspara importar el código JavaScript del cliente de SignalR.
- La propiedad
Copie el directorio
srcy sus contenidos del proyecto de ejemplo en la raíz del proyecto. El directoriosrccontiene 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 textotbMessagey llama a la funciónsendcuando 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
HubConnectionBuildercrea un generador para configurar la conexión al servidor. La funciónwithUrlconfigura 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
messageReceivedque 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 elementodivcon el nombre del autor y el contenido del mensaje en su atributoinnerHTML. Se agrega al elementodivprincipal 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/nodeEl 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.
Pruebas 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 releaseEste 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
srcen 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>.jsminificado. 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.htmlse 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)
Prerequisites
- Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web
- SDK de .NET 6
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 Siguiente.
- Asigne el nombre
SignalRWebpackal proyecto y seleccione Crear. - Seleccione .NET 6.0 (Compatibilidad a largo plazo) en la lista desplegable Marco . Selecciona 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.MSBuildy, 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
Hubsen 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
ongené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étodoNewMessagede 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
usingsiguiente en la parte superior deProgram.cspara resolver la referencia aChatHub:using SignalRWebpack.Hubs;En
Program.cs, asigne la ruta/hubal 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 -yAgregue la propiedad resaltada al archivo
package.jsony 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
privateentrue, 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-cliLa opción
-Edeshabilita 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
scriptsdel archivopackage.jsonpor 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ónmodedeshabilita 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 scriptreleasepara 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
outputinvalida el valor predeterminado dedist. En su lugar, la agrupación se genera en el directoriowwwroot. - La matriz
resolve.extensionsincluye.jspara importar el código JavaScript del cliente de SignalR.
- La propiedad
Copie el directorio
srcy sus contenidos del proyecto de ejemplo en la raíz del proyecto. El directoriosrccontiene 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 textotbMessagey llama a la funciónsendcuando 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
HubConnectionBuildercrea un generador para configurar la conexión al servidor. La funciónwithUrlconfigura 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
messageReceivedque 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 elementodivcon el nombre del autor y el contenido del mensaje en su atributoinnerHTML. Se agrega al elementodivprincipal 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/nodeEl 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.
Pruebas 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 SignalRWebpackantes de introducir el comando.npm run releaseEste 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
srcen 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>.jsminificado. 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.htmlse 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)
Prerequisites
- 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 Siguiente.
- Asigna el nombre *SignalRWebPac`` al proyecto y selecciona 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.MSBuildy 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 -yAgregue la propiedad resaltada al archivo
package.jsony 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
privateentrue, 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.10Algunos 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
-Edeshabilita 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
scriptsdel archivopackage.jsonpor 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ónmodedeshabilita 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 scriptreleasepara 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
outputinvalida el valor predeterminado dedist. En su lugar, la agrupación se genera en el directoriowwwroot. - La matriz
resolve.extensionsincluye.jspara 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.htmlcon 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
.cssdel proyecto.Cree
src/css/main.csscon 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.cssanterior aplica estilo a la aplicación.Reemplaza
src/tsconfig.jsonpor 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.tscon 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ónsendse 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.cscon el código siguiente:using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace SignalRWebPack.Hubs { public class ChatHub : Hub { } }Agregue la instrucción
usingsiguiente en la parte superior del archivoStartup.cspara 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/nodeEl 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
HubConnectionBuildercrea un generador para configurar la conexión al servidor. La funciónwithUrlconfigura 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
messageReceivedque 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 elementodivcon el nombre del autor y el contenido del mensaje en su atributoinnerHTML. Se agrega al elementodivprincipal 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
NewMessagea 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
ongené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étodoNewMessagede 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.
Pruebas 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 SignalRWebPackantes de introducir el comando.npm run releaseEste 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
srcen 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>.jsminificado. 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.htmlse 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)
Prerequisites
- 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.
- Asigna el nombre *SignalRWebPack`, al proyecto y selecciona 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 -yAgregue 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
privateentrue, 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.3Algunos 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
-Edeshabilita 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
scriptsdel archivopackage.jsonpor 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ónmodedeshabilita 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 scriptreleasepara 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
outputinvalida el valor predeterminado dedist. En su lugar, la agrupación se genera en el directoriowwwroot. - La matriz
resolve.extensionsincluye.jspara 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.htmlcon 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
.cssdel proyecto.Cree
src/css/main.csscon 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.cssanterior aplica estilo a la aplicación.Reemplaza
src/tsconfig.jsonpor 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.tscon 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ónsendse 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.Configuremuestra Hello World! . Reemplace la llamada de métodoapp.Runpor 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.cscon 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.cspara 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/signalrEl 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
HubConnectionBuildercrea un generador para configurar la conexión al servidor. La funciónwithUrlconfigura 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
messageReceivedque 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 elementodivcon el nombre del autor y el contenido del mensaje en su atributoinnerHTML. El nuevo mensaje se agrega al elementodivprincipal 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
NewMessagea 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
ongené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étodoNewMessagede 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.
Pruebas 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 SignalRWebPackantes de introducir el comando.npm run releaseEste 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
srcen 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>.jsminificado. 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.htmlse 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.