Intro
So, recently I had to redeploy new ASP.NET Core app few times and I don\’t want the user to stare at some ugly error page. I want something better, some nice page sayng website is under maintenance, and it would be nice if there would be progress of that operation.
Solution
My first try was ofcourse App_offline.htm. Simple static html page that you put in folder where your website is. When this App_offline.htm is present in that folder it would be shown to the user instead of your website. That\’s fine, now the user knows whats going on, but how long is the maintenance gona take? User don\’t know that and that might be a little bit frustrating for him.
What I came up with, is still simple html page, but with auto refresh(so when update is complete user will immediately see updated website), loader(it\’t just better when you see some wheel spinning :)) and progressbar.
You can see whole App_offline.htm page on my gist.
Autorefresh
Autorefresh feature is done with simple meta tag that will refresh page, in our case every 10 seconds:
1 |
<meta http-equiv="refresh" content="10"/> |
Spinner
Spinner is pure css, I got mine from https://loading.io/css/.
Progressbar
Progressbar is just html div with background color.
Updating progressbar
Now we got the page, but how are we gonna update the progress? It\’s quite simple. We need to use our App_offline.htm as template. That is why there is #progress#
placeholder. Every time we make progress in deployment(that is we copy some file) we update the template with percentage and move it in IIS website folder.
I wrote simple powershell script based on this post. Basicaly you get all files from your source(publish) directory with Get-ChildItem
and then move them on server one by one with Copy-Item
and every time you update App_offline.htm
aswell.
Usage is pretty simple, just run:
1 |
& Deploy-WithProgress.ps1 -Source 'C:\SomeApp\bin\Release\netcoreapp3.0\publish' -Destination '\\192.168.1.2\someApp' |
Here is whole script Deploy-WithProgress.ps1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
param( [Parameter(Mandatory = $true)] [string]$Source, [Parameter(Mandatory = $true)] [string]$Destination ) $global:appOfflineGistUrl = 'https://gist.githubusercontent.com/TomasBouda/176632ebcccaf684ea09231935f4d300/raw/ff6662b3131ed43fe206403b62ec2129fae81a83/App_offline.htm' function Copy-WithProgress { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] $Source, [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)] $Destination ) begin{ $template = Invoke-RestMethod -Method GET -Uri $global:appOfflineGistUrl } process{ $filelist = Get-ChildItem $Source -Recurse $total = $filelist.count $position = 0 foreach ($file in $filelist) { $filename = $file.Fullname.Replace($Source, '') $destinationFile = $Destination + $filename Copy-Item $file.FullName -Destination $destinationFile -Force $position++ $progress = [Math]::Round((($position / $total) * 100), 0) $template -replace '#progress#', $progress | Set-Content "$Destination\App_offline.htm" Write-Progress -Activity "Copying data from $Source to $Destination" -Status "Copying File $filename" -PercentComplete $progress } } } Copy-WithProgress -Source $Source -Destination $Destination # Remove App_offline.htm to finish the process and let users browse updated website Remove-Item "$Destination\App_offline.htm" -Force -ErrorAction SilentlyContinue |
When all files are transfered App_offline.htm
is removed and because there is a refresh meta tag on the same page, so it will be refreshed into newly updated website.
Conclusion
I\’m very satisfied whith this maintenance page. Not only that it shows usefull information to the user, but it even get the user immediately on website when update is done. I hope it helps someone build better maintenance page and make users happier. Feel free to edit the html template and powershell script aswell. Happy coding!