Hosting Blazor on Azure Storage with an Azure Functions backend

With a Blazor app, the complete code runs on the client. Blazor (similar to other SPA frameworks) app can use a new feature from Azure Storage: static website hosting. Hosting websites with Azure Storage is a cheap way for static website hosting. In the backend, Azure Functions can be used – which only costs when invoked.

This blog post shows how to host a Blazor app on Azure Storage calling an API service on the backend with Azure Functions.

Disks

Prepare Azure storage

First, let’s create a new Azure Storage account. After logging into the Microsoft Azure portal, you can create an Azure Storage account. To use static website hosting, the account needs to be a StorageV2 account.

Create Azure Storage

After creating this Azure Storage account, you can configure it to enable static websites. You can find this configuration in the Settings tab – select Static website, and click the button Enabled, and add an entry point for the application, e.g Index.html:

Configure Static Website

With the feature Static website enabled, you can see the the primary endpoint that can be used to access the website as soon as the needed files are deployed. A container named $web is created – you need to upload the files needed by the website into this container.

Primary Endpoint

Create a Blazor project

For the Website that will be uploaded, let’s use Blazor. Currently, Blazor is experimental code and shouldn’t be used in production. Blazor uses the syntax of ASP.NET Core Razor Pages, but runs everything in the client browser. Using the HTML technology WebAssembly, and the Mono runtime. The Mono runtime was implemented running WASM bytecode. This allows running binary code in the browser – the browsers needs to support WebAssembly. All modern browsers support Webassembly, as you can see with caniuse.com.

To create a Blazor project, install the Visual Studio extension ASP.NET Core Blazor Language Service. With this extension, you have the option Blazor when creating a new ASP.NET Core Web Application.

Blazor option

The other Blazor template Blazor (ASP.NET Core hosted) creates three projects – a shared library that is used both by a Blazor and a ASP.NET Core project, the Blazor project, and an ASP.NET Core project that is used to host the Blazor application, and also hosts a Web API controller that is accessed by the Blazor application.

Deployment of the Blazor Project

With Publish from Visual Studio, publishing to Azure Storage is not yet offered. You can publish to Azure App Services and Azure Virtual Machines. For publishing to Azure Storage, you need to select Folder, and publish all the needed files to this folder.

Publish to Folder

To upload files to Azure Storage, multiple options exist, but with most options one by one file needs to be uploaded to the storage account. To upload multiple files at once, you can use Visual Studio Code. The extension Azure Storage allows to manage your Azure Storage Accounts, and to upload files to deploy the files for a static website. Be sure to select a subfolder in the publish directory containing the Index.html file as well as the _framework and css subfolders.

After installing the extension Azure Storage vor Visual Studio Code, click on the Azure icon on the left, and then the upload button to upload the files. Because in the portal Azure Storage has been configured to enable static websites, the container named $web is created in the Azure Storage account. This is the container where the upload goes to.

The extension Azure Storage for Visual Studio Code s currently in preview. I’m using version 0.4. To enable the multi-file upload to static websites, you need to define the user setting “azureStorage.preview.staticWebsites”: true.

Upload files using Visual Studio Code

After deploying completes, you can access the Blazor app using the link from the Azure Portal.

Blazor App running in Azure Storage

Using Azure Functions

Next, let’s create an API that is invoked by the Blazor app. Using services hosted in Azure App Services is not really useful when saving money by hosting the Blazor website with Azure Storage. In case you use Azure App Services, you can also host the Blazor website with the same hosting plan (but possible a different Web app). Saving money using Azure Storage, you can also save money with the backend services by not to reserving CPU and memory, but using a server-less offering instead, e.g. Azure Functions.

I’m creating a **Function App* from the Azure Portal (you can also create it directly from Visual Studio 2017).

Create Functions App using Azure Portal

With Function Apps you can choose between a Consumption Plan and an App Service Plan. Depending on the number of requests and the data you download, one or the other plan is the better option to use. If you don’t have continuous load on your service, the Consumption Plan can be the cheaper one. In case you already run an App Service Plan that is not fully busy, you can use it host your Functions there as well. With Azure Functions, you don’t need to change your API if the load changes, you can easily switch the Hosting Plan.

You just need to be aware that some features of Azure Functions need an App Service Plan instead of a Consumption Plan. For example, using the Consumption Plan, a request is limited with the maximum amount of time it can run.

Using Visual Studio 2017, you can use a project template to create Azure Functions. With Azure Functions v2, a .NET Standard Library is created.

Azure Functions offer different ways to activate them. Calling it from the Blazor app, an HTTP Trigger can be used to activate the Function App. With HTTP triggers, a Storage Account is not needed. I’m selecting access rights anonymous to allow anonymous users to access to the list of books returned from the function app.

Create Azure Functions with HTTP Trigger

In addition to the Functions App, a shared library is useful to hold the model type (the Book class, and probably some business logic. This library can be used both from the Function App as well as the Blazor App showing the books.

With the sample code, the Book class holds a few properties. Using the default Blazor JSON deserialization, a public default constructor is needed:


public class Book
{
public Book() { }
public Book(int bookId, string title, string publisher)
=> (BookId, Title, Publisher) = (bookId, title, publisher);
public int BookId { get; set; }
public string Title { get; set; }
public string Publisher { get; set; }
public override string ToString() => Title;
}

view raw

Book.cs

hosted with ❤ by GitHub

The implementation of the Azure function returns a list of Book objects:


public static class BooksFunction
{
[FunctionName("BooksFunction")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequest req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
return new OkObjectResult(GetBooks());
}
public static IEnumerable<Book> GetBooks()
=> new List<Book>
{
new Book(1, "Enterprise Services with the .NET Framework", "Addison Wesley"),
new Book(2, "Professional C# 6 and .NET Core 1.0", "Wrox Press"),
new Book(3, "Professional C# 7 and .NET Core 2.0", "Wrox Press")
};
}

Configuring CORS with Azure Functions

What needs to be done with the Azure Function to enable it to be invoked from Blazor is to configure CORS. You can configure CORS from the Azure Portal selecting the name of the Function App, and selecting the tab Platform features. From there, the API configuration CORS is available. You need to enter the URL used by the Blazor app to allow the Blazor app accessing the Azure Function.

Configure CORS

Call Azure Function from Blazor

To invoke the Azure Function from Blazor, the Blazor app is extended. With the Books component, the HttpClient class is injected. The HttpClient is registered in the Blazor dependency injection container, and makes use of the browser’s XMLHttpRequest object – it’s not possible to leave the browser context.

In the OnInitAsync method, the Http object is used to get the books from the Azure Function. This code fills the books field, that in turn is accessed from the Razor code to display all books.


@page "/books"
@using BooksLib
@inject HttpClient Http
<h1>Books Sample</h1>
<p>This component demonstrates fetching data from Azure Functions.</p>
<p>Status: @message</p>
@if (books == null)
{
<p><em>Loading…</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Title</th>
<th>Publisher</th>
</tr>
</thead>
<tbody>
@foreach (var book in books)
{
<tr>
<td>@book.Title</td>
<td>@book.Publisher</td>
</tr>
}
</tbody>
</table>
}
@functions {
Book[] books;
string message;
protected override async Task OnInitAsync()
{
message = "OnInitAsync";
Http.BaseAddress = new Uri("https://blazorapi.azurewebsites.net&quot;);
books = await Http.GetJsonAsync<Book[]>("/api/BooksFunction");
message = "downloaded books";
}
}

view raw

Books.cshtml

hosted with ❤ by GitHub

Summary

Why using Azure Storage instead of Azure App Services with static web content? It’s just a lot cheaper using Azure Storage instead of an App Service, and it scales big. With the backend, Azure Functions can be used – also offering a cheap variant compared to App Services, and this as well offers big scaling.

Blazor is currently experimental, and static website hosting from Azure Storage is in preview – but everything looks very promising. You can use static website hosting from other technologies, e.g. with Angular as well.

More Information

Read chapter 32, Web API of my new book Professional C# 7 and .NET Core 2.0 on how to share code between an ASP.NET Core Web API project and an Azure Function app.

Static website hosting in Azure Storage

Blazor

Get the complete sample in the Azure folder from More Samples!

Enjoy programming and learning,
Christian

5 thoughts on “Hosting Blazor on Azure Storage with an Azure Functions backend

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.