Blazor server side better error UI
We'd implemented a custom logger to show errors for Blazor WASM. For server side, global exception handling is not possible yet (see https://github.com/dotnet/aspnetcore/issues/30940). However, we can still show a better messagebox (modal dialog) instead of the yellow bottom bar, for blazor server side. We can provide an option to the user to refresh the page in this modal dialog (message modal can have two options: Close & Refresh Page). However, this should be carefully implemented. Because, if we handle every error and try to show message, it won't work. We should only handle the errors for blazor application's page, not other HTTP requests and MVC page requests.
Good day, this fix gonna be available on version 5.4?
Good day, this fix gonna be available on version 5.4?
Yes, we expect it to be available on version 6.0.
Any updates on this one?
There have been investigations, but the results were unsatisfactory due to some limitations. However, we will close the problem once we have found a satisfactory solution.
So what's the workaround so far? Catching the exception in the component's code on the Blazor side?
If so, how do I open the exception modal with the message?
Yes, we try to ensure that there are no unhandled exceptions as mentioned in Microsoft's documentation.
By the way, you can see the work done within the scope of this issue here.
Abp 7.3.3 Dotnet core 7
It can be done easily by creating the ComponentBase from scratch just like the original (github):
in blazor components, every events will be called from Task IHandleEvent.HandleEventAsync so if it can be override, then we can create a handler based on the Task.Status
- Create the
MyComponentBase:
public abstract class MyComponentBase : IComponent, IHandleEvent, IHandleAfterRender
{
...
Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
{
var task = callback.InvokeAsync(arg);
var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion &&
task.Status != TaskStatus.Canceled;
// After each event, we synchronously re-render (unless !ShouldRender())
// This just saves the developer the trouble of putting "StateHasChanged();"
// at the end of every event callback.
StateHasChanged();
return shouldAwaitTask ?
CallStateHasChangedOnAsyncCompletion(task) :
Task.CompletedTask;
}
...
}
Then task will be run in CallStateHasChangedOnAsyncCompletion method:
private async Task CallStateHasChangedOnAsyncCompletion(Task task)
{
try
{
await task;
}
catch // avoiding exception filters for AOT runtime support
{
// Ignore exceptions from task cancellations, but don't bother issuing a state change.
if (task.IsCanceled)
{
return;
}
throw;
}
StateHasChanged();
}
- Instead of
throwcall a method for handle thetask.ExceptionnamedOnCallStateHasChangedOnAsyncCompletionExceptionAsync:
protected virtual Task OnCallStateHasChangedOnAsyncCompletionExceptionAsync(AggregateException? exception)
=> Task.CompletedTask;
Make sure to set the virtual keyword to allow override this method.
- Now CallStateHasChangedOnAsyncCompletion would be like this:
private async Task CallStateHasChangedOnAsyncCompletion(Task task)
{
try
{
await task;
}
catch // avoiding exception filters for AOT runtime support
{
// Ignore exceptions from task cancellations, but don't bother issuing a state change.
if (task.IsCanceled)
{
return;
}
await OnCallStateHasChangedOnAsyncCompletionExceptionAsync(task.Exception);
}
StateHasChanged();
}
-
From
OwningComponentBaseandAbpComponentBaserewrite it toMyOwningComponentBaseandMyAbpComponentBase. -
In
MyAbpComponentBase, theOnCallStateHasChangedOnAsyncCompletionExceptionAsyncmethod can be override like this:
protected override Task OnCallStateHasChangedOnAsyncCompletionExceptionAsync(AggregateException? exception)
{
return HandleErrorAsync(exception);
}
HandleErrorAsync is a method in MyAbpComponentBase that appear a friendly modal message.
In the RunInitAndSetParametersAsync method also can do the same for handle errors.
private async Task RunInitAndSetParametersAsync()
{
try
{
...
}
catch (Exception ex)
{
await OnRunInitAndSetParametersExceptionAsync(ex);
}
}
public virtual Task OnRunInitAndSetParametersExceptionAsync(Exception ex) => Task.CompletedTask;
In MyAbpComponentBase:
public override Task OnRunInitAndSetParametersExceptionAsync(Exception ex)
{
return HandleErrorAsync(ex);
}
With Regards
I bumped into it in ABP 7.4.0 commercial: Unhandled exception rendering component: Forbidden Volo.Abp.Http.Client.AbpRemoteCallException
I bumped into it in ABP 7.4.0 commercial:
Unhandled exception rendering component: Forbidden Volo.Abp.Http.Client.AbpRemoteCallException
Please check the logs of the backend app, and create a new issue. Thanks.
Abp 7.3.3 Dotnet core 7
It can be done easily by creating the
ComponentBasefrom scratch just like the original (github):in blazor components, every events will be called from
Task IHandleEvent.HandleEventAsyncso if it can be override, then we can create a handler based on theTask.Status
- Create the
MyComponentBase:public abstract class MyComponentBase : IComponent, IHandleEvent, IHandleAfterRender { ... Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg) { var task = callback.InvokeAsync(arg); var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion && task.Status != TaskStatus.Canceled; // After each event, we synchronously re-render (unless !ShouldRender()) // This just saves the developer the trouble of putting "StateHasChanged();" // at the end of every event callback. StateHasChanged(); return shouldAwaitTask ? CallStateHasChangedOnAsyncCompletion(task) : Task.CompletedTask; } ... }Then task will be run in
CallStateHasChangedOnAsyncCompletionmethod:private async Task CallStateHasChangedOnAsyncCompletion(Task task) { try { await task; } catch // avoiding exception filters for AOT runtime support { // Ignore exceptions from task cancellations, but don't bother issuing a state change. if (task.IsCanceled) { return; } throw; } StateHasChanged(); }
- Instead of
throwcall a method for handle thetask.ExceptionnamedOnCallStateHasChangedOnAsyncCompletionExceptionAsync:protected virtual Task OnCallStateHasChangedOnAsyncCompletionExceptionAsync(AggregateException? exception) => Task.CompletedTask;Make sure to set the
virtualkeyword to allow override this method.
- Now CallStateHasChangedOnAsyncCompletion would be like this:
private async Task CallStateHasChangedOnAsyncCompletion(Task task) { try { await task; } catch // avoiding exception filters for AOT runtime support { // Ignore exceptions from task cancellations, but don't bother issuing a state change. if (task.IsCanceled) { return; } await OnCallStateHasChangedOnAsyncCompletionExceptionAsync(task.Exception); } StateHasChanged(); }
- From
OwningComponentBaseandAbpComponentBaserewrite it toMyOwningComponentBaseandMyAbpComponentBase.- In
MyAbpComponentBase, theOnCallStateHasChangedOnAsyncCompletionExceptionAsyncmethod can be override like this:protected override Task OnCallStateHasChangedOnAsyncCompletionExceptionAsync(AggregateException? exception) { return HandleErrorAsync(exception); }
HandleErrorAsyncis a method inMyAbpComponentBasethat appear a friendly modal message.In the
RunInitAndSetParametersAsyncmethod also can do the same for handle errors.private async Task RunInitAndSetParametersAsync() { try { ... } catch (Exception ex) { await OnRunInitAndSetParametersExceptionAsync(ex); } } public virtual Task OnRunInitAndSetParametersExceptionAsync(Exception ex) => Task.CompletedTask;In
MyAbpComponentBase:public override Task OnRunInitAndSetParametersExceptionAsync(Exception ex) { return HandleErrorAsync(ex); }With Regards
i noticed some information being missed, for example , the ```StateHasChanged()``` method does not exist in the custom component base , also , the ```HandleErrorAsync()``` method is only available if you inherits from ```AbpComponentBase```
i quit liked the solution but the implementation needs more detail.
also , i think it's time for @Abp to think about a solution to handle errors generally.
Thanks.
Any progress on this one?