FJvdM
FJvdM

Reputation: 135

How to identify SignalR core hub errors on the JavaScript client?

We are trying to setup error handling on a SignalR Core invoke method from a JavaScript client that needs to identify the error's type and act accordingly (e.g. if it is an authorization error, the user should be prompted to log in, etc.).

We've determined that the error returned from the hub contains a message and a stack property and have build the following that sets an error code based on the text contained in the message property: enter image description here

Will the error text always be returned in English (and can thus be used to identify the error)? Or is there a better way to go about achieving this?

We are using .Net Core 3.1 and @microsoft/signalr 3.1.10.

Upvotes: 4

Views: 1684

Answers (1)

UncooleBen
UncooleBen

Reputation: 121

According to aspnetcore/SignalR server-side code on GitHub, it seems that the invocation errors are indeed passed by string value.

The method responsible for send back error are defined as follow:

private async Task SendInvocationError(string invocationId, HubConnectionContext connection, string errorMessage)
{
    if (string.IsNullOrEmpty(invocationId))
    {
        return;
    }

    await connection.WriteAsync(CompletionMessage.WithError(invocationId, errorMessage));
}

And a few examples on how it is being called:

if (!await IsHubMethodAuthorized(scope.ServiceProvider, connection, descriptor, hubMethodInvocationMessage.Arguments, hub))
{
    Log.HubMethodNotAuthorized(_logger, hubMethodInvocationMessage.Target);
    await SendInvocationError(hubMethodInvocationMessage.InvocationId, connection,
        $"Failed to invoke '{hubMethodInvocationMessage.Target}' because user is unauthorized");
    return;
}
var errorMessage = ErrorMessageHelper.BuildErrorMessage($"Failed to invoke '{bindingFailureMessage.Target}' due to an error on the server.",
    bindingFailureMessage.BindingFailure.SourceException, _enableDetailedErrors);
return SendInvocationError(bindingFailureMessage.InvocationId, connection, errorMessage);

The only information about error is the string parameter of errorMessage.

On the other hand, the client-side javascript library source code:

HubConnection.prototype.connectionClosed = function (error) {
    this.logger.log(_ILogger__WEBPACK_IMPORTED_MODULE_2__["LogLevel"].Debug, "HubConnection.connectionClosed(" + error + ") called while in state " + this.connectionState + ".");
    // Triggering this.handshakeRejecter is insufficient because it could already be resolved without the continuation having run yet.
    this.stopDuringStartError = this.stopDuringStartError || error || new Error("The underlying connection was closed before the hub handshake could complete.");
    // If the handshake is in progress, start will be waiting for the handshake promise, so we complete it.
    // If it has already completed, this should just noop.
    if (this.handshakeResolver) {
        this.handshakeResolver();
    }
    this.cancelCallbacksWithError(error || new Error("Invocation canceled due to the underlying connection being closed."));
    
    ...
};

This indicates that "Invocation canceled due to the underlying connection being closed." is the default error message when none was provided by server.

Therefore, I believe if the SignalR team didn't change the error message sending mechanism, your string.includes approach is reasonable.

Upvotes: 1

Related Questions