Reputation: 31
I've tried to search answers and found many, but those are working for older versions of .Net framework. I use 6.0.3.
My first approach was to find the function or procedure that gets called upon connection loss, tried to override the default reconnection function in the _Layout.cshtml file and found many solutions on various forums but those just simply don't work in my case.
Please don't hurt me, I'm not even a developer, but managed to implement all the features. This is the last needed thing.
Upvotes: 3
Views: 3583
Reputation: 31
We had a similar issue with our Blazor Server. The server, which was using default settings, would disconnect and the user would have to manually hit "refresh" to reconnect. We found that to be a poor user experience.
Finally found some example code, written by Microsoft, to reconnect the browser when the Blazor server disconnects, including refreshing the page if needed. Microsoft provides examples for both Blazor Web App and Blazor Server, reproduced here for ease of use.
The following code examples were copied from https://learn.microsoft.com/en-us/aspnet/core/blazor/fundamentals/signalr?view=aspnetcore-8.0#modify-the-server-side-reconnection-handler
App.razor:
<div id="reconnect-modal" style="display: none;"></div>
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script src="boot.js"></script>
Create the following wwwroot/boot.js file.
Blazor Web App:
(() => {
const maximumRetryCount = 3;
const retryIntervalMilliseconds = 5000;
const reconnectModal = document.getElementById('reconnect-modal');
const startReconnectionProcess = () => {
reconnectModal.style.display = 'block';
let isCanceled = false;
(async () => {
for (let i = 0; i < maximumRetryCount; i++) {
reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;
await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
if (isCanceled) {
return;
}
try {
const result = await Blazor.reconnect();
if (!result) {
// The server was reached, but the connection was rejected; reload the page.
location.reload();
return;
}
// Successfully reconnected to the server.
return;
} catch {
// Didn't reach the server; try again.
}
}
// Retried too many times; reload the page.
location.reload();
})();
return {
cancel: () => {
isCanceled = true;
reconnectModal.style.display = 'none';
},
};
};
let currentReconnectionProcess = null;
Blazor.start({
circuit: {
reconnectionHandler: {
onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
onConnectionUp: () => {
currentReconnectionProcess?.cancel();
currentReconnectionProcess = null;
}
}
}
});
})();
Blazor Server:
(() => {
const maximumRetryCount = 3;
const retryIntervalMilliseconds = 5000;
const reconnectModal = document.getElementById('reconnect-modal');
const startReconnectionProcess = () => {
reconnectModal.style.display = 'block';
let isCanceled = false;
(async () => {
for (let i = 0; i < maximumRetryCount; i++) {
reconnectModal.innerText = `Attempting to reconnect: ${i + 1} of ${maximumRetryCount}`;
await new Promise(resolve => setTimeout(resolve, retryIntervalMilliseconds));
if (isCanceled) {
return;
}
try {
const result = await Blazor.reconnect();
if (!result) {
// The server was reached, but the connection was rejected; reload the page.
location.reload();
return;
}
// Successfully reconnected to the server.
return;
} catch {
// Didn't reach the server; try again.
}
}
// Retried too many times; reload the page.
location.reload();
})();
return {
cancel: () => {
isCanceled = true;
reconnectModal.style.display = 'none';
},
};
};
let currentReconnectionProcess = null;
Blazor.start({
reconnectionHandler: {
onConnectionDown: () => currentReconnectionProcess ??= startReconnectionProcess(),
onConnectionUp: () => {
currentReconnectionProcess?.cancel();
currentReconnectionProcess = null;
}
}
});
})();
Upvotes: 2
Reputation: 1138
This worked for my purposes. I typically develop using dotnet watch run
for hot reloads and allow always
for rude exits. I found it frustrating waiting for the slow reconnect attempts while the app would rebuild if there was a rude exit, and also don't think the user experience would be good for them to see that "reload" link.
I had to work backward through the compiled typescript to figure this out: TypeScript source. The ReconnectionDisplay
(DefaultReconnectionDisplay source) gets initialized within the implementation; I could not find any clean way to gain access to replace the methods:
show()
hide()
update(currentAttempt: number)
failed()
rejected()
This approach with infinite reload attempts keeps me from ending up at a "Site can't be reached" page. The rejected
method seems to get called when the server comes back online and the state was lost on the server, as apposed to attempt timeouts. Reducing the retry interval seems to help to refresh the page sooner as well.
The reason I call defaultReconnectionHandler.onConnectionDown
is that the _reconnectionDisplay
gets constructed and assigned within that method, and then I replace the rejected
call. I suppose reassigning the rejected
call on every iteration is not the best way to do it, but it's a small enough footprint with infrequent iterations and will get garbage collected.
Don't forget to disable autostart
or you'll get an error message that tells you Blazor is already running.
<script src="_framework/blazor.server.js" autostart="false"></script>
<script type="text/javascript">
Blazor.start({
reconnectionOptions: {
maxRetries: Infinity,
retryIntervalMilliseconds: 100
},
reconnectionHandler: {
onConnectionDown: (options, error) => {
Blazor.defaultReconnectionHandler.onConnectionDown(options, error);
Blazor.defaultReconnectionHandler._reconnectionDisplay.rejected = function () {
window.location.reload();
}
}
}
});
</script>
Upvotes: 0
Reputation: 2044
Check out the Blazor Documentation on modifying the reconnection handler. It includes a handler for when the connection is dropped as well as for reconnection. You could probably modify their sample to call location.reload() instead of logging to the console. Just make sure your app won't get stuck in an endless loop for clients that have broken websockets support (if for some reason they can connect, but always drop the connection immediately).
Upvotes: 2