Why are messages pushed from an MVC app with Signa

2019-07-01 10:25发布

问题:

I have a simple SignalR hub application, using MVC4/C# on the server end, hosted in an Azure Web Role (single instance, in the emulator, with an http input endpoint), and TypeScript compiled to JS on the client. I have a gallery of images that a number of clients may upload to, and when somebody uploads then I want any other connected clients to receive a message telling them this has happened and giving them the chance to refresh, similar to the way you are notified about a new answer to a question you are viewing here on SO.

On the server I have a method which returns a list of the items in the gallery, and another method to push messages to clients, like this:

    public virtual void PushMessage(String message)
    {
        System.Diagnostics.Debug.WriteLine("PushMessage: " + message);
        this.Clients.All.pushMessage(message);
    }

    public virtual GalleryDataResponse<T> List(String accountID, String armID, Int32 page)
    {
        GalleryDataResponse<T> response = new GalleryDataResponse<T>();
        try
        {
            IEnumerable<T> items = getAllItems(accountID, armID, page);
            if (items != null && items.Count() > 0)
            {
                response.Result = DataResponse.RESULT_DATA;
                response.Message = String.Empty;
                response.Items = items;
            }
            else
            {
                response.Result = DataResponse.RESULT_EMPTY;
                response.Message = @"There are no " + itemName + @"s in your account at the moment: " + this.Context.ConnectionId;
                response.Items = null;
            }
            this.Caller.pushMesage("HELLO, CALLER");
            PushMessage("HELLO, ALL CLIENTS");
        }
        catch (Exception ex)
        {
            response.Result = DataResponse.RESULT_ERROR;
            response.Message = ex.Message;
            response.Items = null;
        }
        return response;
    }

(The List function is then overridden in a derived class which passes in the type T - this works fine).

On the client, using the SignalR definition files from DefinitelyTyped I have the following TypeScript:

var that = this;
this.connection = $.hubConnection();
this.connection.logging = true;
this.connection.id = this.gServerData.CurrentAccountID + "-" +    this.gServerData.CurrentArmID;
this.connection.start().done(() => {
    that.proxy = that.connection.createProxy(that.gServerData.DataHubName);
    that.proxy.on("pushMessage", (message) => {
        console.log("PUSHED: " + message);
        debugger;
    });
    that.proxy.invoke("List", that.gServerData.CurrentAccountID, that.gServerData.CurrentArmID, 0).done((response: IGalleryDataResponse) => {
        that.handleListResponse(response);
    });
});

... which gives the following JavaScript:

var that = this;
this.connection = $.hubConnection();
this.connection.logging = true;
this.connection.id = this.gServerData.CurrentAccountID + "-" + this.gServerData.CurrentArmID;
this.connection.start().done(function () {
    that.proxy = that.connection.createProxy(that.gServerData.DataHubName);
    that.proxy.on("pushMessage", function (message) {
        t.Tracer.Trace("PUSHED: " + message);
    });
    that.proxy.invoke("List", that.gServerData.CurrentAccountID, that.gServerData.CurrentArmID, 0).done(function (response) {
    that.handleListResponse(response);
    });
});

Invoking my list function works perfectly. I get a response back from the hub exactly as expected. And if I kill the server process but keep the browser alive, I get errors in the console which show me that the client is trying to maintain a connection.

I added these two lines this.Caller.pushMesage("HELLO, CALLER"); PushMessage("HELLO, ALL CLIENTS"); to my List function on the server side to test the server pushing content to the client. I'm expecting them to be called before the list function returns, but this isn't happening. I've also tried calling the hub from outside, like this:

var myHub = GlobalHost.ConnectionManager.GetHubContext<MediaGalleryHub>();
myHub.Clients.pushMessage("Hello from Global.asax");

... but while I can see from tracing that my PushMessage function is being called, I never get anything on the client side.

What am I doing wrong?

References I've already tried include:

http://blogs.msdn.com/b/brunoterkaly/archive/2012/04/09/how-to-build-cloud-based-asynchronous-scalable-web-applications-with-near-real-time-persistent-long-running-connections-with-signalr.aspx

http://msdn.microsoft.com/en-us/magazine/hh965663.aspx

https://github.com/SignalR/SignalR/wiki/Hubs

https://github.com/SignalR/SignalR/wiki/SignalR-JS-Client-Hubs-%28No-Proxy%29

回答1:

This part of the code is wrong:

this.connection.start().done(() => {
    that.proxy = that.connection.createProxy(that.gServerData.DataHubName);
    that.proxy.on("pushMessage", (message) => {
        console.log("PUSHED: " + message);
        debugger;
    });
    that.proxy.invoke("List", that.gServerData.CurrentAccountID, that.gServerData.CurrentArmID, 0).done((response: IGalleryDataResponse) => {
        that.handleListResponse(response);
    });
});

You need to subscribe before you call start:

that.proxy = that.connection.createProxy(that.gServerData.DataHubName);
that.proxy.on("pushMessage", (message) => {
    console.log("PUSHED: " + message);
    debugger;
});    

this.connection.start().done(() => {
    that.proxy.invoke("List", that.gServerData.CurrentAccountID, that.gServerData.CurrentArmID, 0).done((response: IGalleryDataResponse) => {
        that.handleListResponse(response);
    });
});