原文鏈接:https://blazor-university.com/javascript-interop/calling-dotnet-from-javascript/type-safety/
類型安全
在從 JavaScript 調用 .NET[1] 部分中,您可能已經注意到我們的 JavaScript 的第 6 行在將隨機生成的數字傳遞給 .NET 之前調用了 toString()
var?BlazorUniversity?=?BlazorUniversity?||?{};
BlazorUniversity.startRandomGenerator?=?function(dotNetObject)?{setInterval(function?()?{let?text?=?Math.random()?*?1000;console.log("JS:?Generated?"?+?text);dotNetObject.invokeMethodAsync('AddText',?text.toString());},?1000);
};
盡管對象類型在 JavaScript 中是可以互換的,但是當它們被傳遞給我們的 .NET Invokable
方法時,它們就不是那么互換了。調用 .NET 時,請確保為要傳遞的變量選擇正確的 .NET 類型。
JavaScript 類型 | .NET 類型 |
---|---|
boolean | System.Boolean |
string | System.String |
number | System.Float / System.Decimal |
Date | System.DateTime 或 System.String |
枚舉
當 JSInvokable
.NET 方法的參數是枚舉時,JavaScript 應該傳遞枚舉的數值。下面的示例將使用值 TestEnum.SecondValue
調用我們的 .NET 方法。
public?enum?TestEnum
{FirstValue?=?100,SecondValue?=?200
};[JSInvokable("OurInvokableDotNetMethod")]
public?void?OurInvokableDotNetMethod(TestEnum?enumValue)
{
}
但是,如果我們用 [System.Text.Json.Serialization.JsonConverter]
裝飾我們的枚舉,我們可以讓我們的 JavaScript 改為傳遞字符串值。
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
public?enum?TestEnum
{FirstValue?=?100,SecondValue?=?200
};
現在調用 JavaScript 可以傳遞枚舉值的名稱或其數值。以下兩個調用是等效的。
dotNetObject.invokeMethodAsync('OurInvokableDotNetMethod',?'FirstValue');
dotNetObject.invokeMethodAsync('OurInvokableDotNetMethod',?200);
調用靜態 .NET 方法
除了在 .NET 對象實例上調用方法外,Blazor 還使我們能夠調用靜態方法。下一個示例將展示如何從 JavaScript 調用 .NET 并檢索 API 調用可能需要的特定設置,例如 Google Analytics
。
從服務器設置中讀取 JavaScript 設置的好處是,在部署過程中可以根據環境(開發/QA/生產)覆蓋這些值,而無需更改 JavaScript 文件。
源代碼[2]
創建新的 Blazor 服務器端應用程序
打開 /appsettings.json 文件并添加一個名為“JavaScript”的部分
{"Logging":?{"LogLevel":?{"Default":?"Information","Microsoft":?"Warning","Microsoft.Hosting.Lifetime":?"Information"}},"JavaScript":?{"SomeApiKey":??"123456789"},"AllowedHosts":?"*"
}
接下來我們需要一個類來保存這個設置,創建一個名為
Configuration
的文件夾在該文件夾中,創建一個名為
JavaScriptSettings.cs
的文件
public?class?JavaScriptSettings
{public?string?SomeApiKey?{?get;?set;?}
}
編輯 /Startup.cs 文件
在該類的構造函數中,我們將使用注入的
IConfiguration
實例從/appsettings.json
中獲取“JavaScript”部分并將其存儲在靜態引用中。
public?Startup(IConfiguration?configuration)
{Configuration?=?configuration;var?javaScriptSettings?=?configuration.GetSection("JavaScript").Get<JavaScriptSettings>();JavaScriptConfiguration.SetSettings(javaScriptSettings);
}
JavaScriptConfiguration
類還不存在,所以接下來我們將在 Configuration 文件夾中創建它。
public?static?class?JavaScriptConfiguration
{private?static?JavaScriptSettings?Settings;internal?static?void?SetSettings(JavaScriptSettings?settings){Settings?=?settings;}public?static?JavaScriptSettings?GetSettings()?=>?Settings;
}
現在我們的配置文件中有一些新設置,一個在 .NET 中表示這些設置的類,我們正在讀取這些值并將它們存儲在靜態引用中。接下來我們需要從 JavaScript 訪問它。
編輯 /Pages/_Host.cshtml 文件并在現有
<script>
標記下添加以下內容
<script?src="~/scripts/CallingStaticDotNetMethods.js"></script>
接下來,在 /wwwroot 文件夾下創建一個名為 scripts 的文件夾
在該文件夾中,創建一個名為
CallingDotNetStaticMethods.js
的新文件并添加以下腳本
setTimeout(async?function?()?{const?settings?=?await?DotNet.invokeMethodAsync("CallingStaticDotNetMethods",?"GetSettings");alert('API?key:?'?+?settings.someApiKey);
},?1000);
DotNet.invokeMethodAsync
至少需要兩個參數。可以傳遞兩個以上,并且第二個之后的任何參數都被認為是作為參數傳遞給方法的值。
方法所在的二進制文件的全名(不包括文件擴展名)
要執行的方法的標識符
最后一塊拼圖是用 [JSInvokable]
屬性裝飾方法,傳入標識符——在本示例中,標識符將是 GetSettings
。
編輯 /Configuration/JavaScriptConfiguration 類,并更改 GetSettings
方法:
[JSInvokable("GetSettings")]
public?static?JavaScriptSettings?GetSettings()?=>?Settings;
傳遞給 [JSInvokable]
的標識符不必與方法名稱相同。
JavaScript 可調用方法的條件
要成為可通過這種方式調用的候選 .NET 方法,該方法必須滿足以下條件:
擁有該方法的類必須是公共的 -方法必須是公開的
必須是靜態方法
返回類型必須為
void
,或可序列化為 JSON——或者它必須是Task
或Task<T>
,其中T
可序列化為 JSON所有參數必須可序列化為 JSON
該方法必須用
[JSInvokable]
裝飾JSInvokable
屬性中使用的同一標識符不能在單個程序集中多次使用。
注意:不要立即從 JavaScript 調用 .NET 靜態方法
如果您回顧 JavaScript 啟動過程[3]部分,您會記得 JavaScript 在 Blazor 初始化之前已在瀏覽器中初始化。
正是出于這個原因,我們只在初始超時后調用 .NET 靜態方法——在這種情況下,我選擇了一秒。
setTimeout(async?function?()?{const?settings?=?await?DotNet.invokeMethodAsync("CallingStaticDotNetMethods",?"GetSettings");alert('API?key:?'?+?settings.someApiKey);
},?1000);
在撰寫本文時,無法從 JavaScript 檢查 Blazor 是否已準備好被調用,而無需嘗試調用它并失敗。
window.someInitialization?=?async?function?()?{try?{const?settings?=?await?DotNet.invokeMethodAsync("CallingStaticDotNetMethods",?"GetSettings");alert('API?key:?'?+?settings.someApiKey);}catch?{//?Try?againthis.setTimeout(someInitialization,?10);}
}
window.someInitialization();
連接到 Blazor.start
可以在通過調用 Blazor.start
函數初始化 Blazor 時調用我們的 JavaScript。
首先,編輯 /Pages/_Host.cshtml 并更改引用 Blazor 腳本的 <script>
標記,并添加一個名為 autostart
且值為 false
的新屬性。
<script?src="_framework/blazor.server.js"?autostart="false"></script>
接下來,我們需要更改我們的 JavaScript 以便它調用 Blazor.start
- 這將返回一個 Promise<void>
,一旦 Blazor 初始化,我們就可以使用它來執行我們自己的代碼。
Blazor.start({}).then(async?function?()?{const?settings?=?await?DotNet.invokeMethodAsync("CallingStaticDotNetMethods",?"GetSettings");alert('API?key:?'?+?settings.someApiKey);});
這種方法的問題是您只能使用一次。因此,如果我們在不同的腳本中有多個入口點,那么我們將不得不創建自己的掛鉤點來緩存來自 Blazor.start
的結果并將其返回給任何調用腳本。
參考資料
[2]
源代碼: https://github.com/mrpmorris/blazor-university/tree/master/src/JavaScriptInterop/CallingStaticDotNetMethods