1

I know that SignalR can't have a return from client when invokation came from the server. On the github repository of SignalR I asked for a workaround (https://github.com/aspnet/SignalR/issues/1329) and they suggest me to get the result by sending it from the client to server to another method in the hub and so use TaskCompletionSource and some connection metadata to catch the result, but I'm stuck on how to do this

Controller Server :

[HttpPut("send/{message}")]
public async Task<IActionResult> SendMessage(string message)
{
    if (!ModelState.IsValid) return BadRequest(ModelState.Values);

    string connectionId = Request.Headers["connectionId"];
    await _chatHubContext.Clients.Client(connectionId).InvokeAsync("send");

    // Catch the call of MessageReceived and get the chat status

    return new OkObjectResult(new EmptyJsonResult() { Result = "OK" }); 

}

Hub Server

public class ChatHub : Hub
{
    public Task MessageReceive(bool chatStatus)
    {
        // Tell controller that message is received
    }
}

Angular 4 client

import { Component, Inject } from '@angular/core';
import { HubConnection } from '@aspnet/signalr-client';

@Component({
  selector: 'chat',
  templateUrl: './chat.component.html',
  styleUrls: ['./chat.component.css']
})
/** chat component*/
export class ChatComponent {
  hubConnection: HubConnection;
  chatStatus = false;

  /** chat ctor */
  constructor( @Inject('BASE_URL') private originUrl: string) {
    this.hubConnection = new HubConnection(`${this.originUrl}chat`);

    setInterval(() => {
      this.chatStatus = !this.chatStatus;
    },
      5000);

    this.hubConnection
      .start()
      .then(() => {
        this.hubConnection.on('send', (message: string) => {
          if (this.chatStatus) {
            //send message
          }
          this.hubConnection
            .invoke('messageReceived', this.chatStatus);
        });
      });

  }
}

As you can see on this code, I don't know what to do in the controller method and the Hub method to know that the method MessageReceive was called and to get his return to send it back to the controller request.

Hayha
  • 2,144
  • 1
  • 15
  • 27
  • Check my [answer](https://stackoverflow.com/a/75100129/4393935) as this is now possible using .NET 7. – knoxgon Jan 12 '23 at 17:44

1 Answers1

3

"with a little hacking around with connection metadata and TaskCompletionSource you could also probably make it look a lot like a method invocation returning a value."

Controller server:

Inject HttpConnectionManager.

// using Microsoft.AspNetCore.Http.Connections.Internal;

public async Task<IActionResult> SendMessage(string message)
{
    string connectionId = Request.Headers["connectionId"];

    var chatStatus = await Send(connectionId, message);

    return new OkObjectResult(new { Result = "OK", ChatStatus = chatStatus });
}

private async Task<bool> Send(string connectionId, string message)
{
    var tcs = new TaskCompletionSource<bool>();

    _connectionManager.TryGetConnection(connectionId, out HttpConnectionContext connection);

    connection.Items.Add("tcs", tcs);

    await _chatHubContext.Clients.Client(connectionId).SendAsync("send", message);

    var chatStatus = await tcs.Task;

    connection.Items.Remove("tcs");

    return chatStatus;
}

Hub server:

public Task MessageReceived(bool chatStatus)
{
    Context.Items.TryGetValue("tcs", out object obj);

    var tcs = (TaskCompletionSource<bool>)obj;

    tcs.SetResult(chatStatus);

    return Task.CompletedTask;
}

Angular 4 client:

// No change
aaron
  • 39,695
  • 6
  • 46
  • 102
  • Check for server-side exception: [#163](https://github.com/aspnet/SignalR/issues/163), [#1026](https://github.com/aspnet/SignalR/issues/1026) – aaron Jan 29 '18 at 15:28
  • Yes, It was just the wrong method name used. But your solution works really well and it's exactly what I was looking for. Thank you really much, you're my savior :) – Hayha Jan 29 '18 at 16:31
  • Hey again, this solution no longer works with aspnet/signalr version 1.1.0. Cause Microsoft.AspNetCore.Sockets is no longer used by signalR. Do you have any idea how to improve your answer to make it work with signalr 1.1.0? – Hayha Mar 04 '19 at 01:52
  • Updated solution for Microsoft.AspNetCore.SignalR 1.1.0. – aaron Mar 17 '19 at 11:31
  • Great, it work perfectly. I was so close but just missed the good package : Microsoft.AspNetCore.Http.Connections.Internal . Thank you again – Hayha Mar 18 '19 at 14:42
  • 1
    Check my [answer](https://stackoverflow.com/a/75100129/4393935) as this is now possible using .NET 7. – knoxgon Jan 12 '23 at 17:45