Tony Girgenti Ответов: 1

Как отправить сообщение 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

Спасибо, Нелек. Приятно сознавать, что я сделал что-то правильно.

Тони

1 Ответов

Рейтинг:
8

Tony Girgenti

Я не могу сказать, что я понял это, но мне удалось заставить его работать, тестируя различные стратегии кодирования, особенно вдохновленные этим статья[^ автор: Дино Эспозито.

Я создал кнопку "GetProducts" и изменил Нижний "div" в файле AccessToken.cshtml следующим образом:

<div class="container">
     <div class="row">
          <div class="col-6"> </div>
          <div class="col-6">
               <ul id="messagesList"></ul>
          </div>
     </div>
     <form asp-controller="Products" asp-action="GetProducts">
          <div class="form-group">
               <br />
               <input type="submit" id="get-products-button" value="Get Products" class="btn btn-default" onclick="startTask()" />
          </div>
     </form>
</div>

<script>
     function startTask() {
          $.get("/products/getproducts/");
     }
</script>

<script>
     document.getElementById("get-products-button").addEventListener("click", event => {
          event.preventDefault();
     });
</script>


Я добавил Нижний скрипт просто в качестве средства отладки, чтобы посмотреть, активируется ли кнопка.

Без какой-либо части вышеприведенного кода сообщения клиенту не работают. Я не могу этого объяснить, я просто знаю, что если я удалю какую-либо его часть, она не будет отображать сообщения клиенту.

Сообщения клиенту действительно отображаются, но этот успех имеет свою цену. Когда метод GetProducts попадает в эту строку:
return View("~/Views/API/Products.cshtml", multipleProductModel);

Он не отображает экран просмотра Products.cshtml. Он не выдает ошибку, он просто не отображается.

Метод GetProducts обрабатывает так, как он должен, но он не будет отображать это представление.

Я был бы признателен, если бы кто-нибудь сказал мне, почему этот вид не будет отображаться.

Спасибо,
Тони