© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
P. HimschootMicrosoft Blazorhttps://doi.org/10.1007/978-1-4842-7845-1_15

15. Deploying Your Blazor Application

Peter Himschoot1  
(1)
Melle, Belgium
 

At a certain point in time, your Blazor application will be ready for the big public. Yeah! But the work is not yet done. We need to take our application and copy it to a server connected to the network so other people can use their browser to admire your work! Let us look at how we can deploy our Blazor application.

Deploying Standalone Blazor WebAssembly

When your Blazor application does not require any server support, you can host the application just like any other static website. In this case, the host just needs to serve the files to the browser since everything is executed on the browser.

Hosting on GitHub

GitHub is a free service that allows you to collaborate with others on a development project. It has support for git source control, builds automation, and allows you to host static websites, all free of charge.

Note

If you are not familiar with git source control, there is an excellent book available for free digitally at https://git-scm.com/book/en/v2.

Here, we will host our Blazor application on GitHub, and the process is similar for other static hosting platforms. There are many other excellent hosting solutions out there, but I had to pick one, and GitHub is widely known in the developer community.

Using GitHub requires some knowledge about git. If all of this is familiar, great. If not, the walk-through gives you the git commands you need to execute.

If you don’t have a GitHub account, you will need to create one on https://github.com/. Because modern websites have the tendency to change how they look, I won’t be using screenshots here, but the process should explain itself.

Once you have an account, you should create an organization at https://github.com/settings/organizations. GitHub allows you to have multiple organizations, and each can host a static website. Select a unique name for your organization; here, I will use the MicrosoftBlazorBook organization. After creating the organization, select it. Your browser will show the organization’s page, for example, https://github.com/MicrosoftBlazorBook.

Here, you can find a list of repositories. A repository will host all your sources and their history as you make changes to files using git source control. Since you just created the organization, you will have to create a new repository. Click the New button; give your repository a nice name and description. You should also choose if you want the repository to be public (anyone can see your code) or private. The deployment process is the same for either, so pick one. Complete creating the repository, but don’t add any files like README.

After completion, GitHub will show you a page that displays the command-line commands you can use to create the repository locally.

Note

I will be using Windows Terminal here, which has built-in support for PowerShell commands. All commands should work well with Linux and OSX command line.

On your local machine, create a folder where you want your project to go, open a command line on that folder, and execute the commands shown in GitHub there (just use copy-paste). For example, my organization is called MicrosoftBlazorBook and the repository is StandAloneWASM:
echo "# StandAloneWASM" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/MicrosoftBlazorBook/StandAloneWASM.git
git push -u origin main

First, this will create a README.md file in the current folder, and then this will create a git repository in the current folder. Next, this adds the README.md file to the repository, creates a new commit with a comment, and finally pushes the repository to the GitHub server. Now we are ready to deploy a static website.

Creating a Simple Website

Add a new index.html file in your folder with some simple content like Listing 15-1 .
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>Hello world!</h1>
</body>
</html>
Listing 15-1

A Basic HTML File

Since we made a change to your site, we will upload these changes into GitHub using git in the command line.

First, you need to add the modifications to git by executing the git add . command. Don’t forget the . which will make git add all changes in the current folder and subfolders to the commit when we create it, so make sure you are in the project’s folder where the README.md file is.
git add .
Now we need to take all these changes and group them into a commit with
git commit -m "Step 1"
A git commit is all the changes grouped with meta-data, including a mandatory message which we set using the -m parameter. And finally, we can send our changes to the GitHub repository using
git push

When you refresh the GitHub repository page, you should see index.html.

Deploying a Simple Site in GitHub

To host your simple website in GitHub, we need to select a specific branch it should host. Think of a branch like a separate copy of your files with its own history. Branches are normally used so developers can work on new features without bothering other developers. When the feature is complete, we can take the changes and apply it to the main branch where everyone will merge their changes. You can select the branch using the https://github.com/MicrosoftBlazorBook/StandAloneWASM/settings/pages page, but there is another way. If the branch is named gh-pages, then GitHub picks that branch automatically. Run the following commands to create a gh-pages branch locally, choose the gh-pages as the current branch using the checkout command, and push it to the GitHub repository on the server:
git branch gh-pages
git checkout gh-pages
git push --set-upstream origin gh-pages

After executing these commands, the deployment process will start, and by refreshing the pages page (the preceding URL), you can see the status. Refresh until GitHub tells you it is ready.

Click the link (e.g., https://microsoftblazorbook.github.io/StandAloneWASM/), and you should see your static website in action!

Deploying a Blazor WASM Project

Let us create and deploy a standalone Blazor WASM project now. First, we need to use the main branch:
git checkout main
Using the command line, create a new Blazor WASM project:
dotnet new blazorwasm

When we compile our Blazor project, two new folders will be created: obj and bin. We don’t need to keep these folders in source control, and an easy way to do this is by telling git to ignore these. Since this is a common scenario, we can use

dotnet new gitignore

Finally, we don’t need the index.html file from the previous part:
rm index.html
Now we can commit all our changes to GitHub with
git add .
git commit -m "Step 2"
git push
Now we are ready to publish our project. We will tell dotnet to create a release version using the -c option and put it into a release folder using the -o option. Publishing will optimize our Blazor WASM project by removing all unneeded code and assemblies, making the initial download smaller. This will take longer to build and shorter to load the Blazor site in the browser.
dotnet publish -c Release -o release
In the next step, we will copy the release folder into the gh-pages branch. Start by moving the release folder to a temporary folder outside our local repository:
mv release ../temp
Now check out the gh-pages branch:
git checkout gh-pages
Let us look at the files in our gh-pages branch:
ls
You will see that we have a bin and obj folder which we don’t need:
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         8/21/2021  12:03 PM                bin
d-----         8/21/2021  12:03 PM                obj
-a----         8/21/2021  12:11 PM            285 index.html
-a----         8/21/2021  11:13 AM             38 README.md
Remove these folders:
rm -r bin
rm -r obj
We can now inspect the publish folder using the tree command:
tree ../temp /F
This will show us all the files that make up our application:
Folder PATH listing for volume Local Disk
Volume serial number is 1044-BB65
C:\CODE\GITHUB\MICROSOFT.BLAZOR.3RD\CH15\TEMP
│   web.config
└───wwwroot
    │   favicon.ico
    │   icon-192.png
    │   index.html
    │   StandAlone.styles.css
    │
    ├───css
    │   │   app.css
    │   │
    │   ├───bootstrap
    │   │       bootstrap.min.css
    │   │       bootstrap.min.css.map
    │   │
    │   └───open-iconic
    │       │   FONT-LICENSE
    │       │   ICON-LICENSE
    │       │   README.md
    │       │
    │       └───font
    │           ├───css
    │           │       open-iconic-bootstrap.min.css
    │           │
    │           └───fonts
    │                   open-iconic.eot
    │                   open-iconic.otf
    │                   open-iconic.svg
    │                   open-iconic.ttf
    │                   open-iconic.woff
    │
    ├───sample-data
    │       weather.json
    │
    └───_framework
            blazor.boot.json
            blazor.boot.json.br
            blazor.boot.json.gz
            blazor.webassembly.js
            blazor.webassembly.js.br
            blazor.webassembly.js.gz
            dotnet.6.0.0-preview.7.21377.19.js
            dotnet.6.0.0-preview.7.21377.19.js.br
            dotnet.6.0.0-preview.7.21377.19.js.gz
            dotnet.timezones.blat
            dotnet.timezones.blat.br
            dotnet.timezones.blat.gz
            dotnet.wasm
            dotnet.wasm.br
            dotnet.wasm.gz
            icudt.dat
            icudt.dat.br
            icudt.dat.gz
            icudt_CJK.dat
            icudt_CJK.dat.br
            icudt_CJK.dat.gz
            icudt_EFIGS.dat
            icudt_EFIGS.dat.br
            icudt_EFIGS.dat.gz
            icudt_no_CJK.dat
            icudt_no_CJK.dat.br
            icudt_no_CJK.dat.gz
            Microsoft.AspNetCore.Components.dll
            Microsoft.AspNetCore.Components.dll.br
            Microsoft.AspNetCore.Components.dll.gz
            Microsoft.AspNetCore.Components.Web.dll
            Microsoft.AspNetCore.Components.Web.dll.br
            Microsoft.AspNetCore.Components.Web.dll.gz
            Microsoft.AspNetCore.Components.WebAssembly.dll
            Microsoft.AspNetCore.Components.WebAssembly.dll.br
            Microsoft.AspNetCore.Components.WebAssembly.dll.gz
            Microsoft.Extensions.Configuration.Abstractions.dll
            Microsoft.Extensions.Configuration.Abstractions.dll.br
            Microsoft.Extensions.Configuration.Abstractions.dll.gz
            Microsoft.Extensions.Configuration.dll
            Microsoft.Extensions.Configuration.dll.br
            Microsoft.Extensions.Configuration.dll.gz
            Microsoft.Extensions.Configuration.Json.dll
            Microsoft.Extensions.Configuration.Json.dll.br
            Microsoft.Extensions.Configuration.Json.dll.gz
            Microsoft.Extensions.DependencyInjection.Abstractions.dll
            Microsoft.Extensions.DependencyInjection.Abstractions.dll.br
            Microsoft.Extensions.DependencyInjection.Abstractions.dll.gz
            Microsoft.Extensions.DependencyInjection.dll
            Microsoft.Extensions.DependencyInjection.dll.br
            Microsoft.Extensions.DependencyInjection.dll.gz
            Microsoft.Extensions.Logging.Abstractions.dll
            Microsoft.Extensions.Logging.Abstractions.dll.br
            Microsoft.Extensions.Logging.Abstractions.dll.gz
            Microsoft.Extensions.Logging.dll
            Microsoft.Extensions.Logging.dll.br
            Microsoft.Extensions.Logging.dll.gz
            Microsoft.Extensions.Options.dll
            Microsoft.Extensions.Options.dll.br
            Microsoft.Extensions.Options.dll.gz
            Microsoft.Extensions.Primitives.dll
            Microsoft.Extensions.Primitives.dll.br
            Microsoft.Extensions.Primitives.dll.gz
            Microsoft.JSInterop.dll
            Microsoft.JSInterop.dll.br
            Microsoft.JSInterop.dll.gz
            Microsoft.JSInterop.WebAssembly.dll
            Microsoft.JSInterop.WebAssembly.dll.br
            Microsoft.JSInterop.WebAssembly.dll.gz
            StandAlone.dll
            StandAlone.dll.br
            StandAlone.dll.gz
            StandAlone.pdb.gz
            System.Collections.Concurrent.dll
            System.Collections.Concurrent.dll.br
            System.Collections.Concurrent.dll.gz
            System.Collections.dll
            System.Collections.dll.br
            System.Collections.dll.gz
            System.ComponentModel.dll
            System.ComponentModel.dll.br
            System.ComponentModel.dll.gz
            System.Linq.dll
            System.Linq.dll.br
            System.Linq.dll.gz
            System.Memory.dll
            System.Memory.dll.br
            System.Memory.dll.gz
            System.Net.Http.dll
            System.Net.Http.dll.br
            System.Net.Http.dll.gz
            System.Net.Http.Json.dll
            System.Net.Http.Json.dll.br
            System.Net.Http.Json.dll.gz
            System.Net.Primitives.dll
            System.Net.Primitives.dll.br
            System.Net.Primitives.dll.gz
            System.Private.CoreLib.dll
            System.Private.CoreLib.dll.br
            System.Private.CoreLib.dll.gz
            System.Private.Runtime.InteropServices.JavaScript.dll
            System.Private.Runtime.InteropServices.JavaScript.dll.br
            System.Private.Runtime.InteropServices.JavaScript.dll.gz
            System.Private.Uri.dll
            System.Private.Uri.dll.br
            System.Private.Uri.dll.gz
            System.Runtime.CompilerServices.Unsafe.dll
            System.Runtime.CompilerServices.Unsafe.dll.br
            System.Runtime.CompilerServices.Unsafe.dll.gz
            System.Runtime.dll
            System.Runtime.dll.br
            System.Runtime.dll.gz
            System.Text.Encodings.Web.dll
            System.Text.Encodings.Web.dll.br
            System.Text.Encodings.Web.dll.gz
            System.Text.Json.dll
            System.Text.Json.dll.br
            System.Text.Json.dll.gz
The web.config file is used for Internet Information Services (IIS) deployments, but we don’t need it for GitHub. We only need the files from the wwwroot folder, so copy this folder in our gh-pages branch:
mv ..\temp\wwwroot\*
Git works for both Windows- and Unix-based operating systems. However, these use different file endings, and we don’t want git to change these. Why? Because the Blazor runtime will check if our files have been changed after deployment, and it will refuse to load these files. We can tell git not to make changes using a .gitattributes file, so add one using the following command:
"* binary" >> .gitattributes
This tells git to treat all our files as binary so it will not try to fix file endings. Commit these files and push them to the git repository on GitHub:
git add .
git commit -m "Step 3"
git push

Now you can reload the site (which will not work yet); for example, in my case, this would be https://microsoftblazorbook.github.io/StandAloneWASM/.

This will display Figure 15-1.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig1_HTML.jpg
Figure 15-1

Our Blazor Site Does Not Load Correctly (Yet)

Fix the Base Tag

Why is this not loading correctly? Open the browser debugger’s console, as shown in Figure 15-2.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig2_HTML.png
Figure 15-2

The Browser Debugger’s Console

As you can see, the browser is trying to load the JavaScript and CSS files from the root of https://microsoftblazorbook.github.io, but our files are hosted at https://microsoftblazorbook.github.io/StandAloneWASM/. What we need to do is to instruct the browser to prefix each file’s URL with StandAloneWASM. This is done through the index.html’s base tag. And if you remember from Chapter 9, routing also used this base tag to figure out which component to show! So use your favorite editor to update the base tag in index.html to use your repository’s name as in Listing 15-2.

Listing 15-2. Update the index.html’s Base Tag

../images/469993_3_En_15_Chapter/469993_3_En_15_Figa_HTML.gif../images/469993_3_En_15_Chapter/469993_3_En_15_Figb_HTML.gif

Now we can push our change to GitHub with the following commands:
git add .
git commit -m "Fix base tag"
git push

Wait for the deployment to complete and refresh your site’s page. You can review the deployment process at https://github.com/MicrosoftBlazorBook/StandAloneWASM/deployments/activity_log?environment=github-pages, replacing your organization and repository name in the URL.

Disabling Jekyll

Still not working. Again, let us look at the browser debugger’s console as shown in Figure 15-3. It is loading the JavaScript and CSS files, but it cannot find the _framework files.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig3_HTML.png
Figure 15-3

Blazor Does Not Find the _framework Files

Why? GitHub uses Jekyll (https://github.com/jekyll) which is a static site generator. Jekyll stores its files in folders that start with an underscore, and GitHub will not host files inside folders that start with an underscore. We can disable Jekyll by adding an empty .nojekyll file in the root folder. So use your favorite editor again to add this file and use the following commands to send this to GitHub:
git add .
git commit -m "Fix Jekyll"
git push

Wait for the deployment to complete and refresh your site’s page. Your Blazor site should work! Great!

Fixing GitHub 404s

There is still one problem we need to fix. Navigate in your Blazor site to the Counter route and make the browser refresh by hitting F5. It will display a 404 page! This is because GitHub will try to load the Counter file from the URL. We can fix this by copying our root index.html file to a 404.html file, which GitHub will then send back to the browser.

First, copy index.html to 404.html using this command:
cp index.html 404.html
Now we need to push this change back to the GitHub with these commands:
git add .
git commit -m "Fix 404 page"
git push

Now refreshing the counter route will work.

Note

Here, we have been deploying our standalone Blazor WebAssembly application by pushing changes in source control to GitHub. Some other hosts also allow you to do this too; for example, you can deploy your application using Azure DevOps. If you want to host your Blazor application on a host that does not have source control integration, you will have to upload the publish folder using the host its own tools; this might even be with FTP!

Alternatives for GitHub

There are many alternatives to deploy your Blazor Standalone WebAssembly project; each will have its own little quirks to make it work, but everyone will require you to set the base tag in index.html correctly. For example, you could also deploy your project as an Azure Static Website. For more information about deploying your project as an Azure Static Website, visit https://docs.microsoft.com/azure/static-web-apps/deploy-blazor.

Deploying Your Site As WebAssembly

With .NET 6, we can now compile our complete solution as a WASM file and run everything as WebAssembly. By default, you will run .NET assemblies in the browser where the WASM .NET runtime will interpret IL instructions. By compiling everything into WASM, you can get significant performance improvements! However, the WASM file is larger than the .NET assembly equivalent, so compiling everything as WASM will come at the cost of a longer initial download. This is also known as Ahead-Of-Time (AOT ) compilation . AOT mainly benefits applications that are CPU intensive, so you might not even need this for your application.

To enable AOT compilation, you should add the RunAOTCompilation flag to your project as shown in Listing 15-3.
<PropertyGroup>
  <TargetFramework>net6.0</TargetFramework>
  <Nullable>enable</Nullable>
  <RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
Listing 15-3

Enabling AOT Compilation

Now you can publish your application just like before with the publish command
dotnet publish -c Release -o release

This will take some time, so grab something to drink. While you are developing, AOT is not used because compiling takes so much longer.

Once deployment is ready, look inside the release/_framework folder and search for dotnet.wasm. This file on my machine is around 12 MB! Without OAT, this file is around 2 MB. Do note that the actual download is a lot smaller due to the compression used by Blazor. You will also find the original .dll files in the release folder. Sometimes your application might use reflection; in that case, the necessary .dll files are still downloaded. So we still need to deploy these.

We can now deploy our AOT compiled release just like before.

Deploying Hosted Applications

For both the hosted Blazor WebAssembly and Blazor Server applications, you will need to deploy to a host that supports executing .NET on the server. You can deploy this to Windows Internet Information Services (IIS) or to Linux Apache.

Understanding the Deployment Models

With ASP.NET Core hosted applications, we have a number of choices for deploying our application.

One option is to use a framework-dependent deployment . In this case, the deployment files only contain your application files with their dependencies. No runtime is deployed, so this will only work on a server where the .NET runtime has been deployed before. One advantage of using framework-dependent deployment is that your deployment will work everywhere since portable .NET assemblies are used.

The other option is to use a self-contained deployment . In this case, the deployment contains all the files that are needed to run the application, including the runtime. Because of this, you need to specify which platform you want to target, for example, 64-bit Windows, and it will only deploy to that platform. The main advantage of this is that there is no dependency on what has been installed on the server, except for the platform of course. Another advantage is that you can use any version of .NET, even previews. Most commercial hosts will only give you long-term support versions of the .NET runtime.

To create a deployment, you use the dotnet publish command. For example, to create a self-contained deployment for 64-bit Linux, you use
dotnet publish -c Release -o release --self-contained --runtime linux-x64
And if you want to create a portable framework-dependent deployment:
dotnet publish -c Release -o release –no-self-contained

Deploying to Microsoft Azure

Most of us don’t have a server lying around to deploy to, so here we will deploy to an Azure web app. If you don’t have an Azure account, you can get one for free. Open your browser and visit https://azure.microsoft.com/. Here, you can create a free account, and you even get $200 credit.

An Azure web app is a hosting service that makes it very easy to deploy and run your Blazor application.

We will use Visual Studio to create a release and deploy it into Azure. Create a new hosted Blazor WebAssembly project (or Blazor Server, your choice). Before we can deploy to Azure, we need to add our Azure account to Visual Studio. So open File ➤ Account Settings…. Click Add as in Figure 15-4 to add your Azure account.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig4_HTML.jpg
Figure 15-4

Add Your Azure Account

Creating the Publishing Profile

Right-click the server project and select Publish… from the drop-down menu. The Publish wizard will open, which gives you the choices of deployment targets. Choose Azure as in Figure 15-5.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig5_HTML.jpg
Figure 15-5

Deploy to Azure

Click Next. Now you are presented with deploying to an Azure web app, a container, or a virtual machine as shown in Figure 15-6.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig6_HTML.jpg
Figure 15-6

Azure Deployment Choices

Select Azure App Service (Windows) and click Next. Now the Select existing or create a new Azure App Service dialog from Figure 15-7 appears. You can either select an existing App Service or create a new one.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig7_HTML.jpg
Figure 15-7

Select Azure App Service

Click the + button to add a new Azure App Service. Figure 15-8 will be shown. Enter a unique name, select your Azure subscription if you have more than one, and create a new resource group and hosting plan.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig8_HTML.jpg
Figure 15-8

Create an App Service Dialog

A resource group groups together a bunch of Azure resources, such as a web app and its database, and allows you to manage and delete all of them as one. To create a new resource group, click New… and enter a new resource group name.

A hosting plan will select what kind of hardware your site will run on and the data center where the hardware resides. Click New… and enter a name, select a data center near you, and select the Free size (shown here as F1 in Figure 15-8) as in Figure 15-9. You can have up to ten free hosting plans per region for your subscription.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig9_HTML.jpg
Figure 15-9

Create a Hosting Plan

Click Next again and select the Publish option as in Figure 15-10. The other option will set up for you a Continuous Integration and Deployment using GitHub Actions which actually is a better option. With GitHub Actions, you can have your site deployed automatically every time you push new features into your repository. If you would like to learn more about GitHub Actions, visit https://github.com/features/actions, or if you prefer a book, read www.apress.com/gp/book/9781484264638.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig10_HTML.jpg
Figure 15-10

Publish or Use CI/CD

Select Finish.

Selecting Publishing Options

VS will now display the Publish profile as in Figure 15-11, and you can change some of the deployment options before proceeding.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig11_HTML.jpg
Figure 15-11

The Publish Profile

Click the Show all settings link, which will display the publish options as in Figure 15-12. With the Deployment Mode drop-down, you can choose between Framework-dependent or Self-Contained as discussed previously in this chapter. Please select Self-Contained and Save.
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig12_HTML.jpg
Figure 15-12

Publish Options

Publishing the Application

Now you can click the Publish button as shown in the top right corner of Figure 15-11. Visual Studio will build a release version and deploy it to Azure.

When publishing completes, an alert as in Figure 15-13 will be shown. Click the link to look at the result of the publish. Now everyone with an Internet connection can admire your work!
../images/469993_3_En_15_Chapter/469993_3_En_15_Fig13_HTML.jpg
Figure 15-13

Publish Complete

Summary

In this chapter, we looked at deploying a Blazor application. With a standalone Blazor WebAssembly application, all we need is a file server so the browser can download the html, CSS, JavaScript, and DLL files. As an example, we used GitHub to deploy to. Remember to set the base tag in the html page to match the location where the files are downloaded from.

Deploying a Blazor Server or Blazor WebAssembly hosted project is just like deploying an ASP.NET Core site. As an example, we deployed our application to Azure as a web app. Visual Studio takes care of most of the work. Without Visual Studio, we can still create a deployment using the command line, and then we would need to upload the files onto the server. Each hosting provider has their own specific way of doing this.