Skip to content

SignalR HubException does not send innerException property #12633

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Efreeto opened this issue Jul 26, 2019 · 6 comments
Closed

SignalR HubException does not send innerException property #12633

Efreeto opened this issue Jul 26, 2019 · 6 comments
Labels
area-signalr Includes: SignalR clients and servers
Milestone

Comments

@Efreeto
Copy link

Efreeto commented Jul 26, 2019

Describe the bug

HubException class has a constructor with innerException, but providing it does nothing, and client only sees message.

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core 2.2
  2. It doesn't matter whether EnableDetailedErrors option is true or false
  3. C# Server: throw new HubException("I am error", new DivideByZeroException("infinitely"));
  4. JS Client: Receives an error, which only has message and stack

Expected behavior

  1. JS Client: Receives an error, which has message, stack, innerException.

Additional context

I've seen many discussions from code owners saying "We've considered customized objects before, but we only send messages now", so maybe this is part of that decision. So is innerException just there as a noop, or can I send it somehow? Actually I want to send any custom object as an error.

message alone is not enough for me. Even though I own both server and client, I want to serve different cultures

For example,

  1. JS Client 1: Does something bad.
  2. C# Server: throw new MyException(123, 'A', 'B');
  3. JS Client 1: Receives error -> Looks up error code 123 for Language 1 -> Tells user "'A' is not found in 'B'"

But I also want to do,

  1. JS Client 2: Does something equally bad.
  2. C# Server: throw new MyException(123, 'A', 'B'); // same as above
  3. JS Client 2: Receives error -> Looks up error code 123 for Language 2 -> Tells user "'B' does not contain 'A'... desu"
@Efreeto
Copy link
Author

Efreeto commented Jul 26, 2019

Let me know if you want me to file the part in Additional context as a seperate feature request.

@Efreeto Efreeto changed the title HubException does not send innerException property SignalR HubException does not send innerException property Jul 26, 2019
@mkArtakMSFT mkArtakMSFT added the area-signalr Includes: SignalR clients and servers label Jul 29, 2019
@analogrelay
Copy link
Contributor

This is by design since the protocol is explicitly decoupled from the implementation. Either of the client or server could be sending messages from non-.NET applications (Java Client, Azure Functions server, etc.) so mapping things beyond the message is challenging. Our current design is to strongly advise that HubException be for truely exceptional circumstances that the client can't react to and should only report the issue. If you need to return structured error data, this is better suited to a return value with structured results indicating the error details.

Returning the stack trace is definitely not in our plan as there are security risks to exposing stack traces.

@Efreeto
Copy link
Author

Efreeto commented Aug 1, 2019 via email

@analogrelay analogrelay added this to the Discussions milestone Aug 1, 2019
@analogrelay
Copy link
Contributor

analogrelay commented Aug 1, 2019

Would it be truly exceptional circumstances if a server throws an error that a client using user should can know about and react?

Of course this is a design choice but in general the answer is no, this isn't an exceptional condition. In general, if the caller is expected to be able to react and recover, an Exception shouldn't be used. The .NET Best Practices for exceptions specifically try to avoid using Exceptions for cases the caller should be able to handle (https://docs.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#handle-common-conditions-without-throwing-exceptions).

We're also looking at adding the "pipeline" concept described in that doc (#5353) which will help with error handling and management. You wouldn't have to wrap every method in a try..catch for example, and would have some degree of customization over how the error is rendered.

We don't plan to serialize the entire exception though, to be clear. We can't do so in a way that guarantees the client will be able to deserialize it properly. While your application may be a tightly coupled .NET Server and .NET Client, this isn't something SignalR can expect in general. Adding things like the Hub Pipeline will enable you more control over this when you do have a tightly coupled app, but SignalR shouldn't be doing it in the general case.

@Efreeto
Copy link
Author

Efreeto commented Aug 1, 2019

Thank you so much for your explanation again Andrew. The design decision became more clear to me, and it will prepare us as we consider other methods to do this. We ultimately decided to send serialized error as a string.

By the way, the "pipeline" concept is a very welcome feature that was removed in the new version. For us, to at least remove the string "An error occurred on the server while streaming results.", I'd need to send the message with HubConnectionContext and invocation id. So I overrode DefaultHubDispatcher like this...:

    internal class MyHubDispatcher<T> : DefaultHubDispatcher<T>
    {
        public override Task DispatchMessageAsync(HubConnectionContext connection, HubMessage hubMessage)
        {
            if (hubMessage is StreamInvocationMessage streamInvocationMessage)
            {
                var args = streamInvocationMessage.Arguments;    // client sends two dummy args
                args[args.Length - 1] = streamInvocationMessage.InvocationId;
                args[args.Length - 2] = connection;
                return base.DispatchMessageAsync(connection, new StreamInvocationMessage(streamInvocationMessage.InvocationId, streamInvocationMessage.Target, args));    // pass two args to invoked methods so they can send more customized messages
            }

            return base.DispatchMessageAsync(connection, hubMessage);
        }
    }

But it became a little ugly. I will be definitely looking forward to this new feature in 3.0. :)

@Efreeto Efreeto closed this as completed Aug 1, 2019
@BrennanConroy
Copy link
Member

@Efreeto Just an FYI, that feature will not be in 3.0. It hasn't been officially planned for a specific release yet.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 2, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-signalr Includes: SignalR clients and servers
Projects
None yet
Development

No branches or pull requests

4 participants