Integrating AspNet.WebApi with AspNet.SignalR

1.2k Views Asked by At

I want to integrate Microsoft.AspNet.SignalR version="2.1.2" with Microsoft.AspNet.WebApi version="5.2.2" so that the API can communicate in REAL-TIME. I found one VERY NICE SAMPLE that implements/works exactly the same way I want, but the sample uses jquery.signalR-0.5.0.js. Some of the earlier implementations have changed, and so far here is what I have done in a failed effort to upgrade the solution to use the latest signalr, asp.net web api and owin. I left the Hub as it is

using SignalR.Hubs;
namespace NdcDemo.Hubs
{
    // This hub has no inbound APIs, since all inbound communication is done
    // via the HTTP API. It's here for clients which want to get continuous
    // notification of changes to the ToDo database.
    [HubName("todo")]
    public class ToDoHub : Hub { }
}

I also left the ApiControllerWithHub class as it is

using System;
using System.Web.Http;
using SignalR;
using SignalR.Hubs;
namespace NdcDemo.Controllers
{
    public abstract class ApiControllerWithHub<THub> : ApiController
        where THub : IHub
    {
        Lazy<IHubContext> hub = new Lazy<IHubContext>(
            () => GlobalHost.ConnectionManager.GetHubContext<THub>()
        );
        protected IHubContext Hub
        {
            get { return hub.Value; }
        }
    }
}

For the ToDoController, I changed the

Hub.Clients.addItem(item), Hub.Clients.updateItem(toUpdate),
 Hub.Clients.deleteItem(id)

to

Hub.Clients.All.addItem(item), Hub.Clients.All.updateItem(toUpdate),
 Hub.Clients.All.deleteItem(id)

and this is now the full ToDoController class

 using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Threading;
    using System.Web.Http;
    using NdcDemo.Hubs;
    using NdcDemo.Models;

    namespace NdcDemo.Controllers
    {
        [InvalidModelStateFilter]
        public class ToDoController : ApiControllerWithHub<ToDoHub>
        {
            private static List<ToDoItem> db = new List<ToDoItem>
            {
                new ToDoItem { ID = 0, Title = "Do a silly demo on-stage at NDC" },
                new ToDoItem { ID = 1, Title = "Wash the car" },
                new ToDoItem { ID = 2, Title = "Get a haircut", Finished = true }
            };
            private static int lastId = db.Max(tdi => tdi.ID);
            public IEnumerable<ToDoItem> GetToDoItems()
            {
                lock (db)
                    return db.ToArray();
            }

            public ToDoItem GetToDoItem(int id)
            {
                lock (db)
                {
                    var item = db.SingleOrDefault(i => i.ID == id);
                    if (item == null)
                        throw new HttpResponseException(
                            Request.CreateResponse(HttpStatusCode.NotFound)
                        );

                    return item;
                }
            }

            public HttpResponseMessage PostNewToDoItem(ToDoItem item)
            {
                lock (db)
                {
                    // Add item to the "database"
                    item.ID = Interlocked.Increment(ref lastId);
                    db.Add(item);

                    // Notify the connected clients
                    Hub.Clients.addItem(item);

                    // Return the new item, inside a 201 response
                    var response = Request.CreateResponse(HttpStatusCode.Created, item);
                    string link = Url.Link("apiRoute", new { controller = "todo", id = item.ID });
                    response.Headers.Location = new Uri(link);
                    return response;
                }
            }

            public ToDoItem PutUpdatedToDoItem(int id, ToDoItem item)
            {
                lock (db)
                {
                    // Find the existing item
                    var toUpdate = db.SingleOrDefault(i => i.ID == id);
                    if (toUpdate == null)
                        throw new HttpResponseException(
                            Request.CreateResponse(HttpStatusCode.NotFound)
                        );

                    // Update the editable fields and save back to the "database"
                    toUpdate.Title = item.Title;
                    toUpdate.Finished = item.Finished;

                    // Notify the connected clients
                    Hub.Clients.updateItem(toUpdate);

                    // Return the updated item
                    return toUpdate;
                }
            }

            public HttpResponseMessage DeleteToDoItem(int id)
            {
                lock (db)
                {
                    int removeCount = db.RemoveAll(i => i.ID == id);
                    if (removeCount <= 0)
                        return Request.CreateResponse(HttpStatusCode.NotFound);

                    // Notify the connected clients
                    Hub.Clients.deleteItem(id);

                    return Request.CreateResponse(HttpStatusCode.OK);
                }
            }
        }
    }

And then I put app.MapSignalR(); But the demo doesn't work...the API doesn't contact clients...Where am I going wrong? I would still appreciate any more simpler recommendations based on this solution.

1

There are 1 best solutions below

0
On

Solution from OP.

Answer: After a a cup of camomile tea, i found out that the clients had to include the KEYWORD CLIENT before the dynamic methods in Todo.js... So, here is what that needs to be modified so that the sample works

hub.client.addItem = function (item) {
        alert("i just received something...");
        viewModel.add(item.ID, item.Title, item.Finished);
    };
    hub.client.deleteItem = function (id) {
        viewModel.remove(id);
    };
    hub.client.updateItem = function (item) {
        viewModel.update(item.ID, item.Title, item.Finished);
    };

And it works!