Как отправить сообщение signalr клиенту MVC с веб-контроллера
Привет,
У меня есть Asp.Net ядро 2.1, веб-приложение MVC в Visual Studio 2017 15.7.5.
Я пытался выяснить, как отправить одно строковое текстовое сообщение SignalR в MVC View.cshtml из метода контроллера, который вызывается из представления MVC, используя этот тип ссылки: @Html.RouteLink("Get Products", "GetProducts").
Помещая его в другой контекст, я хочу отправить сообщение извне концентратора (непосредственно с сервера на клиент), введя экземпляр IHubContext в мой контроллер и добавив его в конструктор.
У меня есть моя настройка приложения таким образом:
Автозагрузки.в CS:
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.EntityFrameworkCore; using ChinavasionAPI.Data; using ChinavasionAPI.Hubs; namespace ChinavasionAPI { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Adds a default in-memory implementation of IDistributedCache. services.AddDistributedMemoryCache(); services.AddSession(options => { // Set a short timeout for easy testing. options.IdleTimeout = TimeSpan.FromSeconds(10); options.Cookie.HttpOnly = true; }); services.AddRouting(); services.AddSignalR(); services.AddMvc(); services.AddDbContext<ChinavasionAPIContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ChinavasionAPIContext"))); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseSession(); app.UseSignalR(routes => { routes.MapHub<ChvHub>("/chvHub"); }); app.UseMvc(routes => { routes.MapRoute( name: "token", template: "token", defaults: new { controller = "API", action = "GetAccessToken" }); routes.MapRoute( name: "GetCustomers", template: "customers", defaults: new { controller = "Customers", action = "GetCustomers" }); routes.MapRoute( name: "GetProducts", template: "products", defaults: new { controller = "Products", action = "GetProducts" }); } } }
ChvHub:
using Microsoft.AspNetCore.SignalR; using System.Threading.Tasks; namespace ChinavasionAPI.Hubs { public class ChvHub : Hub { public async Task SendMessage(string user, string message) { await Clients.All.SendAsync("ReceiveMessage", user, message); } } }
chat.js:
const connection = new signalR.HubConnectionBuilder() .withUrl("/chvHub") .build(); connection.on("ReceiveMessage", (user, message) => { const msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); const encodedMsg = user + " says " + msg; const li = document.createElement("li"); li.textContent = encodedMsg; document.getElementById("messagesList").appendChild(li); }); connection.start().catch(err => console.error(err.toString())); document.getElementById("sendButton").addEventListener("click", event => { const user = document.getElementById("userInput").value; const message = document.getElementById("messageInput").value; connection.invoke("SendMessage", user, message).catch(err => console.error(err.toString())); event.preventDefault(); });
ProductsController.в CS:
using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using Lakeside.Api.AdapterLibrary; using ChinavasionAPI.DTOs; using ChinavasionAPI.Models; using ChinavasionAPI.Models.CAPIViewModels; using ChinavasionAPI.Data; using ChinavasionAPI.Hubs; using Microsoft.AspNetCore.SignalR; namespace ChinavasionAPI.Controllers { public class ProductsController : Controller { private readonly ChinavasionAPIContext _context; private readonly IHubContext<ChvHub> _hubContext; public ProductsController(ChinavasionAPIContext context, IHubContext<ChvHub> hubcontext) { _context = context; _hubContext = hubcontext; } public async Task<IActionResult> GetProducts() { var model = new AccessModel(); model.UserAccessModel = _context.UserAccessModels.Single(a => a.ID == 1); var accessToken = (model.UserAccessModel.AccessToken ?? TempData["accessToken"]).ToString(); var serverUrl = (model.UserAccessModel.ServerUrl ?? TempData["serverUrl"]).ToString(); var nopApiClient = new ApiClient(accessToken, serverUrl); MultipleProductModel multipleProductModel = new MultipleProductModel(); List<Category> categories = new List<Category>(); string jsonUrl = $"/api/products?limit=75&fields=id,sku,name,images,categories"; object productsData = nopApiClient.Get(jsonUrl); var productsRootObject = JsonConvert.DeserializeObject<ProductsRootObject>(productsData.ToString()); var products = productsRootObject.Products.Where( product => !string.IsNullOrEmpty(product.Name)); multipleProductModel.MProductsApi = productsRootObject.Products; int? setupID = 1; if (setupID == null) { return NotFound(); } var setup = await _context.Setups.SingleOrDefaultAsync(m => m.ID == setupID); if (setup == null) { return NotFound(); } serverUrl = setup.ServerUrl; string serverKey = setup.Key; string queryCall = "api/getProductDetails.php"; string parameterName = "model_code"; multipleProductModel.MProducts.Clear(); var productsService = new Service.ProductService(); foreach(ProductApi nopProduct in products) { Product chvproduct = (await productsService.GetProductBySkuAPIAsync(serverUrl, serverKey, queryCall, parameterName, nopProduct.Sku)); if (chvproduct == null) { multipleProductModel.MProducts.Add(new Product { product_id = 0, model_code = "Not on file" }); } else { multipleProductModel.MProducts.Add(chvproduct); } await this._hubContext.Clients.All.SendAsync("ReceiveMessage", "NewMessage", "AnotherNewMessage"); } return View("~/Views/API/Products.cshtml", multipleProductModel); }
Маркер accessToken.cshtml по:
@model ChinavasionAPI.Models.AccessModel @using Microsoft.AspNetCore.Http.Extensions @{ ViewData["Title"] = "Access Token"; } @section Scripts { <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <script src="~/lib/signalr/signalr.js"></script> <script src="~/js/chat.js"></script> } <div> <h2>Access Data</h2> @using (Html.BeginRouteForm("Submit", FormMethod.Post)) { <table> <tr> <td> <label for="serverUrl">Server url: </label> </td> <td> <input name="serverUrl" type="text" id="serverUrl" value="@Model.UserAccessModel.ServerUrl" style="width:400px" /> </td> </tr> <tr> <td> <label for="clientId">Client Id: </label> </td> <td> <input name="clientId" type="text" id="clientId" value="@Model.UserAccessModel.ClientId" style="width:400px" /> </td> </tr> <tr> <td> <label for="clientSecret">Client Secret: </label> </td> <td> <input name="clientSecret" type="text" id="clientSecret" value="@Model.UserAccessModel.ClientSecret" style="width:400px" /> </td> </tr> <tr> <td> <label for="redirectUrl">Redirect Url: </label> </td> <td> <input name="redirectUrl" type="text" id="redirectUrl" readonly="readonly" value="@Model.UserAccessModel.RedirectUrl" style="width:400px" /> </td> </tr> <tr> <td></td> <td> <br /> <input type="submit" value="Get Access Token" /> <button type="button" id="refresh-token-button">Refresh Access Token</button> </td> </tr> </table> } </div> <div class="container"> <div class="row"> <div class="col-6"> </div> <div class="col-6"> <ul id="messagesList"></ul> </div> </div> </div> @Html.RouteLink("Get Customers", "GetCustomers") @Html.RouteLink("Get Products", "GetProducts")
Когда я нажимаю на ссылку, сгенерированную из: @формат HTML.RouteLink("Get Products", "GetProducts"), он обрабатывает все в "GetProducts" перед отображением из возвращаемого представления("~/Views/API/Products.cshtml", multipleProductModel);, но он никогда не отображает никаких сообщений на экране просмотра AccessToken.cshtml.
Когда я использую инструменты разработчика в Firefox, в консоли показывает сервер SignalR соединение веб-розетка в консоли(через WebSocket, связанных с WS://localhost в:64370/chvHub?ИД=QeZ8xRJSFHuzzQIZ72X2dg), но потом, когда я щелкните на ссылке, сгенерированной от: @HTML-код.RouteLink("вам продукты", "getproducts так"), он показывает это отключения(подключение отсоединенного.).
Кроме того, когда я использую отладчик Visual Studio и останавливаю его в методе GetProducts, _hubContext не имеет соединения.
По какой-то причине, когда управление переходит к ProductsController, соединение SignalR теряется.
Любая помощь, которую кто-либо может оказать, чтобы помочь в отображении одного сообщения SignalR в представлении, будет с благодарностью оценена.
Спасибо,
Тони
Что я уже пробовал:
Я добавил эту строку в метод ConfigureServices в Startup.cs:
services.AddSignalR();
Я добавил Эти строки, чтобы настроить способ запуска.в CS:
app.UseSignalR(routes => { routes.MapHub<ChvHub>("/chvHub"); });
Я настроил свой контроллер таким образом, чтобы он вводил экземпляр IHubContext:
public class ProductsController : Controller { private readonly ChinavasionAPIContext _context; private readonly IHubContext<ChvHub> _hubContext; public ProductsController(ChinavasionAPIContext context, IHubContext<ChvHub> hubcontext) { _context = context; _hubContext = hubcontext; }
Добавлена эта строка в контроллере для отправки сообщения клиенту:
await this._hubContext.Clients.All.SendAsync("ReceiveMessage", "NewMessage", "AnotherNewMessage");
В консоли диспетчера пакетов Nuget я выполнил следующие команды:
НПМ инициализации г
НПМ установить @Паш/помощью SignalR
Я установил через Управление пакетами Nuget: Microsoft.AspNetCore.SignalR Последняя Стабильная Версия 1.0.2.
Nelek
+5 за попытку написать хороший вопрос. К сожалению я не могу помочь с этой темой
Tony Girgenti
Спасибо, Нелек. Приятно сознавать, что я сделал что-то правильно.
Тони