Improve initialization and SignalR configuration

Fixes: #6887 and #6887 and #5624

Adds support for calling Blazor.start({...}) and passing in a
configuration object.

For now all you can configure is the SignalR HubConnectionBuilder. This
is a priority right now because we want to make configuring SignalR's
logging accessible.
This commit is contained in:
Ryan Nowak 2019-03-29 15:11:00 -07:00 committed by Ryan Nowak
parent 555b506a97
commit f2e9ef3441
8 changed files with 84 additions and 28 deletions

View File

@ -2,7 +2,7 @@ import '@dotnet/jsinterop';
import './GlobalExports';
import * as signalR from '@aspnet/signalr';
import { MessagePackHubProtocol } from '@aspnet/signalr-protocol-msgpack';
import { fetchBootConfigAsync, loadEmbeddedResourcesAsync } from './BootCommon';
import { fetchBootConfigAsync, loadEmbeddedResourcesAsync, shouldAutoStart } from './BootCommon';
import { CircuitHandler } from './Platform/Circuits/CircuitHandler';
import { AutoReconnectCircuitHandler } from './Platform/Circuits/AutoReconnectCircuitHandler';
import RenderQueue from './Platform/Circuits/RenderQueue';
@ -10,9 +10,21 @@ import { ConsoleLogger } from './Platform/Logging/Loggers';
import { LogLevel, ILogger } from './Platform/Logging/ILogger';
import { discoverPrerenderedCircuits, startCircuit } from './Platform/Circuits/CircuitManager';
let renderingFailed = false;
async function boot(): Promise<void> {
type SignalRBuilder = (builder: signalR.HubConnectionBuilder) => void;
interface BlazorOptions {
configureSignalR?: SignalRBuilder,
};
let renderingFailed = false;
let started = false;
async function boot(options?: BlazorOptions): Promise<void> {
if (started) {
throw new Error('Blazor has already started.');
}
started = true;
// For development.
// Simply put a break point here and modify the log level during
@ -30,7 +42,9 @@ async function boot(): Promise<void> {
return loadEmbeddedResourcesAsync(bootConfig);
});
const initialConnection = await initializeConnection(circuitHandlers, logger);
// pass options.configureSignalR to configure the signalR.HubConnectionBuilder
const configureSignalR = (options && options.configureSignalR) || null;
const initialConnection = await initializeConnection(configureSignalR, circuitHandlers, logger);
const circuits = discoverPrerenderedCircuits(document);
for (let i = 0; i < circuits.length; i++) {
@ -55,7 +69,7 @@ async function boot(): Promise<void> {
// We can't reconnect after a failure, so exit early.
return false;
}
const reconnection = await initializeConnection(circuitHandlers, logger);
const reconnection = await initializeConnection(configureSignalR, circuitHandlers, logger);
const results = await Promise.all(circuits.map(circuit => circuit.reconnect(reconnection)));
if (reconnectionFailed(results)) {
@ -81,14 +95,20 @@ async function boot(): Promise<void> {
}
}
async function initializeConnection(circuitHandlers: CircuitHandler[], logger: ILogger): Promise<signalR.HubConnection> {
async function initializeConnection(configureSignalR: SignalRBuilder | null, circuitHandlers: CircuitHandler[], logger: ILogger): Promise<signalR.HubConnection> {
const hubProtocol = new MessagePackHubProtocol();
(hubProtocol as any).name = 'blazorpack';
const connection = new signalR.HubConnectionBuilder()
const connectionBuilder = new signalR.HubConnectionBuilder()
.withUrl('_blazor')
.withHubProtocol(hubProtocol)
.configureLogging(signalR.LogLevel.Information)
.build();
.withHubProtocol(hubProtocol);
if (configureSignalR) {
configureSignalR(connectionBuilder);
}
const connection = connectionBuilder.build();
connection.on('JS.BeginInvokeJS', DotNet.jsCallDispatcher.beginInvokeJSFromDotNet);
connection.on('JS.RenderBatch', (browserRendererId: number, batchId: number, batchData: Uint8Array) => {
@ -131,4 +151,7 @@ function unhandledError(connection: signalR.HubConnection, err: Error, logger: I
}
}
boot();
window['Blazor'].start = boot;
if (shouldAutoStart()) {
boot();
}

View File

@ -6,9 +6,17 @@ import { getAssemblyNameFromUrl } from './Platform/Url';
import { renderBatch } from './Rendering/Renderer';
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
import { Pointer } from './Platform/Platform';
import { fetchBootConfigAsync, loadEmbeddedResourcesAsync } from './BootCommon';
import { fetchBootConfigAsync, loadEmbeddedResourcesAsync, shouldAutoStart } from './BootCommon';
let started = false;
async function boot(options?: any) {
if (started) {
throw new Error('Blazor has already started.');
}
started = true;
async function boot() {
// Configure environment for execution under Mono WebAssembly with shared-memory rendering
const platform = Environment.setPlatform(monoPlatform);
window['Blazor'].platform = platform;
@ -43,4 +51,7 @@ async function boot() {
platform.callEntryPoint(mainAssemblyName, bootConfig.entryPoint, []);
}
boot();
window['Blazor'].start = boot;
if (shouldAutoStart()) {
boot();
}

View File

@ -37,3 +37,10 @@ interface BootJsonData {
jsReferences: string[];
linkerEnabled: boolean;
}
// Tells you if the script was added without <script src="..." autostart="false"></script>
export function shouldAutoStart() {
return document &&
document.currentScript &&
document.currentScript.getAttribute('autostart') !== 'false';
}

View File

@ -1,8 +1,6 @@
import { platform } from './Environment';
import { navigateTo, internalFunctions as uriHelperInternalFunctions } from './Services/UriHelper';
import { internalFunctions as httpInternalFunctions } from './Services/Http';
import { attachRootComponentToElement } from './Rendering/Renderer';
import { Pointer } from './Platform/Platform';
// Make the following APIs available in global scope for invocation from JS
window['Blazor'] = {
@ -13,4 +11,4 @@ window['Blazor'] = {
http: httpInternalFunctions,
uriHelper: uriHelperInternalFunctions
}
};
};

View File

@ -210,6 +210,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\..\.editorconfig = ..\..\.editorconfig
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Mvc.Components.Prerendering", "..\Mvc\Mvc.Components.Prerendering\src\Microsoft.AspNetCore.Mvc.Components.Prerendering.csproj", "{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -1324,6 +1326,18 @@ Global
{9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x64.Build.0 = Debug|Any CPU
{9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x86.ActiveCfg = Debug|Any CPU
{9088E4E4-B855-457F-AE9E-D86709A5E1F4}.Release|x86.Build.0 = Debug|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x64.ActiveCfg = Debug|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x64.Build.0 = Debug|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x86.ActiveCfg = Debug|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Debug|x86.Build.0 = Debug|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|Any CPU.Build.0 = Release|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x64.ActiveCfg = Release|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x64.Build.0 = Release|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x86.ActiveCfg = Release|Any CPU
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1423,6 +1437,7 @@ Global
{04262990-929C-42BF-85A9-21C25FA95617} = {2FC10057-7A0A-4E34-8302-879925BC0102}
{DC47C40A-FC38-44E4-94A4-ADE794E76309} = {2FC10057-7A0A-4E34-8302-879925BC0102}
{9088E4E4-B855-457F-AE9E-D86709A5E1F4} = {7260DED9-22A9-4E9D-92F4-5E8A4404DEAF}
{3A4132B6-60DA-43A0-8E7B-4BF346F3247C} = {2FC10057-7A0A-4E34-8302-879925BC0102}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC3C47E1-AD1A-4619-9CD3-E08A0148E5CE}

View File

@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<title>Basic test app</title>
<base href="/subdir/" />
<base href="/subdir/" />
<link href="style.css" rel="stylesheet" />
</head>
<body>

View File

@ -15,7 +15,13 @@
<app>@(await Html.RenderComponentAsync<App>(new { Name="Guest" }))</app>
<script src="_framework/components.server.js"></script>
<script src="_framework/components.server.js" autostart="false"></script>
<script>
Blazor.start({
configureSignalR: function (builder) {
builder.configureLogging(2); // LogLevel.Information
}
});
</script>
</body>
</html>

View File

@ -15,15 +15,11 @@
interactive states, we only load the .js file when told to.
*@
<hr />
<button id="load-boot-script" onclick="loadBootScript(event)">Load boot script</button>
<script>
function loadBootScript(event) {
event.srcElement.disabled = true;
var scriptElem = document.createElement('script');
scriptElem.src = '_framework/components.server.js';
document.body.appendChild(scriptElem);
}
<button id="load-boot-script" onclick="Blazor.start()">Load boot script</button>
<script src="_framework/components.server.js" autostart="false"></script>
<script>
// Used by InteropOnInitializationComponent
function setElementValue(element, newValue) {
element.value = newValue;