By Endy Tjahjono. Last update 26 Jul 2020.
Blazor WebAssembly (let’s shorten it to BWA) documentation already covered deployment gotcha to GitHub Pages (let’s shorten it to GHP). That is, since BWA uses the history.pushState
to manipulate URL for routing, if the user is not at the application root URL and refresh the browser, the user will get 404 error, because there is nothing at the actual server at that URL.
For GHP the solution is to provide a 404 page that redirects to application root URL, while sending the rest of the URL as query string. Then the BWA’s index.html
should be modified to capture the query string and give it to the application once the application is running. Rafael Pedicini explained this solution very clearly in his article. Steven Sanderson has a working example of BWA in GHP using this strategy.
Unfortunately for me, the provided solution doesn’t work as is. My GHP deployment has several differences from the given solution.
First difference is that I use GHP’s Jekyll to generate my website. BWA package has two folders that starts with underscore, the _framework
folder and the _bin
folder under it. Folders/files that start with underscore will not be deployed as is by Jekyll. Rafael’s example uses .nojekyll
file put at the website root folder to disable Jekyll for the entire website.
This would be fine if my website is used solely for the BWA app. But the BWA app is only a part of the website located in a subfolder. I tried putting .nojekyll
file at the root of the BWA folder instead of the website root folder, but it is ignored by GHP (_framework
folder is still not deployed).
The working solution is to add the two folders into the include
section in GHP’s _config.yml
:
include:
- "_bin"
- "_framework"
Three important things:
app/mybwa/_framework
(in my case), but it is registered as just _framework
."
._bin
is registered before _framework
.Miss any of the three and it will not work.
Second difference is my website source code contains mixed line endings (LF and CRLF both). And I haven’t set up a .gitattributes
file. It turned out BWA has integrity check for the package and will not run if any files in the package is tampered. Git altering line endings during commit will cause error like this in the browser:
Failed to find a valid digest in the ‘integrity’ attribute for resource ‘http://mydomain/app/mybwa/_framework/wasm/dotnet.3.2.0.js’ with computed SHA-256 integrity ‘blabla…’. The resource has been blocked.
Solution is to tell git to consider everything inside the BWA folder as binary. Need to add something like this in .gitattributes
file:
app/mybwa/** binary
Notice the double asterisks. It means consider as binary all files in said folder, and all subfolders under it.
Third difference is my app is in a subdirectory, not at the root of the website. GHP only uses the 404.html
at the root of the website. At first the 404.html
page was at the root of my app, and the redirect didn’t work. I had to move the 404.html
to the root of my website and modified the javascript to add a check: if the URL is for the BWA app, then proceed with the redirect, if not, then just tell the user the page is not found. The javascript ends up looking like this:
(function () {
var segments = window.location.pathname.split("/");
if (segments.length > 2 && String(segments[2]).toLowerCase() == "mybwa") {
var segmentCount = 2;
var wl = window.location;
var basePath = wl.pathname.split("/").slice(0, segmentCount + 1).join("/");
var restPath = wl.pathname.slice(1).split("/").slice(segmentCount).join("/").replace(/&/g, "~and~");
var search = wl.search ? "&q=" + wl.search.slice(1).replace(/&/g, "~and~") : "";
var targetUrl = wl.protocol + "//" + wl.hostname + (wl.port ? ":" + wl.port : "")
+ basePath + "/?p=/" + restPath + search + wl.hash;
wl.replace(targetUrl);
}
})();
So is my BWA running now? Not yet.
I did something else that caused the package integrity to fail. BWA needs to know the actual base URL it is running from in order to generate correct URLs. It is set in index.html
’s base
tag:
<base href="/app/mybwa/" />
But in the source code I set base
to /
like the default. I would really prefer not to include the deployment location in the source code. I believe it is deployment concern and not a development concern. If I give this app to 5 people I don’t want to compile for each person. Base path should be set in config file or environment variable, but I haven’t found a way to do that. Setting base
to /
simplifies debugging too.
When deploying I will publish the app and then change the published index.html
’s base
to /app/mybwa/
. This will break the integrity check. To make the app run I disabled integrity check by following Steven Sanderson’s note to add BlazorCacheBootResources
tag in the app’s .csproj
file:
<PropertyGroup>
<!-- ... -->
<BlazorCacheBootResources>false</BlazorCacheBootResources>
</PropertyGroup>
And after that my Blazor WebAssembly app runs on GitHub Pages.
Using Blazor WebAssembly version 3.2.0.