Bagaimana cara mengirim pesan pemberi sinyal ke klien MVC dari pengontrol web

pemrograman


Halo,

Saya memiliki aplikasi Web Asp.Net Core 2.1, MVC di Visual Studio 2017 15.7.5.

Saya telah mencoba mencari cara untuk mengirim pesan teks string SignalR tunggal ke MVC View.cshtml dari metode pengontrol yang dipanggil dari MVC View menggunakan jenis tautan ini: @Html.RouteLink(“Get Products”, ” Dapatkan Produk”).

Menempatkannya dalam konteks yang berbeda, saya ingin mengirim pesan dari luar hub (langsung dari server ke klien) dengan memasukkan instance IHubContext ke pengontrol saya dan menambahkannya ke konstruktor.

Saya mengatur aplikasi saya dengan cara ini:

Startup.cs:

C#
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:

C#
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);
          }
     }
}

obrolan.js:

JavaScript
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();
});

Pengontrol Produk.cs:

C#
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:

HTML
@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")

Ketika saya mengklik tautan yang dihasilkan dari: @Html.RouteLink(“Dapatkan Produk”, “DapatkanProduk”)ia memproses semua yang ada di “GetProducts” sebelum ditampilkan dari tampilan kembali (“~/Views/API/Products.cshtml”, multipleProductModel);, tetapi tidak pernah menampilkan pesan apa pun di layar tampilan AccessToken.cshtml.

Saat saya menggunakan alat pengembang di Firefox, konsol menampilkan koneksi SignalR Web Socket di konsol (WebSocket terhubung ke ws://localhost:64370/chvHub?id=QeZ8xRJSFHuzzQIZ72X2dg), tetapi kemudian ketika saya mengklik tautan yang dihasilkan dari: @ Html.RouteLink(“Dapatkan Produk”, “GetProduk”), ini menunjukkan terputus(Koneksi terputus.).

Juga, ketika saya menggunakan debugger Visual Studio dan menghentikannya dalam metode GetProducts, _hubContext tidak memiliki koneksi.

Untuk beberapa alasan, ketika kontrol masuk ke ProductsController, koneksi SignalR terputus.

Bantuan apa pun yang dapat diberikan siapa pun untuk membantu menampilkan satu pesan SignalR ke suatu tampilan akan sangat kami hargai.

Terima kasih,
Toni

Apa yang saya coba:

Saya telah menambahkan baris ini ke metode ConfigureServices di Startup.cs:

services.AddSignalR();

Saya telah menambahkan baris ini ke metode Konfigurasi di Startup.cs:

C#
app.UseSignalR(routes =>
            {
               routes.MapHub<ChvHub>("/chvHub");
            });

Saya telah mengkonfigurasi pengontrol saya untuk memasukkan instance IHubContext dengan cara ini:

C#
public class ProductsController : Controller
    {
          private readonly ChinavasionAPIContext _context;
          private readonly IHubContext<ChvHub> _hubContext;
          public ProductsController(ChinavasionAPIContext context, IHubContext<ChvHub> hubcontext)
          {
               _context = context;
               _hubContext = hubcontext;
          }

Menambahkan baris ini di pengontrol untuk mengirim pesan ke klien:

C#
await this._hubContext.Clients.All.SendAsync("ReceiveMessage", "NewMessage", "AnotherNewMessage");

Di Konsol Manajer Paket Nuget saya menjalankan perintah ini:

npm init -y
npm instal @aspnet/signalr

Saya menginstal melalui Kelola Paket Nuget: Microsoft.AspNetCore.SignalR Stabil Terbaru 1.0.2.

Solusi 1

Saya tidak bisa mengatakan bahwa saya menemukan jawabannya, tetapi saya berhasil membuatnya berfungsi dengan menguji berbagai strategi pengkodean yang terutama terinspirasi dari ini artikel[^] oleh Dino Esposito.

Saya membuat tombol untuk “GetProducts” dan memodifikasi “div” bawah di file AccessToken.cshtml seperti ini:

JavaScript
<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>

Saya menambahkan skrip bawah hanya sebagai bantuan debugging untuk melihat apakah tombol tersebut diaktifkan.

Tanpa bagian apa pun dari kode di atas, pesan ke klien tidak akan berfungsi. Saya tidak dapat menjelaskannya, saya hanya tahu bahwa jika saya menghapus bagian mana pun, pesan tersebut tidak akan ditampilkan kepada klien.

Pesan kepada klien memang ditampilkan, namun keberhasilan ini ada harganya. Ketika metode GetProducts sampai ke baris ini:

C#
return View("~/Views/API/Products.cshtml", multipleProductModel);

Itu tidak menampilkan layar tampilan Products.cshtml. Itu tidak memberikan kesalahan, hanya saja tidak ditampilkan.

Metode GetProducts memproses sebagaimana mestinya, tetapi tidak akan menampilkan tampilan tersebut.

Saya akan sangat menghargai jika ada yang bisa memberi tahu saya mengapa tampilan itu tidak ditampilkan.

Terima kasih,
Toni

コメント

タイトルとURLをコピーしました