[Blazor][Fixes #12056] Avoid producing warnings for bad inputs.
* Does not produce warnings when receiving invalid event arguments. * Does not produce warnings when receiving an invalid event handler id. * Does not produce warnings when receiving an ack for an unknown batch.
This commit is contained in:
parent
e7a1dc620b
commit
74c09470c6
|
|
@ -213,7 +213,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
|
|||
|
||||
if (!_eventBindings.TryGetValue(eventHandlerId, out var callback))
|
||||
{
|
||||
throw new ArgumentException($"There is no event handler with ID {eventHandlerId}");
|
||||
throw new ArgumentException($"There is no event handler associated with this event. EventId: '{eventHandlerId}'.", nameof(eventHandlerId));
|
||||
}
|
||||
|
||||
Log.HandlingEvent(_logger, eventHandlerId, eventArgs);
|
||||
|
|
|
|||
|
|
@ -2883,7 +2883,7 @@ namespace Microsoft.AspNetCore.Components.Test
|
|||
{
|
||||
return renderer.DispatchEventAsync(eventHandlerId, new EventArgs());
|
||||
});
|
||||
Assert.Equal($"There is no event handler with ID {eventHandlerId}", ex.Message);
|
||||
Assert.Contains($"There is no event handler associated with this event. EventId: '{eventHandlerId}'.", ex.Message);
|
||||
Assert.Equal(2, numEventsFired);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,24 +27,9 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current <see cref="Circuits.Circuit"/>.
|
||||
/// Sets the <see cref="IJSRuntime"/> for the current excution context.
|
||||
/// </summary>
|
||||
/// <param name="circuitHost">The <see cref="Circuits.Circuit"/>.</param>
|
||||
/// <remarks>
|
||||
/// Calling <see cref="SetCurrentCircuitHost(CircuitHost)"/> will store related values such as the
|
||||
/// <see cref="IJSRuntime"/> and <see cref="Renderer"/>
|
||||
/// in the local execution context. Application code should not need to call this method,
|
||||
/// it is primarily used by the Server-Side Components infrastructure.
|
||||
/// </remarks>
|
||||
public static void SetCurrentCircuitHost(CircuitHost circuitHost)
|
||||
{
|
||||
if (circuitHost is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(circuitHost));
|
||||
}
|
||||
|
||||
JSInterop.JSRuntime.SetCurrentJSRuntime(circuitHost.JSRuntime);
|
||||
}
|
||||
public void SetCurrentJSRuntime() => JSInterop.JSRuntime.SetCurrentJSRuntime(JSRuntime);
|
||||
|
||||
// This event is fired when there's an unrecoverable exception coming from the circuit, and
|
||||
// it need so be torn down. The registry listens to this even so that the circuit can
|
||||
|
|
@ -116,7 +101,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
|
||||
try
|
||||
{
|
||||
SetCurrentCircuitHost(this);
|
||||
SetCurrentJSRuntime();
|
||||
_initialized = true; // We're ready to accept incoming JSInterop calls from here on
|
||||
|
||||
await OnCircuitOpenedAsync(cancellationToken);
|
||||
|
|
@ -342,6 +327,28 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
}
|
||||
}
|
||||
|
||||
// Called by the client when it completes rendering a batch.
|
||||
// OnRenderCompletedAsync is used in a fire-and-forget context, so it's responsible for its own
|
||||
// error handling.
|
||||
public async Task OnRenderCompletedAsync(long renderId, string errorMessageOrNull)
|
||||
{
|
||||
AssertInitialized();
|
||||
AssertNotDisposed();
|
||||
|
||||
try
|
||||
{
|
||||
_ = Renderer.OnRenderCompletedAsync(renderId, errorMessageOrNull);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Captures sync exceptions when invoking OnRenderCompletedAsync.
|
||||
// An exception might be throw synchronously when we receive an ack for a batch we never produced.
|
||||
Log.OnRenderCompletedFailed(_logger, renderId, CircuitId, e);
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(e, $"Failed to complete render batch '{renderId}'."));
|
||||
UnhandledException(this, new UnhandledExceptionEventArgs(e, isTerminating: false));
|
||||
}
|
||||
}
|
||||
|
||||
// BeginInvokeDotNetFromJS is used in a fire-and-forget context, so it's responsible for its own
|
||||
// error handling.
|
||||
public async Task BeginInvokeDotNetFromJS(string callId, string assemblyName, string methodIdentifier, long dotNetObjectId, string argsJson)
|
||||
|
|
@ -353,7 +360,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
{
|
||||
await Renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
SetCurrentCircuitHost(this);
|
||||
SetCurrentJSRuntime();
|
||||
Log.BeginInvokeDotNet(_logger, callId, assemblyName, methodIdentifier, dotNetObjectId);
|
||||
DotNetDispatcher.BeginInvoke(callId, assemblyName, methodIdentifier, dotNetObjectId, argsJson);
|
||||
});
|
||||
|
|
@ -363,10 +370,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
// We don't expect any of this code to actually throw, because DotNetDispatcher.BeginInvoke doesn't throw
|
||||
// however, we still want this to get logged if we do.
|
||||
Log.BeginInvokeDotNetFailed(_logger, callId, assemblyName, methodIdentifier, dotNetObjectId, ex);
|
||||
if (Client.Connected)
|
||||
{
|
||||
await NotifyClientError(Client, "Interop call failed.");
|
||||
}
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, "Interop call failed."));
|
||||
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
|
||||
}
|
||||
}
|
||||
|
|
@ -382,7 +386,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
{
|
||||
await Renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
SetCurrentCircuitHost(this);
|
||||
SetCurrentJSRuntime();
|
||||
if (!succeded)
|
||||
{
|
||||
// We can log the arguments here because it is simply the JS error with the call stack.
|
||||
|
|
@ -401,10 +405,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
// An error completing JS interop means that the user sent invalid data, a well-behaved
|
||||
// client won't do this.
|
||||
Log.EndInvokeDispatchException(_logger, ex);
|
||||
if (Client.Connected)
|
||||
{
|
||||
await NotifyClientError(Client, "Invalid interop arguments.");
|
||||
}
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, "Invalid interop arguments."));
|
||||
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
|
||||
}
|
||||
}
|
||||
|
|
@ -419,17 +420,13 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
WebEventData webEventData;
|
||||
try
|
||||
{
|
||||
AssertInitialized();
|
||||
webEventData = WebEventData.Parse(eventDescriptorJson, eventArgsJson);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Invalid event data is fatal. We expect a well-behaved client to send valid JSON.
|
||||
Log.DispatchEventFailedToParseEventData(_logger, ex);
|
||||
if (Client.Connected)
|
||||
{
|
||||
await NotifyClientError(Client, "Invalid event data.");
|
||||
}
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, "Bad input data."));
|
||||
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
|
||||
return;
|
||||
}
|
||||
|
|
@ -438,7 +435,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
{
|
||||
await Renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
SetCurrentCircuitHost(this);
|
||||
SetCurrentJSRuntime();
|
||||
return Renderer.DispatchEventAsync(
|
||||
webEventData.EventHandlerId,
|
||||
webEventData.EventFieldInfo,
|
||||
|
|
@ -450,10 +447,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
// A failure in dispatching an event means that it was an attempt to use an invalid event id.
|
||||
// A well-behaved client won't do this.
|
||||
Log.DispatchEventFailedToDispatchEvent(_logger, webEventData.EventHandlerId.ToString(), ex);
|
||||
if (Client.Connected)
|
||||
{
|
||||
await NotifyClientError(Client, "Failed to dispatch event.");
|
||||
}
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, "Failed to dispatch event."));
|
||||
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
|
||||
}
|
||||
}
|
||||
|
|
@ -469,7 +463,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
{
|
||||
await Renderer.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
SetCurrentCircuitHost(this);
|
||||
SetCurrentJSRuntime();
|
||||
Log.LocationChange(_logger, uri, CircuitId);
|
||||
var navigationManager = (RemoteNavigationManager)Services.GetRequiredService<NavigationManager>();
|
||||
navigationManager.NotifyLocationChanged(uri, intercepted);
|
||||
|
|
@ -491,7 +485,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
// LocationChangeException means that it failed in user-code. Treat this like an unhandled
|
||||
// exception in user-code.
|
||||
Log.LocationChangeFailedInCircuit(_logger, uri, CircuitId, nex);
|
||||
await ReportUnhandledException(nex);
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(nex, "Location change failed."));
|
||||
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(nex, isTerminating: false));
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -499,10 +493,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
// Any other exception means that it failed validation, or inside the NavigationManager. Treat
|
||||
// this like bad data.
|
||||
Log.LocationChangeFailed(_logger, uri, CircuitId, ex);
|
||||
if (Client.Connected)
|
||||
{
|
||||
await NotifyClientError(Client, $"Location change to {uri} failed.");
|
||||
}
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(ex, $"Location change to '{uri}' failed."));
|
||||
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, isTerminating: false));
|
||||
}
|
||||
}
|
||||
|
|
@ -527,7 +518,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
|
||||
// Dispatch any buffered renders we accumulated during a disconnect.
|
||||
// Note that while the rendering is async, we cannot await it here. The Task returned by ProcessBufferedRenderBatches relies on
|
||||
// OnRenderCompleted to be invoked to complete, and SignalR does not allow concurrent hub method invocations.
|
||||
// OnRenderCompletedAsync to be invoked to complete, and SignalR does not allow concurrent hub method invocations.
|
||||
_ = Renderer.Dispatcher.InvokeAsync(() => Renderer.ProcessBufferedRenderBatches());
|
||||
}
|
||||
|
||||
|
|
@ -566,39 +557,50 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
private async Task ReportUnhandledException(Exception exception)
|
||||
{
|
||||
Log.CircuitUnhandledException(_logger, CircuitId, exception);
|
||||
|
||||
await TryNotifyClientErrorAsync(Client, GetClientErrorMessage(exception), exception);
|
||||
}
|
||||
|
||||
private string GetClientErrorMessage(Exception exception, string additionalInformation = null)
|
||||
{
|
||||
if (_options.DetailedErrors)
|
||||
{
|
||||
return exception.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return
|
||||
$"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
|
||||
$"detailed exceptions in '{typeof(CircuitOptions).Name}.{nameof(CircuitOptions.DetailedErrors)}'. {additionalInformation}";
|
||||
}
|
||||
}
|
||||
|
||||
// exception is only populated when either the renderer or the synchronization context signal exceptions.
|
||||
// In other cases it is null and should never be sent to the client.
|
||||
// error contains the information to send to the client.
|
||||
private async Task TryNotifyClientErrorAsync(IClientProxy client, string error, Exception exception = null)
|
||||
{
|
||||
if (!Client.Connected)
|
||||
{
|
||||
_logger.LogDebug("Client is disconnected YO.");
|
||||
Log.UnhandledExceptionClientDisconnected(
|
||||
_logger,
|
||||
CircuitId,
|
||||
exception);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_options.DetailedErrors)
|
||||
{
|
||||
await NotifyClientError(Client, exception.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
var message =
|
||||
$"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
|
||||
$"detailed exceptions in '{typeof(CircuitOptions).Name}.{nameof(CircuitOptions.DetailedErrors)}'";
|
||||
await NotifyClientError(Client, message);
|
||||
}
|
||||
Log.CircuitTransmittingClientError(_logger, CircuitId);
|
||||
await client.SendAsync("JS.Error", error);
|
||||
Log.CircuitTransmittedClientErrorSuccess(_logger, CircuitId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.CircuitUnhandledExceptionFailed(_logger, CircuitId, ex);
|
||||
Log.CircuitTransmitErrorFailed(_logger, CircuitId, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NotifyClientError(IClientProxy client, string error)
|
||||
{
|
||||
_logger.LogDebug("About to notify of an error");
|
||||
await client.SendAsync("JS.Error", error);
|
||||
_logger.LogDebug("Completed notify of an error");
|
||||
}
|
||||
|
||||
private static class Log
|
||||
{
|
||||
private static readonly Action<ILogger, Exception> _intializationStarted;
|
||||
|
|
@ -613,7 +615,10 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
private static readonly Action<ILogger, string, Exception> _onCircuitClosed;
|
||||
private static readonly Action<ILogger, Type, string, string, Exception> _circuitHandlerFailed;
|
||||
private static readonly Action<ILogger, string, Exception> _circuitUnhandledException;
|
||||
private static readonly Action<ILogger, string, Exception> _circuitUnhandledExceptionFailed;
|
||||
private static readonly Action<ILogger, string, Exception> _circuitTransmittingClientError;
|
||||
private static readonly Action<ILogger, string, Exception> _circuitTransmittedClientErrorSuccess;
|
||||
private static readonly Action<ILogger, string, Exception> _circuitTransmitErrorFailed;
|
||||
private static readonly Action<ILogger, string, Exception> _unhandledExceptionClientDisconnected;
|
||||
|
||||
private static readonly Action<ILogger, string, string, string, Exception> _beginInvokeDotNetStatic;
|
||||
private static readonly Action<ILogger, string, long, string, Exception> _beginInvokeDotNetInstance;
|
||||
|
|
@ -628,6 +633,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
private static readonly Action<ILogger, string, string, Exception> _locationChangeSucceeded;
|
||||
private static readonly Action<ILogger, string, string, Exception> _locationChangeFailed;
|
||||
private static readonly Action<ILogger, string, string, Exception> _locationChangeFailedInCircuit;
|
||||
private static readonly Action<ILogger, long, string, Exception> _onRenderCompletedFailed;
|
||||
|
||||
private static class EventIds
|
||||
{
|
||||
|
|
@ -644,7 +650,10 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
public static readonly EventId OnCircuitClosed = new EventId(109, "OnCircuitClosed");
|
||||
public static readonly EventId CircuitHandlerFailed = new EventId(110, "CircuitHandlerFailed");
|
||||
public static readonly EventId CircuitUnhandledException = new EventId(111, "CircuitUnhandledException");
|
||||
public static readonly EventId CircuitUnhandledExceptionFailed = new EventId(112, "CircuitUnhandledExceptionFailed");
|
||||
public static readonly EventId CircuitTransmittingClientError = new EventId(112, "CircuitTransmittingClientError");
|
||||
public static readonly EventId CircuitTransmittedClientErrorSuccess = new EventId(113, "CircuitTransmittedClientErrorSuccess");
|
||||
public static readonly EventId CircuitTransmitErrorFailed = new EventId(114, "CircuitTransmitErrorFailed");
|
||||
public static readonly EventId UnhandledExceptionClientDisconnected = new EventId(115, "UnhandledExceptionClientDisconnected");
|
||||
|
||||
// 200s used for interactive stuff
|
||||
public static readonly EventId DispatchEventFailedToParseEventData = new EventId(200, "DispatchEventFailedToParseEventData");
|
||||
|
|
@ -659,6 +668,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
public static readonly EventId LocationChangeSucceded = new EventId(209, "LocationChangeSucceeded");
|
||||
public static readonly EventId LocationChangeFailed = new EventId(210, "LocationChangeFailed");
|
||||
public static readonly EventId LocationChangeFailedInCircuit = new EventId(211, "LocationChangeFailedInCircuit");
|
||||
public static readonly EventId OnRenderCompletedFailed = new EventId(212, " OnRenderCompletedFailed");
|
||||
}
|
||||
|
||||
static Log()
|
||||
|
|
@ -666,87 +676,102 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
_intializationStarted = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
EventIds.InitializationFailed,
|
||||
"Circuit initialization started");
|
||||
"Circuit initialization started.");
|
||||
|
||||
_intializationSucceded = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
EventIds.InitializationFailed,
|
||||
"Circuit initialization succeeded");
|
||||
"Circuit initialization succeeded.");
|
||||
|
||||
_intializationFailed = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
EventIds.InitializationFailed,
|
||||
"Circuit initialization failed");
|
||||
"Circuit initialization failed.");
|
||||
|
||||
_disposeStarted = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.DisposeStarted,
|
||||
"Disposing circuit {CircuitId} started");
|
||||
"Disposing circuit '{CircuitId}' started.");
|
||||
|
||||
_disposeSucceded = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.DisposeSucceeded,
|
||||
"Disposing circuit {CircuitId} succeded");
|
||||
"Disposing circuit '{CircuitId}' succeded.");
|
||||
|
||||
_disposeFailed = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.DisposeFailed,
|
||||
"Disposing circuit {CircuitId} failed");
|
||||
"Disposing circuit '{CircuitId}' failed.");
|
||||
|
||||
_onCircuitOpened = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.OnCircuitOpened,
|
||||
"Opening circuit with id {CircuitId}.");
|
||||
"Opening circuit with id '{CircuitId}'.");
|
||||
|
||||
_onConnectionUp = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.OnConnectionUp,
|
||||
"Circuit id {CircuitId} connected using connection {ConnectionId}.");
|
||||
"Circuit id '{CircuitId}' connected using connection '{ConnectionId}'.");
|
||||
|
||||
_onConnectionDown = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.OnConnectionDown,
|
||||
"Circuit id {CircuitId} disconnected from connection {ConnectionId}.");
|
||||
"Circuit id '{CircuitId}' disconnected from connection '{ConnectionId}'.");
|
||||
|
||||
_onCircuitClosed = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.OnCircuitClosed,
|
||||
"Closing circuit with id {CircuitId}.");
|
||||
"Closing circuit with id '{CircuitId}'.");
|
||||
|
||||
_circuitHandlerFailed = LoggerMessage.Define<Type, string, string>(
|
||||
LogLevel.Error,
|
||||
EventIds.CircuitHandlerFailed,
|
||||
"Unhandled error invoking circuit handler type {handlerType}.{handlerMethod}: {Message}");
|
||||
|
||||
_circuitUnhandledException = LoggerMessage.Define<string>(
|
||||
LogLevel.Error,
|
||||
EventIds.CircuitUnhandledException,
|
||||
"Unhandled exception in circuit {CircuitId}");
|
||||
_circuitUnhandledException = LoggerMessage.Define<string>(
|
||||
LogLevel.Error,
|
||||
EventIds.CircuitUnhandledException,
|
||||
"Unhandled exception in circuit '{CircuitId}'.");
|
||||
|
||||
_circuitUnhandledExceptionFailed = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.CircuitUnhandledExceptionFailed,
|
||||
"Failed to transmit exception to client in circuit {CircuitId}");
|
||||
_circuitTransmittingClientError = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.CircuitTransmittingClientError,
|
||||
"About to notify client of an error in circuit '{CircuitId}'.");
|
||||
|
||||
_circuitTransmittedClientErrorSuccess = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.CircuitTransmittedClientErrorSuccess,
|
||||
"Successfully transmitted error to client in circuit '{CircuitId}'.");
|
||||
|
||||
_circuitTransmitErrorFailed = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.CircuitTransmitErrorFailed,
|
||||
"Failed to transmit exception to client in circuit '{CircuitId}'.");
|
||||
|
||||
_unhandledExceptionClientDisconnected = LoggerMessage.Define<string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.UnhandledExceptionClientDisconnected,
|
||||
"An exception ocurred on the circuit host '{CircuitId}' while the client is disconnected.");
|
||||
|
||||
_beginInvokeDotNetStatic = LoggerMessage.Define<string, string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.BeginInvokeDotNet,
|
||||
"Invoking static method with identifier '{MethodIdentifier}' on assembly '{Assembly}' with callback id '{CallId}'");
|
||||
"Invoking static method with identifier '{MethodIdentifier}' on assembly '{Assembly}' with callback id '{CallId}'.");
|
||||
|
||||
_beginInvokeDotNetInstance = LoggerMessage.Define<string, long, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.BeginInvokeDotNet,
|
||||
"Invoking instance method '{MethodIdentifier}' on instance '{DotNetObjectId}' with callback id '{CallId}'");
|
||||
"Invoking instance method '{MethodIdentifier}' on instance '{DotNetObjectId}' with callback id '{CallId}'.");
|
||||
|
||||
_beginInvokeDotNetStaticFailed = LoggerMessage.Define<string, string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.BeginInvokeDotNetFailed,
|
||||
"Failed to invoke static method with identifier '{MethodIdentifier}' on assembly '{Assembly}' with callback id '{CallId}'");
|
||||
"Failed to invoke static method with identifier '{MethodIdentifier}' on assembly '{Assembly}' with callback id '{CallId}'.");
|
||||
|
||||
_beginInvokeDotNetInstanceFailed = LoggerMessage.Define<string, long, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.BeginInvokeDotNetFailed,
|
||||
"Failed to invoke instance method '{MethodIdentifier}' on instance '{DotNetObjectId}' with callback id '{CallId}'");
|
||||
"Failed to invoke instance method '{MethodIdentifier}' on instance '{DotNetObjectId}' with callback id '{CallId}'.");
|
||||
|
||||
_endInvokeDispatchException = LoggerMessage.Define(
|
||||
LogLevel.Debug,
|
||||
|
|
@ -776,25 +801,30 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
_locationChange = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.LocationChange,
|
||||
"Location changing to {URI} in circuit {CircuitId}.");
|
||||
"Location changing to {URI} in circuit '{CircuitId}'.");
|
||||
|
||||
_locationChangeSucceeded = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.LocationChangeSucceded,
|
||||
"Location change to {URI} in circuit {CircuitId} succeded.");
|
||||
"Location change to '{URI}' in circuit '{CircuitId}' succeded.");
|
||||
|
||||
_locationChangeFailed = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.LocationChangeFailed,
|
||||
"Location change to {URI} in circuit {CircuitId} failed.");
|
||||
"Location change to '{URI}' in circuit '{CircuitId}' failed.");
|
||||
|
||||
_locationChangeFailedInCircuit = LoggerMessage.Define<string, string>(
|
||||
LogLevel.Error,
|
||||
EventIds.LocationChangeFailed,
|
||||
"Location change to {URI} in circuit {CircuitId} failed.");
|
||||
"Location change to '{URI}' in circuit '{CircuitId}' failed.");
|
||||
|
||||
_onRenderCompletedFailed = LoggerMessage.Define<long, string>(
|
||||
LogLevel.Debug,
|
||||
EventIds.OnRenderCompletedFailed,
|
||||
"Failed to complete render batch '{RenderId}' in circuit host '{CircuitId}'.");
|
||||
}
|
||||
|
||||
public static void InitializationStarted(ILogger logger) =>_intializationStarted(logger, null);
|
||||
public static void InitializationStarted(ILogger logger) => _intializationStarted(logger, null);
|
||||
public static void InitializationSucceeded(ILogger logger) => _intializationSucceded(logger, null);
|
||||
public static void InitializationFailed(ILogger logger, Exception exception) => _intializationFailed(logger, exception);
|
||||
public static void DisposeStarted(ILogger logger, string circuitId) => _disposeStarted(logger, circuitId, null);
|
||||
|
|
@ -802,7 +832,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
public static void DisposeFailed(ILogger logger, string circuitId, Exception exception) => _disposeFailed(logger, circuitId, exception);
|
||||
public static void CircuitOpened(ILogger logger, string circuitId) => _onCircuitOpened(logger, circuitId, null);
|
||||
public static void ConnectionUp(ILogger logger, string circuitId, string connectionId) => _onConnectionUp(logger, circuitId, connectionId, null);
|
||||
public static void ConnectionDown(ILogger logger, string circuitId, string connectionId) => _onConnectionDown(logger, circuitId, connectionId, null);
|
||||
public static void ConnectionDown(ILogger logger, string circuitId, string connectionId) => _onConnectionDown(logger, circuitId, connectionId, null);
|
||||
public static void CircuitClosed(ILogger logger, string circuitId) => _onCircuitClosed(logger, circuitId, null);
|
||||
|
||||
public static void CircuitHandlerFailed(ILogger logger, CircuitHandler handler, string handlerMethod, Exception exception)
|
||||
|
|
@ -816,7 +846,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
}
|
||||
|
||||
public static void CircuitUnhandledException(ILogger logger, string circuitId, Exception exception) => _circuitUnhandledException(logger, circuitId, exception);
|
||||
public static void CircuitUnhandledExceptionFailed(ILogger logger, string circuitId, Exception exception) => _circuitUnhandledExceptionFailed(logger, circuitId, exception);
|
||||
public static void CircuitTransmitErrorFailed(ILogger logger, string circuitId, Exception exception) => _circuitTransmitErrorFailed(logger, circuitId, exception);
|
||||
public static void EndInvokeDispatchException(ILogger logger, Exception ex) => _endInvokeDispatchException(logger, ex);
|
||||
public static void EndInvokeJSFailed(ILogger logger, long asyncHandle, string arguments) => _endInvokeJSFailed(logger, asyncHandle, arguments, null);
|
||||
public static void EndInvokeJSSucceeded(ILogger logger, long asyncCall) => _endInvokeJSSucceeded(logger, asyncCall, null);
|
||||
|
|
@ -851,6 +881,10 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
public static void LocationChangeSucceeded(ILogger logger, string uri, string circuitId) => _locationChangeSucceeded(logger, uri, circuitId, null);
|
||||
public static void LocationChangeFailed(ILogger logger, string uri, string circuitId, Exception exception) => _locationChangeFailed(logger, uri, circuitId, exception);
|
||||
public static void LocationChangeFailedInCircuit(ILogger logger, string uri, string circuitId, Exception exception) => _locationChangeFailedInCircuit(logger, uri, circuitId, exception);
|
||||
public static void UnhandledExceptionClientDisconnected(ILogger logger, string circuitId, Exception exception) => _unhandledExceptionClientDisconnected(logger, circuitId, exception);
|
||||
public static void CircuitTransmittingClientError(ILogger logger, string circuitId) => _circuitTransmittingClientError(logger, circuitId, null);
|
||||
public static void CircuitTransmittedClientErrorSuccess(ILogger logger, string circuitId) => _circuitTransmittedClientErrorSuccess(logger, circuitId, null);
|
||||
public static void OnRenderCompletedFailed(ILogger logger, long renderId, string circuitId, Exception e) => _onRenderCompletedFailed(logger, renderId, circuitId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
// disposed.
|
||||
}
|
||||
|
||||
public Task OnRenderCompleted(long incomingBatchId, string errorMessageOrNull)
|
||||
public Task OnRenderCompletedAsync(long incomingBatchId, string errorMessageOrNull)
|
||||
{
|
||||
if (_disposing)
|
||||
{
|
||||
|
|
@ -273,9 +273,9 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
|
||||
if (lastBatchId < incomingBatchId)
|
||||
{
|
||||
HandleException(
|
||||
new InvalidOperationException($"Received an acknowledgement for batch with id '{incomingBatchId}' when the last batch produced was '{lastBatchId}'."));
|
||||
return Task.CompletedTask;
|
||||
// This exception is due to a bad client input, so we mark it as such to prevent loging it as a warning and
|
||||
// flooding the logs with warnings.
|
||||
throw new InvalidOperationException($"Received an acknowledgement for batch with id '{incomingBatchId}' when the last batch produced was '{lastBatchId}'.");
|
||||
}
|
||||
|
||||
// Normally we will not have pending renders, but it might happen that we reached the limit of
|
||||
|
|
@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
}
|
||||
else
|
||||
{
|
||||
pendingRenderInfo.TrySetException(new RemoteRendererException(errorMessageOrNull));
|
||||
pendingRenderInfo.TrySetException(new InvalidOperationException(errorMessageOrNull));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.Web.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an exception related to remote rendering.
|
||||
/// </summary>
|
||||
internal class RemoteRendererException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs an instance of <see cref="RemoteRendererException"/>.
|
||||
/// </summary>
|
||||
/// <param name="message">The exception message.</param>
|
||||
public RemoteRendererException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,6 @@ using System.Runtime.CompilerServices;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Server.Circuits;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
|
@ -203,7 +201,7 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
}
|
||||
|
||||
Log.ReceivedConfirmationForBatch(_logger, renderId);
|
||||
_ = circuitHost.Renderer.OnRenderCompleted(renderId, errorMessageOrNull);
|
||||
_ = circuitHost.OnRenderCompletedAsync(renderId, errorMessageOrNull);
|
||||
}
|
||||
|
||||
public async ValueTask OnLocationChanged(string uri, bool intercepted)
|
||||
|
|
@ -281,29 +279,15 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
private static readonly Action<ILogger, string, Exception> _circuitHostShutdown =
|
||||
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(6, "CircuitHostShutdown"), "Call to '{CallSite}' received after the circuit was shut down");
|
||||
|
||||
private static readonly Action<ILogger, string, Exception> _circuitTerminatedGracefully =
|
||||
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(7, "CircuitTerminatedGracefully"), "Circuit '{CircuitId}' terminated gracefully");
|
||||
|
||||
private static readonly Action<ILogger, string, Exception> _invalidInputData =
|
||||
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(8, "InvalidInputData"), "Call to '{CallSite}' received invalid input data");
|
||||
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(7, "InvalidInputData"), "Call to '{CallSite}' received invalid input data");
|
||||
|
||||
private static readonly Action<ILogger, Exception> _circuitInitializationFailed =
|
||||
LoggerMessage.Define(LogLevel.Debug, new EventId(9, "CircuitInitializationFailed"), "Circuit initialization failed");
|
||||
LoggerMessage.Define(LogLevel.Debug, new EventId(8, "CircuitInitializationFailed"), "Circuit initialization failed");
|
||||
|
||||
public static void NoComponentsRegisteredInEndpoint(ILogger logger, string endpointDisplayName)
|
||||
{
|
||||
_noComponentsRegisteredInEndpoint(logger, endpointDisplayName, null);
|
||||
}
|
||||
public static void NoComponentsRegisteredInEndpoint(ILogger logger, string endpointDisplayName) => _noComponentsRegisteredInEndpoint(logger, endpointDisplayName, null);
|
||||
|
||||
public static void ReceivedConfirmationForBatch(ILogger logger, long batchId)
|
||||
{
|
||||
_receivedConfirmationForBatch(logger, batchId, null);
|
||||
}
|
||||
|
||||
public static void UnhandledExceptionInCircuit(ILogger logger, string circuitId, Exception exception)
|
||||
{
|
||||
_unhandledExceptionInCircuit(logger, circuitId, exception);
|
||||
}
|
||||
public static void ReceivedConfirmationForBatch(ILogger logger, long batchId) => _receivedConfirmationForBatch(logger, batchId, null);
|
||||
|
||||
public static void CircuitAlreadyInitialized(ILogger logger, string circuitId) => _circuitAlreadyInitialized(logger, circuitId, null);
|
||||
|
||||
|
|
@ -311,8 +295,6 @@ namespace Microsoft.AspNetCore.Components.Server
|
|||
|
||||
public static void CircuitHostShutdown(ILogger logger, [CallerMemberName] string callSite = "") => _circuitHostShutdown(logger, callSite, null);
|
||||
|
||||
public static void CircuitTerminatedGracefully(ILogger logger, string circuitId) => _circuitTerminatedGracefully(logger, circuitId, null);
|
||||
|
||||
public static void InvalidInputData(ILogger logger, [CallerMemberName] string callSite = "") => _invalidInputData(logger, callSite, null);
|
||||
|
||||
public static void CircuitInitializationFailed(ILogger logger, Exception exception) => _circuitInitializationFailed(logger, exception);
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
component.TriggerRender();
|
||||
}
|
||||
|
||||
await renderer.OnRenderCompleted(2, null);
|
||||
await renderer.OnRenderCompletedAsync(2, null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(9, renderer._unacknowledgedRenderBatches.Count);
|
||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
}
|
||||
Assert.Equal(10, renderer._unacknowledgedRenderBatches.Count);
|
||||
|
||||
await renderer.OnRenderCompleted(2, null);
|
||||
await renderer.OnRenderCompletedAsync(2, null);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(10, renderer._unacknowledgedRenderBatches.Count);
|
||||
|
|
@ -153,7 +153,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
|
||||
var componentId = renderer.AssignRootComponentId(component);
|
||||
component.TriggerRender();
|
||||
_ = renderer.OnRenderCompleted(2, null);
|
||||
_ = renderer.OnRenderCompletedAsync(2, null);
|
||||
|
||||
@event.Reset();
|
||||
firstBatchTCS.SetResult(null);
|
||||
|
|
@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
|
||||
foreach (var id in renderIds.ToArray())
|
||||
{
|
||||
_ = renderer.OnRenderCompleted(id, null);
|
||||
_ = renderer.OnRenderCompletedAsync(id, null);
|
||||
}
|
||||
|
||||
secondBatchTCS.SetResult(null);
|
||||
|
|
@ -234,14 +234,14 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
};
|
||||
|
||||
// Receive the ack for the intial batch
|
||||
_ = renderer.OnRenderCompleted(2, null);
|
||||
_ = renderer.OnRenderCompletedAsync(2, null);
|
||||
// Receive the ack for the second batch
|
||||
_ = renderer.OnRenderCompleted(3, null);
|
||||
_ = renderer.OnRenderCompletedAsync(3, null);
|
||||
|
||||
firstBatchTCS.SetResult(null);
|
||||
secondBatchTCS.SetResult(null);
|
||||
// Repeat the ack for the third batch
|
||||
_ = renderer.OnRenderCompleted(3, null);
|
||||
_ = renderer.OnRenderCompletedAsync(3, null);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(exceptions);
|
||||
|
|
@ -297,14 +297,14 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
};
|
||||
|
||||
// Receive the ack for the intial batch
|
||||
_ = renderer.OnRenderCompleted(2, null);
|
||||
_ = renderer.OnRenderCompletedAsync(2, null);
|
||||
// Receive the ack for the second batch
|
||||
_ = renderer.OnRenderCompleted(2, null);
|
||||
_ = renderer.OnRenderCompletedAsync(2, null);
|
||||
|
||||
firstBatchTCS.SetResult(null);
|
||||
secondBatchTCS.SetResult(null);
|
||||
// Repeat the ack for the third batch
|
||||
_ = renderer.OnRenderCompleted(3, null);
|
||||
_ = renderer.OnRenderCompletedAsync(3, null);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(exceptions);
|
||||
|
|
@ -358,7 +358,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
};
|
||||
|
||||
// Pretend that we missed the ack for the initial batch
|
||||
_ = renderer.OnRenderCompleted(3, null);
|
||||
_ = renderer.OnRenderCompletedAsync(3, null);
|
||||
firstBatchTCS.SetResult(null);
|
||||
secondBatchTCS.SetResult(null);
|
||||
|
||||
|
|
@ -414,12 +414,11 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
exceptions.Add(e);
|
||||
};
|
||||
|
||||
_ = renderer.OnRenderCompleted(4, null);
|
||||
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => renderer.OnRenderCompletedAsync(4, null));
|
||||
firstBatchTCS.SetResult(null);
|
||||
secondBatchTCS.SetResult(null);
|
||||
|
||||
// Assert
|
||||
var exception = Assert.Single(exceptions);
|
||||
Assert.Equal(
|
||||
"Received an acknowledgement for batch with id '4' when the last batch produced was '3'.",
|
||||
exception.Message);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,20 @@ namespace Microsoft.AspNetCore.Components.Web
|
|||
{
|
||||
// This class represents the second half of parsing incoming event data,
|
||||
// once the type of the eventArgs becomes known.
|
||||
|
||||
public static WebEventData Parse(string eventDescriptorJson, string eventArgsJson)
|
||||
{
|
||||
WebEventDescriptor eventDescriptor;
|
||||
try
|
||||
{
|
||||
eventDescriptor = Deserialize<WebEventDescriptor>(eventDescriptorJson);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidOperationException("Error parsing the event descriptor", e);
|
||||
}
|
||||
|
||||
return Parse(
|
||||
Deserialize<WebEventDescriptor>(eventDescriptorJson),
|
||||
eventDescriptor,
|
||||
eventArgsJson);
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +34,7 @@ namespace Microsoft.AspNetCore.Components.Web
|
|||
eventDescriptor.BrowserRendererId,
|
||||
eventDescriptor.EventHandlerId,
|
||||
InterpretEventFieldInfo(eventDescriptor.EventFieldInfo),
|
||||
ParseEventArgsJson(eventDescriptor.EventArgsType, eventArgsJson));
|
||||
ParseEventArgsJson(eventDescriptor.EventHandlerId, eventDescriptor.EventArgsType, eventArgsJson));
|
||||
}
|
||||
|
||||
private WebEventData(int browserRendererId, ulong eventHandlerId, EventFieldInfo eventFieldInfo, EventArgs eventArgs)
|
||||
|
|
@ -44,43 +53,34 @@ namespace Microsoft.AspNetCore.Components.Web
|
|||
|
||||
public EventArgs EventArgs { get; }
|
||||
|
||||
private static EventArgs ParseEventArgsJson(string eventArgsType, string eventArgsJson)
|
||||
private static EventArgs ParseEventArgsJson(ulong eventHandlerId, string eventArgsType, string eventArgsJson)
|
||||
{
|
||||
switch (eventArgsType)
|
||||
try
|
||||
{
|
||||
case "change":
|
||||
return DeserializeChangeEventArgs(eventArgsJson);
|
||||
case "clipboard":
|
||||
return Deserialize<UIClipboardEventArgs>(eventArgsJson);
|
||||
case "drag":
|
||||
return Deserialize<UIDragEventArgs>(eventArgsJson);
|
||||
case "error":
|
||||
return Deserialize<UIErrorEventArgs>(eventArgsJson);
|
||||
case "focus":
|
||||
return Deserialize<UIFocusEventArgs>(eventArgsJson);
|
||||
case "keyboard":
|
||||
return Deserialize<UIKeyboardEventArgs>(eventArgsJson);
|
||||
case "mouse":
|
||||
return Deserialize<UIMouseEventArgs>(eventArgsJson);
|
||||
case "pointer":
|
||||
return Deserialize<UIPointerEventArgs>(eventArgsJson);
|
||||
case "progress":
|
||||
return Deserialize<UIProgressEventArgs>(eventArgsJson);
|
||||
case "touch":
|
||||
return Deserialize<UITouchEventArgs>(eventArgsJson);
|
||||
case "unknown":
|
||||
return EventArgs.Empty;
|
||||
case "wheel":
|
||||
return Deserialize<UIWheelEventArgs>(eventArgsJson);
|
||||
default:
|
||||
throw new ArgumentException($"Unsupported value '{eventArgsType}'.", nameof(eventArgsType));
|
||||
return eventArgsType switch
|
||||
{
|
||||
"change" => DeserializeChangeEventArgs(eventArgsJson),
|
||||
"clipboard" => Deserialize<UIClipboardEventArgs>(eventArgsJson),
|
||||
"drag" => Deserialize<UIDragEventArgs>(eventArgsJson),
|
||||
"error" => Deserialize<UIErrorEventArgs>(eventArgsJson),
|
||||
"focus" => Deserialize<UIFocusEventArgs>(eventArgsJson),
|
||||
"keyboard" => Deserialize<UIKeyboardEventArgs>(eventArgsJson),
|
||||
"mouse" => Deserialize<UIMouseEventArgs>(eventArgsJson),
|
||||
"pointer" => Deserialize<UIPointerEventArgs>(eventArgsJson),
|
||||
"progress" => Deserialize<UIProgressEventArgs>(eventArgsJson),
|
||||
"touch" => Deserialize<UITouchEventArgs>(eventArgsJson),
|
||||
"unknown" => EventArgs.Empty,
|
||||
"wheel" => Deserialize<UIWheelEventArgs>(eventArgsJson),
|
||||
_ => throw new InvalidOperationException($"Unsupported event type '{eventArgsType}'. EventId: '{eventHandlerId}'."),
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new InvalidOperationException($"There was an error parsing the event arguments. EventId: '{eventHandlerId}'.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static T Deserialize<T>(string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(json, JsonSerializerOptionsProvider.Options);
|
||||
}
|
||||
private static T Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json, JsonSerializerOptionsProvider.Options);
|
||||
|
||||
private static EventFieldInfo InterpretEventFieldInfo(EventFieldInfo fieldInfo)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@ using System.Collections.Concurrent;
|
|||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Ignitor;
|
||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -20,7 +23,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
{
|
||||
public class ComponentHubReliabilityTest : IClassFixture<AspNetSiteServerFixture>, IDisposable
|
||||
{
|
||||
private static readonly TimeSpan DefaultLatencyTimeout = TimeSpan.FromSeconds(10);
|
||||
private static readonly TimeSpan DefaultLatencyTimeout = TimeSpan.FromSeconds(Debugger.IsAttached ? 60 : 10);
|
||||
private readonly AspNetSiteServerFixture _serverFixture;
|
||||
|
||||
public ComponentHubReliabilityTest(AspNetSiteServerFixture serverFixture, ITestOutputHelper output)
|
||||
|
|
@ -52,7 +55,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
return new Exception(error + Environment.NewLine + logs);
|
||||
};
|
||||
|
||||
_ = _serverFixture.RootUri; // this is needed for the side-effects of getting the URI.
|
||||
_ = _serverFixture.RootUri; // this is needed for the side-effects of getting the URI.
|
||||
TestSink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
|
||||
TestSink.MessageLogged += LogMessages;
|
||||
}
|
||||
|
|
@ -199,6 +202,118 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Assert.Contains(Logs, l => (l.LogLevel, l.Message) == (LogLevel.Debug, "Call to 'DispatchBrowserEvent' received before the circuit host initialization"));
|
||||
}
|
||||
|
||||
private async Task GoToTestComponent(IList<Batch> batches)
|
||||
{
|
||||
var rootUri = _serverFixture.RootUri;
|
||||
Assert.True(await Client.ConnectAsync(new Uri(rootUri, "/subdir"), prerendered: false), "Couldn't connect to the app");
|
||||
Assert.Single(batches);
|
||||
|
||||
await Client.SelectAsync("test-selector-select", "BasicTestApp.CounterComponent");
|
||||
Assert.Equal(2, batches.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DispatchingAnInvalidEventArgument_DoesNotProduceWarnings()
|
||||
{
|
||||
// Arrange
|
||||
var expectedError = $"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
|
||||
$"detailed exceptions in 'CircuitOptions.DetailedErrors'. Bad input data.";
|
||||
|
||||
var eventDescriptor = Serialize(new WebEventDescriptor()
|
||||
{
|
||||
BrowserRendererId = 0,
|
||||
EventHandlerId = 3,
|
||||
EventArgsType = "mouse",
|
||||
});
|
||||
|
||||
await GoToTestComponent(Batches);
|
||||
Assert.Equal(2, Batches.Count);
|
||||
|
||||
// Act
|
||||
await Client.ExpectCircuitError(() => Client.HubConnection.SendAsync(
|
||||
"DispatchBrowserEvent",
|
||||
eventDescriptor,
|
||||
"{sadfadsf]"));
|
||||
|
||||
// Assert
|
||||
var actualError = Assert.Single(Errors);
|
||||
Assert.Equal(expectedError, actualError);
|
||||
Assert.DoesNotContain(Logs, l => l.LogLevel > LogLevel.Information);
|
||||
Assert.Contains(Logs, l => (l.LogLevel, l.Exception?.Message) == (LogLevel.Debug, "There was an error parsing the event arguments. EventId: '3'."));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DispatchingAnInvalidEvent_DoesNotTriggerWarnings()
|
||||
{
|
||||
// Arrange
|
||||
var expectedError = $"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
|
||||
$"detailed exceptions in 'CircuitOptions.DetailedErrors'. Failed to dispatch event.";
|
||||
|
||||
var eventDescriptor = Serialize(new WebEventDescriptor()
|
||||
{
|
||||
BrowserRendererId = 0,
|
||||
EventHandlerId = 1990,
|
||||
EventArgsType = "mouse",
|
||||
});
|
||||
|
||||
var eventArgs = new UIMouseEventArgs
|
||||
{
|
||||
Type = "click",
|
||||
Detail = 1,
|
||||
ScreenX = 47,
|
||||
ScreenY = 258,
|
||||
ClientX = 47,
|
||||
ClientY = 155,
|
||||
};
|
||||
|
||||
await GoToTestComponent(Batches);
|
||||
Assert.Equal(2, Batches.Count);
|
||||
|
||||
// Act
|
||||
await Client.ExpectCircuitError(() => Client.HubConnection.SendAsync(
|
||||
"DispatchBrowserEvent",
|
||||
eventDescriptor,
|
||||
Serialize(eventArgs)));
|
||||
|
||||
// Assert
|
||||
var actualError = Assert.Single(Errors);
|
||||
Assert.Equal(expectedError, actualError);
|
||||
Assert.DoesNotContain(Logs, l => l.LogLevel > LogLevel.Information);
|
||||
Assert.Contains(Logs, l => (l.LogLevel, l.Message, l.Exception?.Message) ==
|
||||
(LogLevel.Debug,
|
||||
"There was an error dispatching the event '1990' to the application.",
|
||||
"There is no event handler associated with this event. EventId: '1990'. (Parameter 'eventHandlerId')"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DispatchingAnInvalidRenderAcknowledgement_DoesNotTriggerWarnings()
|
||||
{
|
||||
// Arrange
|
||||
var expectedError = $"There was an unhandled exception on the current circuit, so this circuit will be terminated. For more details turn on " +
|
||||
$"detailed exceptions in 'CircuitOptions.DetailedErrors'. Failed to complete render batch '1846'.";
|
||||
|
||||
await GoToTestComponent(Batches);
|
||||
Assert.Equal(2, Batches.Count);
|
||||
|
||||
Client.ConfirmRenderBatch = false;
|
||||
await Client.ClickAsync("counter");
|
||||
|
||||
// Act
|
||||
await Client.ExpectCircuitError(() => Client.HubConnection.SendAsync(
|
||||
"OnRenderCompleted",
|
||||
1846,
|
||||
null));
|
||||
|
||||
// Assert
|
||||
var actualError = Assert.Single(Errors);
|
||||
Assert.Equal(expectedError, actualError);
|
||||
Assert.DoesNotContain(Logs, l => l.LogLevel > LogLevel.Information);
|
||||
Assert.Contains(Logs, l => (l.LogLevel, l.Message, l.Exception?.Message) ==
|
||||
(LogLevel.Debug,
|
||||
$"Failed to complete render batch '1846' in circuit host '{Client.CircuitId}'.",
|
||||
"Received an acknowledgement for batch with id '1846' when the last batch produced was '4'."));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CannotInvokeOnRenderCompletedBeforeInitialization()
|
||||
{
|
||||
|
|
@ -249,7 +364,10 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
public async Task OnLocationChanged_ReportsDebugForExceptionInValidation()
|
||||
{
|
||||
// Arrange
|
||||
var expectedError = "Location change to http://example.com failed.";
|
||||
var expectedError = "There was an unhandled exception on the current circuit, so this circuit will be terminated. " +
|
||||
"For more details turn on detailed exceptions in 'CircuitOptions.DetailedErrors'. " +
|
||||
"Location change to 'http://example.com' failed.";
|
||||
|
||||
var rootUri = _serverFixture.RootUri;
|
||||
var baseUri = new Uri(rootUri, "/subdir");
|
||||
Assert.True(await Client.ConnectAsync(baseUri, prerendered: false), "Couldn't connect to the app");
|
||||
|
|
@ -267,7 +385,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Assert.DoesNotContain(Logs, l => l.LogLevel > LogLevel.Information);
|
||||
Assert.Contains(Logs, l =>
|
||||
{
|
||||
return l.LogLevel == LogLevel.Debug && Regex.IsMatch(l.Message, "Location change to http://example.com in circuit .* failed.");
|
||||
return (l.LogLevel, l.Message) == (LogLevel.Debug, $"Location change to 'http://example.com' in circuit '{Client.CircuitId}' failed.");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -275,7 +393,10 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
public async Task OnLocationChanged_ReportsErrorForExceptionInUserCode()
|
||||
{
|
||||
// Arrange
|
||||
var expectedError = "There was an unhandled exception .?";
|
||||
var expectedError = "There was an unhandled exception on the current circuit, so this circuit will be terminated. " +
|
||||
"For more details turn on detailed exceptions in 'CircuitOptions.DetailedErrors'. " +
|
||||
"Location change failed.";
|
||||
|
||||
var rootUri = _serverFixture.RootUri;
|
||||
var baseUri = new Uri(rootUri, "/subdir");
|
||||
Assert.True(await Client.ConnectAsync(baseUri, prerendered: false), "Couldn't connect to the app");
|
||||
|
|
@ -287,14 +408,14 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
await Client.ExpectCircuitError(() => Client.HubConnection.SendAsync(
|
||||
"OnLocationChanged",
|
||||
new Uri(baseUri, "/test").AbsoluteUri,
|
||||
false));
|
||||
false));
|
||||
|
||||
// Assert
|
||||
var actualError = Assert.Single(Errors);
|
||||
Assert.Matches(expectedError, actualError);
|
||||
Assert.Equal(expectedError, actualError);
|
||||
Assert.Contains(Logs, l =>
|
||||
{
|
||||
return l.LogLevel == LogLevel.Error && Regex.IsMatch(l.Message, "Unhandled exception in circuit .*");
|
||||
return (l.LogLevel, l.Message) == (LogLevel.Error, $"Location change to '{new Uri(_serverFixture.RootUri,"/test")}' in circuit '{Client.CircuitId}' failed.");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -373,6 +494,10 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
TestSink.MessageLogged -= LogMessages;
|
||||
}
|
||||
|
||||
private string Serialize<T>(T browserEventDescriptor) =>
|
||||
JsonSerializer.Serialize(browserEventDescriptor, TestJsonSerializerOptionsProvider.Options);
|
||||
|
||||
[DebuggerDisplay("{LogLevel.ToString(),nq} - {Message ?? \"null\",nq} - {Exception?.Message,nq}")]
|
||||
private class LogMessage
|
||||
{
|
||||
public LogMessage(LogLevel logLevel, string message, Exception exception)
|
||||
|
|
@ -394,7 +519,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
|
||||
private class Batch
|
||||
{
|
||||
public Batch(int id, byte [] data)
|
||||
public Batch(int id, byte[] data)
|
||||
{
|
||||
Id = id;
|
||||
Data = data;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Text.Json;
|
|||
using System.Threading.Tasks;
|
||||
using Ignitor;
|
||||
using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
|
||||
using Microsoft.AspNetCore.Components.Rendering;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -500,8 +501,8 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
// Arrange
|
||||
await GoToTestComponent(Batches);
|
||||
var sink = _serverFixture.Host.Services.GetRequiredService<TestSink>();
|
||||
var logEvents = new List<(LogLevel logLevel, string)>();
|
||||
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name));
|
||||
var logEvents = new List<(LogLevel logLevel, string eventIdName, Exception exception)>();
|
||||
sink.MessageLogged += (wc) => logEvents.Add((wc.LogLevel, wc.EventId.Name, wc.Exception));
|
||||
|
||||
// Act
|
||||
var browserDescriptor = new WebEventDescriptor()
|
||||
|
|
@ -520,8 +521,9 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
});
|
||||
|
||||
Assert.Contains(
|
||||
(LogLevel.Debug, "DispatchEventFailedToParseEventData"),
|
||||
logEvents);
|
||||
logEvents,
|
||||
e => e.eventIdName == "DispatchEventFailedToParseEventData" && e.logLevel == LogLevel.Debug &&
|
||||
e.exception.Message == "There was an error parsing the event arguments. EventId: '6'.");
|
||||
|
||||
// Taking any other action will fail because the circuit is disposed.
|
||||
await Client.ExpectCircuitErrorAndDisconnect(async () =>
|
||||
|
|
@ -563,7 +565,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.ServerExecutionTests
|
|||
Assert.Contains(
|
||||
logEvents,
|
||||
e => e.eventIdName == "DispatchEventFailedToDispatchEvent" && e.logLevel == LogLevel.Debug &&
|
||||
e.exception is ArgumentException ae && ae.Message.Contains("There is no event handler with ID 1"));
|
||||
e.exception is ArgumentException ae && ae.Message.Contains("There is no event handler associated with this event. EventId: '1'."));
|
||||
|
||||
// Taking any other action will fail because the circuit is disposed.
|
||||
await Client.ExpectCircuitErrorAndDisconnect(async () =>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<h1>Counter</h1>
|
||||
<p>Current count: @currentCount</p>
|
||||
<p><button @onclick="@(handleClicks ? (Action)IncrementCount : null)">Click me</button></p>
|
||||
<p><button id="counter" @onclick="@(handleClicks ? (Action)IncrementCount : null)">Click me</button></p>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" @bind="handleClicks" />
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
|
||||
namespace ComponentsApp.Server
|
||||
{
|
||||
public class Startup
|
||||
|
|
|
|||
Loading…
Reference in New Issue