Blazor文档阅读 》 Fundamentals 。
ASP.NET Core Blazor logging
WASM应用可以自定义录记功能,只需设置WebAssemblyHostBuilder.Logging辖属。
在Program.cs中添加下列使用声明:
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
通过LoggingBuilderExtensions.SetMinimumLevel可以自定义录记级别:
var builder = WebAssemblyHostBuilder.CreateDefault(args);
...
builder.Logging.SetMinimumLevel(LogLevel.Debug);
builder.Logging.AddProvider(new CustomLoggingProvider());
Logging辖属的类型为ILoggingBuilder,ILoggingBuilder中的方法也在Loggin中存在。
Hosted Blazor WebAssembly logging
一个宿寄的WASM应用如果采用预渲染,可以执行初始化代码两次。录记第一次发生在服务端执行初始化代码的时候,第二次发生在客户端初始化代码的时候。
SignalR .NET client logging
略
Log in Razor components
@using Microsoft.Extensions.Logging;
用来支持IntelliSense,这样LogWarning以及LogError等可以自动补全。下面是组件中使用ILogger的一个示例:
@page "/counter"
@using Microsoft.Extensions.Logging;
@inject ILogger<Counter> logger;
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
logger.LogWarning("Someone has clicked me!");
currentCount++;
}
}
下面额例子展示组件中对ILoggerFactory的使用:
@page "/counter"
@using Microsoft.Extensions.Logging;
@inject ILoggerFactory LoggerFactory
<h1>Counter</h1>
<p>Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
var logger = LoggerFactory.CreateLogger<Counter>();
logger.LogWarning("Someone has clicked me!");
currentCount++;
}
}
参考https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0
Handle errors in ASP.NET Core Blazor apps
Detailed errors during development
当错误发生时,Blazor应用会在底部区域显示一个淡黄色的错误条:
- 开发环境中,错误条会指向浏览器console,那里有打印出来的异常信息
- 生产环境中,错误条会提示刷新浏览器
UI由https://docs.microsoft.com/en-us/aspnet/core/blazor/project-structure?view=aspnetcore-5.0提供。
WASM应用可在wwwroot/index.html
调整:
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
blazor-error-ui的CSS类定义在wwwroot/css/app.css
。
Manage unhandled exceptions in developer code
在生成环境中,不要将错误信息显示在UI上面,这么做有以下问题:
- 泄露敏感信息给终端用户
- 帮助恶意用户探索应用的弱点,从而威胁到安全性
Global exception handling
应用可以将一个专门的错误处理组件设置成cascading value来以统一的方式处理错误。
下面是错误处理组件的示例,它将自己作为一个cascading value传递给childcontent:
@using Microsoft.Extensions.Logging
@inject ILogger<Error> Logger
<CascadingValue Value=this>
@ChildContent
</CascadingValue>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; }
public void ProcessError(Exception ex)
{
Logger.LogError("Error:ProcessError - Type: {Type} Message: {Message}",
ex.GetType(), ex.Message);
}
}
使用组件而不是注入的服务、自定义的录记器是因为组件可以显示内容,并且应用CSS样式。
在App组件内,将Router包裹在Error组件之内,这样允许Router组件以CascadingParameter的方式接收Error组件:
<Error>
<Router ...>
...
</Router>
</Error>
如何在其他组件内使用Error组件呢?
首先定义一个Error类型的CascadingParameter:
[CascadingParameter]
public Error Error { get; set; }
然后这样:
try
{
...
}
catch (Exception ex)
{
Error.ProcessError(ex);
}
若ProcessError需要渲染UI,比如输出错误信息,那么需要在ProcessErrors结束前调用StateHasChanged
。
Log errors with a persistent provider
如果发生了未处理的异常,那么异常会被录记到当前服务容器配置的ILogger。默认情况下,Blazor应用录记到控制台输出。可以考虑将异常输出到一个更恒久的位置,比如一个后端web api所提供的录记服务。后端web api可以选择将录记内容交给Application Performance Management(APM)。
开发者必须决定录记的时机,以及内容的严重度。要访问录记功能被恶意滥用,比如不要录记因URL提供的参数(如productid)是未知的。
更多参考:
- https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/logging?view=aspnetcore-5.0
- https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-5.0
- https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-5.0
原生的https://docs.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview支持Blazor WebAssembly应用。以后Blazor框架或许会支持原生的Google Analytics。参考https://github.com/microsoft/ApplicationInsights-dotnet/issues/2143以及https://github.com/dotnet/aspnetcore/issues/5461。
与此同时,客户端的WASM应用可以使用基于JS的https://docs.microsoft.com/en-us/azure/azure-monitor/app/javascript来将录记内容提交到Application Insights。
Places where errors may occur
Component instantiation
创建组件时会调用组件的构造函数;以@inject
或者[inject]
注入的DI服务若非singleton,其构造函数也会被调用。这时候出现异常会阻止框架例现化此组件。若要录记此异常,则需使用try-catch语句来拦截此异常。
Lifecycle methods
文中展示了一个OnParametersSetAsync()中出现异常如何处理的例子。
Rendering logic
razor文件中的声明性的标签会被编译进C#的BuildRenderTree方法。组件渲染时,此方法会被调用。此过程也可能出现异常。
比如说对于常见的NullReferenceException,要么使用的时候判断其是否为null,要么给其设一个默认值。
Event handlers
事件处理器可能会抛出异常,如果用户代码不处理这些异常,那么框架会将这些异常录记下来。
Component disposal
实现了System.IDisposable的组件在移除的时候若抛出异常,需要拦截住异常并录记。
JavaScript interop
IJSRuntime.InvokeAsync允许.NET代码在浏览器内异步调用JS的运行库。
下列条件适用于InvokeAsync:
- 如果InvokeAsync失败且调用是同步的,一个.NET异常会抛出。这在传入参数出错的情况下有可能发生。用户必须自行处理此类异常。
- 如果InvokeAsync失败且调用是异步的,那么对应的.NET任务便失败了。用户必须自定处理此类异常。如果使用await操作,可以将其围绕在try-catch语句中。
- 默认情况下,InvokeAsync必须在一定时限内完成,否则就会超时出错。默认时限是一分钟。如果超时,那么System.Threading.Tasks所涉及的异常时OperationCanceledException。用户需要拦截并录记此异常。
同样地,若JS代码调用C#中标为[JSInvokable]
的方法也可能出错,此时JS测的Promise会被拒绝。
参考:
- https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-5.0
- https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-5.0
Advanced scenarios
组件可以嵌套,但是要避免无限递归:
- 不要渲染带有循环引用的数据结构
- 不要创建带有循环引用的布局
- 不要让用户有机会篡改输入数据从而导致无线循环
开发者可以自行撰写RenderTreeBuilder逻辑,参考https://docs.microsoft.com/en-us/aspnet/core/blazor/advanced-scenarios?view=aspnetcore-5.0#manual-rendertreebuilder-logic。但是要注意:
- OpenElement和CloseElement的使用要搭对
- 属性必须添加到正确的位置
不正确的逻辑会导致未定义的行为,以及其他各种问题。
要有心理准备,手写RenderTreeBuilder可能跟用https://docs.microsoft.com/en-us/dotnet/standard/managed-code手写.NET代码一样复杂。
ASP.NET Core Blazor SignalR guidance
只考虑WASM。
如何将SignalR添加到宿寄的WASM应用,参考https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr-blazor?view=aspnetcore-5.0。
SignalR cross-origin negotiation for authentication
关于如何配置客户端,以便让其发送凭证给SignalR服务端。
Render mode
宿寄的WASM应用,首次渲染可以发生在服务端。
ASP.NET Core Blazor static files
略。只适用于Server应用。
(本篇完)