注意
- 客户端上的用户可以看到配置和设置文件,用户可以篡改数据。 请勿在应用的配置或文件中存储应用机密、凭据或任何其他敏感数据
- 使用 WebAssembly 或Auto模式时,请记住所有组件代码都会编译并发送到客户端,用户可以在客户端对其进行反向编译和检查。 请勿在客户端渲染的组件中放置专用代码、应用机密或其他敏感信息
应用参数配置
一、文件配置源
1、项目默认配置文件
这里以Blazor web app Auto/Per page项目为例子,创建项目后,可以看到解决方案中会出现两个项目,一个是服务端,一个是客户端(.Client)。这两个项目中,都存在默认项目配置文件appsettings.json
和appsettings.Development.json
。
配置文件的加载规则
既然存在两个默认配置文件,那么当应用运行时,具体是用哪一个配置文件中的数据呢?
首相,在生产环境下,也就是进行了发布、托管部署后,是直接使用各自项目中的appsettings.json
文件的。
如果是在调试环境下(VS中),appsettings.json
是必定加载的,至于appsettings.Development.json
加载还是不加载,要看一下launchSettings.json
文件中,运行服务的配置。如果选择的运行服务环境配置为Development
那么会把appsettings.Development.json
也加载进来,反之则不加载。
- 默认情况下,在本地运行应用时,环境默认为
Development
。 发布应用时,环境默认为Production
。
加载配置文件后,就可以直接使用IConfiguration
服务来直接获取。
如果两个配置文件都加载了,则会先到appsettings.Development.json
中寻找对应参数,如果没有,再到appsettings.json
中查找。
作用区域
- 如果是在服务端的项目配置文件中配置了参数,那么当组件进行预渲染或服务端交互渲染时,可以读取到,当组件客户端交互渲染时则无法读取
- 如果是在客户端的项目配置文件中配置了参数,那么当组件进行预渲染或服务端交互渲染时,读取不到,当组件进行客户端交互渲染时可以读取到
例如,在服务端的appsettings.json
文件中,添加如下参数:
-
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "MyData": "TestData" }
然后在客户端项目中,添加Auto渲染模式的组件:
-
ConfigTest.razor
@page "/config-test" @rendermode InteractiveAuto @inject IConfiguration Configuration <h3>ConfigTest</h3> <p> @if (@Configuration["MyData"] is object) { @:预渲染或服务端交互渲染,读取到参数 MyData:@Configuration["MyData"] } else { @:客户端交互渲染,无法读取到参数 } </p>
例子中,使用了Auto渲染模式,一开始会进行预渲染、服务端交互渲染,然后会进行客户端交互渲染,所以只有刚开始出现页面时能访问到数据,过一会切换到客户端交互渲染后,就访问不到了。
2、静态资源配置文件
上面的所使用的是项目的默认json配置文件,一般情况下,项目配置文件多用来进行一些较为关键的参数配置,例如日志、数据库连接字符串等等。如果有额外的参数需要配置,建议在wwwroot
文件夹下,创建新的json配置文件,进行参数的配置。
-
newDataConfig.json
{ "MyData1": "DataTest1", "MyData2": { "Name": "Schuyler", "Age": 23 }, "MyData3": [ { "Name": "Schuyler1", "Age": 24 }, { "Name": "Schuyler2", "Age": 25 } ] }
客户端安全限制阻止通过用户代码直接访问文件,包括配置文件。 若除了appsettings.json
/appsettings.Development.json
之外,还要将 wwwroot
文件夹中的配置文件加载到IConfiguration
服务中,则需要使用HttpClient
来进行文件的访问。
-
注意,以客户端为例,除了
appsettings.json
/appsettings.Development.json
会自动加载外,不管是想使用自己项目中的wwwroot
文件夹下的配置文件还是服务端项目中的wwwroot
文件夹下的配置文件,最好都用HttpClient
来进行加载。 -
示例-客户端项目的
Program.cs
var builder = WebAssemblyHostBuilder.CreateDefault(args); var http = new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }; builder.Services.AddScoped(sp => http); using var response = await http.GetAsync("newDataConfig2.json"); using var stream = await response.Content.ReadAsStreamAsync(); builder.Configuration.AddJsonStream(stream); await builder.Build().RunAsync();
3、用户机密配置
在使用Visual Studio时,可以通过右键项目,选择管理用户机密,可以对secrets.json
文件进行json配置。
secrets.json
文件是针对当前开发者用户进行应用配置的私有文件,在开发环境下其配置会加载到应用中,且会覆盖appsettings.json
文件的同名配置(如果有)。
其作用在于开发阶段,如果需要使用自己的本地配置,例如本地数据库连接啥的,而又不希望上传到共享项目上(git、SVG等),就可以使用secrets.json
文件进行私有配置的管理,在上传项目时,secrets.json
中的配置将不会被上传。
- 试了一下,在Blazor Web App(Auto)模式下,客户端项目中的
secrets.json
文件是不生效的,原因应该是WebAssembly模式下,已经将相关代码和资料下载到浏览器了,此时只会将客户端appsettings.json
中的配置加载
二、内存配置源
在WebAssembly客户端项目中,除了使用文件进行配置外,还可以在Program
中,使用内存来进行参数配置。
-
注意,只有WebAssembly客户端项目支持这个用法。
-
Program.cs
var builder = WebAssemblyHostBuilder.CreateDefault(args); //内存参数配置 var vehicleData = new Dictionary<string, string?>() { { "color", "blue" }, { "type", "car" }, { "wheels:count", "3" }, { "wheels:brand", "Blazin" }, { "wheels:brand:type", "rally" }, { "wheels:year", "2008" }, }; var memoryConfig = new MemoryConfigurationSource { InitialData = vehicleData }; builder.Configuration.Add(memoryConfig); await builder.Build().RunAsync();
将IConfiguration
实例注入到组件中来访问配置数据。
-
ConfigTest.razor
@page "/config-test" @rendermode InteractiveAuto @inject IConfiguration Configuration <h3>ConfigTest</h3> <p> @if (@Configuration["color"] is object) { @:读取到参数 color:@Configuration["color"] } else { @:无法读取到参数 } </p>
启动
这里只记录常用的内容,更详细的内容请查阅官方文档:https://learn.microsoft.com/zh-cn/aspnet/core/blazor/fundamentals/startup?view=aspnetcore-8.0
一、启动过程和配置
1、自动启动
Blazor 启动过程默认情况下,是通过 Blazor 内置的JS脚本 (例如blazor.web.js
、blazor.server.js
、blazor.webassembly.js
等) 自动异步完成的,不同托管方式的Blazor脚本的引入位置如下(后面一律称为 Blazor<script>
标记):
-
Blazor Web 应用,Blazor 脚本位于
Components/App.razor
文件中<script src="_framework/blazor.web.js"></script>
-
Blazor Server 应用,Blazor 脚本位于
Pages/_Host.cshtml
文件中<script src="_framework/blazor.server.js"></script>
-
Blazor WebAssembly 应用,Blazor 脚本内容位于
wwwroot/index.html
文件中<script src="_framework/blazor.webassembly.js"></script>
后面的关于不同应用的内置启动JS脚本的路径,都用{BLAZOR SCRIPT}
进行表示了,不然太冗余。
2、手动启动
有时候需要在Blazor启动时,做一些自定义的初始化操作,Blazor Web App可以通过如下步骤来实现:
-
将
autostart="false"
属性和值添加到 Blazor<script>
标记中 -
将调用
Blazor.start()
的Js脚本放置在 Blazor启动脚本的</script>
标记之后并放在结束的</body>
标记内。 -
将静态服务器端渲染(静态 SSR)选项置于
ssr
属性中。(如果需要设置) -
将服务器端 Blazor-SignalR 线路选项置于
circuit
属性中。(如果需要设置) -
将客户端 WebAssembly 选项置于
webAssembly
属性中。(如果需要设置) -
示例
<script src="{BLAZOR SCRIPT}" autostart="false"></script> <script> ... Blazor.start({ ssr: { ... }, circuit: { ... }, webAssembly: { ... } }); //如果不需要进行设置,那么直接Start //Blazor.start(); ... </script>
当然了,如果是单独的Blazor Server或者是Blazor WebAssembly只需要设置自己对应的选项就好。
手动启动后执行JS代码
如果希望再Blazor启动后执行一些JS任务,可以链式调用then()
方法。
-
建议使用JS 初始值设定项,而不是
then()
-
示例
<script src="{BLAZOR SCRIPT}" autostart="false"></script> <script> Blazor.start().then(function () { ... }); </script>
3、示例
禁用增强型导航和表单处理
-
示例
<script src="{BLAZOR SCRIPT}" autostart="false"></script> <script> Blazor.start({ ssr: { disableDomPreservation: true } }); </script>
文档准备就绪后再初始化 Blazor
-
示例
<script src="{BLAZOR SCRIPT}" autostart="false"></script> <script> document.addEventListener("DOMContentLoaded", function() { Blazor.start(); }); </script>
二、JavaScript 初始值设定项
JavaScript 初始值设定项在 Blazor 应用加载之前或之后执行逻辑。 JS 初始值设定项在以下场景中很有用:
JS 初始值设定项是在生成过程中检测并自动进行导入的,不需要再去手动触发脚本。如果要定义 JS 初始值设定项,需要在项目的根目录中,默认情况下即 wwwroot
文件夹下,添加{NAME}.lib.module.js
文件,其中{NAME}
占位符表示当前程序集名称。
1、初始化回调方法
Blazor Web 应用
beforeWebStart(options)
:在 Blazor Web 应用启动之前调用,用于自定义加载过程、日志记录级别和其他选项。 Blazor Web 选项对象作为options
参数传递给beforeWebStart
afterWebStarted(blazor)
:在所有 beforeWebStart
结束后调用,可用于注册 Blazor 事件侦听器和自定义事件类型。 Blazor 实例作为参数blazor
传递给 afterWebStarted
。
beforeServerStart(options, extensions)
:在启动第一个服务器运行时之前调用。 接收 SignalR 路线启动选项 (options
) 以及在发布期间添加的任何扩展 (extensions
)。
afterServerStarted(blazor)
:在启动第一个交互式服务器运行时之后调用。
beforeWebAssemblyStart(options, extensions)
:在启动交互式 WebAssembly 运行时之前调用。 接收 Blazor 选项 (options
) 以及在发布期间添加的任何扩展 (extensions
)。选项可以指定使用自定义启动资源加载程序。
afterWebAssemblyStarted(blazor)
:在启动交互式 WebAssembly 运行时之后调用。
Blazor Server、Blazor WebAssembly 和 Blazor Hybrid 应用
beforeStart(options, extensions)
:在 Blazor 启动之前调用。用于自定义加载过程、日志记录级别以及其他特定于托管模型的选项。
- 客户端
beforeStart
接收 Blazor 选项 (options
) 以及在发布期间添加的任何扩展 (extensions
)。 选项可以指定使用自定义启动资源加载程序。 - 服务器端
beforeStart
接收 SignalR 线路启动选项 (options
)。 - 在
BlazorWebView
中,不会传递任何选项。
afterStarted(blazor)
:在 Blazor 准备好从 JS 接收调用后调用。 用于通过进行 JS 互操作调用并注册自定义元素来初始化库。 Blazor 实例作为参数blazor
传递给 afterStarted
。
其他 .NET WebAssembly 运行时回调
onRuntimeConfigLoaded(config)
:在下载启动配置时调用。 允许应用在运行时启动前(参数是来自 dotnet.d.ts
的 MonoConfig
)修改参数(配置):
-
{NAME}.lib.module.js
export function onRuntimeConfigLoaded(config) { // Sample: Enable startup diagnostic logging when the URL contains // parameter debug=1 const params = new URLSearchParams(location.search); if (params.get("debug") == "1") { config.diagnosticTracing = true; } }
onRuntimeReady({ getAssemblyExports, getConfig })
:在 .NET WebAssembly 运行时启动后调用(参数是来自 dotnet.d.ts
的 RuntimeAPI
):
-
{NAME}.lib.module.js
export function onRuntimeReady({ getAssemblyExports, getConfig }) { // Sample: After the runtime starts, but before Main method is called, // call [JSExport]ed method. const config = getConfig(); const exports = await getAssemblyExports(config.mainAssemblyName); exports.Sample.Greet(); }
以上两个方法,都可以返回一个Promise
,并会在启动程序之前等待该Promise
。
2、导入其他模块
如果要在JS初始值设定项中使用其他JS文件,可以通过import
语句导入
-
additionalModule.js
export function logMessage() { console.log('logMessage is logging'); }
-
{NAME}.lib.module.js
import { logMessage } from "/additionalModule.js"; export function beforeStart(options, extensions) { ... logMessage(); }
3、示例
确保加载JS库的顺序
-
示例
以下示例在 script2.js 之前加载 script1.js,在 script4.js 之前加载 script3.js
export function beforeWebStart(options, extensions) { var customScript1 = document.createElement('script'); customScript1.setAttribute('src', 'script1.js'); document.head.appendChild(customScript1); var customScript2 = document.createElement('script'); customScript2.setAttribute('src', 'script2.js'); document.head.appendChild(customScript2); } export function afterWebStarted(blazor) { var customScript1 = document.createElement('script'); customScript1.setAttribute('src', 'script3.js'); document.head.appendChild(customScript1); var customScript2 = document.createElement('script'); customScript2.setAttribute('src', 'script4.js'); document.head.appendChild(customScript2); }
环境
这里只记录常用的内容,更详细的内容请查阅官方文档:https://learn.microsoft.com/zh-cn/aspnet/core/blazor/fundamentals/environments?view=aspnetcore-8.0
在本地使用Debug模式运行应用时,环境默认为 Development
。 发布应用时,环境默认为 Production
。
建议使用以下约定
- 始终使用
Development
环境名称进行本地开发。 这是因为,在为应用的本地开发运行配置应用和工具时,ASP.NET Core 框架需要该名称 - 对于测试、暂存和生产环境,请始终发布和部署应用,不要直接运行源码
- 命名自由但建议规范:测试 →
Test
;预发布 →Staging
;生产 →Production
;同时配置文件必须严格匹配,如:Staging
→appsettings.Staging.json
;Production
→appsettings.Production.json
一、组件中读取环境
在客户端组件中,如果想要获取应用的环境信息,可以通过注入IWebAssemblyHostEnvironment
并读取 Environment
属性来得到。
@page "/read-environment"
@using Microsoft.AspNetCore.Components.WebAssembly.Hosting
@inject IWebAssemblyHostEnvironment Env
<h1>Environment example</h1>
<p>Environment: @HostEnvironment.Environment</p>
需要注意的是,对于Blazor Web App Auto应用,IWebAssemblyHostEnvironment
仅在客户端项目(.Client
)中进行了服务的注册,如果客户端项目中的组件允许预渲染,那么当组件在服务器上预渲染时,会由于找不到IWebAssemblyHostEnvironment
而在注入该服务时出现异常。
针对这个问题,可以在服务器项目上创建IWebAssemblyHostEnvironment
的自定义服务,然后进行注册:
-
ServerHostEnvironment.cs
public class ServerHostEnvironment(IWebHostEnvironment env, NavigationManager nav) : IWebAssemblyHostEnvironment { public string Environment => env.EnvironmentName; public string BaseAddress => nav.BaseUri; }
-
Program.cs
...... builder.Services.TryAddScoped<IWebAssemblyHostEnvironment, ServerHostEnvironment>(); ......
二、启动时读取客户端环境
在启动过程中,WebAssemblyHostBuilder
会通过HostEnvironment
属性公开 IWebAssemblyHostEnvironment
,因此可以在客户端项目的Program中,通过builder.HostEnvironment
来获取环境信息。
if (builder.HostEnvironment.Environment == "Development")
{
......
};
类似HostEnvironmentEnvExtensions
为IHostEnvironment
(服务端项目中Program.cs
所使用的主机环境类型)提供了一系列便捷的扩展方法一样,WebAssemblyHostEnvironmentExtensions
则是为IWebAssemblyHostEnvironment
(客户端项目中Program.cs
所使用的主机环境类型)提供了一系列便捷的扩展方法,可在当前环境中检查 Development
、Production
、Staging
和自定义环境名称
bool IsDevelopment()
bool IsProduction()
bool IsStaging()
bool IsEnvironment(envName)
if (builder.HostEnvironment.IsStaging())
{
......
};
if (builder.HostEnvironment.IsEnvironment("Custom"))
{
......
};