We will create a .NET solution involving a Blazor WASM project that communicates with a WebAPI project through REST calls. We will then dockerize both projects and have them orchestrated through docker-compose for testing purposes. Finally, we will deploy both images to Azure Container Apps.
Source Code: https://github.com/medhatelmasry/BlazorApiACR
Prerequisites
- .NET 9.0
- Azure CLI
- Visual Studio Code
- Docker Desktop
Getting Started
We will run terminal window commands that will create the following:
- A solution folder named BlazorApiACR
- A Minimal API project named WeatherApiBackend
- A Blazor WASM project named BlazorFrontend
Run the following commands in a suitable working directory:
mkdir BlazorApiACR
cd BlazorApiACR
dotnet new sln
dotnet new webapi --no-https -o WeatherApiBackend
dotnet sln add WeatherApiBackend/WeatherApiBackend.csproj
dotnet new blazorwasm --no-https -o BlazorFrontend
dotnet sln add BlazorFrontend/BlazorFrontend.csproj
Enable CORS in the WebAPI project
In the WeatherApiBackend/Program.cs file, add this code right before “var app = builder.Build();”:
// Add CORS Service
builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
Also, in WeatherApiBackend/Program.cs, add this code right after “var app = builder.Build();”:
app.UseCors("CorsPolicy");
Link Frontend to Backend
We must change the Weather Forecast endpoint so that it points to the WebAPI endpoint.
- View WeatherApiBackend/Properties/launchSettings.json and note the value of applicationUrl. Although I will be using http://localhost:5025, you must adjust the URL to your environment.
- Edit BlazorFrontend/Program.cs and change this statement:
builder.Services.AddScoped( sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
TO
builder.Services.AddScoped(
sp => new HttpClient
{
BaseAddress = new Uri("http://localhost:5025/")
}
);
NOTE: Make sure you use the port number in your environment instead of 5085.
- Edit BlazorFrontend/Pages/Weather.razor. Change:
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
TO
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
Test Locally
Start the backend with:
cd WeatherApiBackend
dotnet run
Start the frontend with:
cd BlazorFrontend
dotnet watch
The Blazor app opens in your default browser. Click on the Weather tab.
Create a text file WeatherApiBackend/Dockerfile and add to it this code for the backend WebAPI project:
# WeatherApiBackend/Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["WeatherApiBackend.csproj", "."]
RUN dotnet restore "WeatherApiBackend.csproj"
COPY . .
RUN dotnet build -c Release -o /app/build
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80/tcp
EXPOSE 443/tcp
ENTRYPOINT ["dotnet", "WeatherApiBackend.dll"]
# docker build -t weatherapi .
# docker run -p 8080:80 weatherapi
Create a text file BlazorFrontend/Dockerfile and add to it this code for the frontend Blazor WASM project:
# BlazorFrontend/Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["BlazorFrontend.csproj", "./"]
RUN dotnet restore "BlazorFrontend.csproj"
COPY . .
RUN dotnet build "BlazorFrontend.csproj" -c Release -o /app/build
RUN dotnet publish "BlazorFrontend.csproj" -c Release -o /app/publish
FROM nginx:alpine AS final
WORKDIR /var/www/web
COPY --from=build /app/publish/wwwroot .
COPY ./nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
# docker build -t blazorfrontend .
# docker run -p 8080:80 blazorfrontend
The frontend will be hosted in the Docker image by the nginx web server. This requires some basic configuration. Therefore, add a text file BlazorFrontend/nginx.conf with this content:
events { }
http {
include mime.types;
types {
application/wasm wasm;
}
server {
listen 80;
index index.html;
location / {
root /var/www/web;
try_files $uri $uri/ /index.html =404;
}
}
}
Inside of the BlazorApiACR solution folder, add a text file named docker-compose.yml with this code:
services:
webapi:
build:
context: ./WeatherApiBackend
dockerfile: Dockerfile
ports:
- "5025:80"
restart: always
environment:
- ASPNETCORE_ENVIRONMENT=Development
networks:
- blazor-network
wasm:
build:
context: ./BlazorFrontend
dockerfile: Dockerfile
depends_on:
- webapi
ports:
- 8888:80
networks:
- blazor-network
networks:
blazor-network:
driver: bridge
# docker compose up --build
# docker compose down
NOTE: In the docker-compose.yml, change the port 5025 to match the port number for the WebAPI backend in your environment.
The docker-compose.yml will build Docker images for both the backend and frontend projects and orchestrate them to work together in a virtual network.
Make sure you start Docker Desktop. You will then run this command from inside of the BlazorApiACR solution folder:
docker compose up --build
Wait until activity in the terminal window settles down. Thereafter, visit http://localhost:8888 in your browser. Thiw is what the Weather tab in the Blazor app will look like:
Prepare Deployment to Azure Container Apps
We will be using The Azure CLI tool az.
a. Login to Azure using the az tool:
az login
b. Create Azure Resource Group:
az group create --name <RESOURCE_GROUP> --location <LOCATION>
az group create --name blazor-api-acr-RG --location "East US"
c. Create Azure Container Registry (ACR):
az acr create --resource-group <RESOURCE_GROUP> --name <ACR_NAME> --sku Basic
az acr create --resource-group blazor-api-acr-RG --name blapiacr --sku Basic
d. Login to Azure Container Registry:
az acr login --name <ACR_NAME>
az acr login --name blapiacr
Build and Push Backend WebAPI Image
a. Build backend WebAPI image:
docker build --platform linux/amd64 -t <ACR_NAME>.azurecr.io/backend:latest ./backend
docker build --platform linux/amd64 -t blapiacr.azurecr.io/weatherapi:latest ./WeatherApiBackend
b. Push backend WebAPI image to ACR:
docker push <ACR_NAME>.azurecr.io/backend:latest
docker push blapiacr.azurecr.io/weatherapi:latest
c. Register required providers:
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights
d. Create an environment for Container Apps:
az containerapp env create --name <ENV_NAME> --resource-group <RESOURCE_GROUP> --location <LOCATION>
az containerapp env create --name blapi-container-app-env --resource-group blazor-api-acr-RG --location "East US"
NOTE: This takes a while to process, so be patient.
e. Get admin credentials (username and password) to access the registry.
az acr update -n <ACR_NAME>--admin-enabled true
az acr update -n blapiacr --admin-enabled true
f. Deploy backend app (public):
az containerapp create \--name backend-app \--resource-group <RESOURCE_GROUP> \--environment <ENV_NAME> \--image <ACR_NAME>.azurecr.io/backend:latest \--target-port 4000 \--ingress external \--registry-server <ACR_NAME>.azurecr.ioaz containerapp create \--name backend-app \--resource-group blazor-api-acr-RG \--environment blapi-container-app-env \--image blapiacr.azurecr.io/weatherapi:latest \--target-port 80 \--ingress external \--registry-server blapiacr.azurecr.io
g Find backend URL
az containerapp show \--name backend-app \--resource-group <RESOURCE_GROUP> \--query properties.configuration.ingress.fqdn
az containerapp show \--name backend-app \--resource-group blazor-api-acr-RG \--query properties.configuration.ingress.fqdn
I got the following response for the WebAPI backend:
This meants that the deployed backend can be accessed at:
https://backend-app.nicebush-967940f0.eastus.azurecontainerapps.io/weatherforecast
I pointed by browser to the above URL and experienced this response:
We now know that our WebAPI is deployed to Azure Container Apps and is alive and well. The next step is to modify our frontend app BaseAddress with the address of the backend.
Build and Push Frontend Blazor Image
a. Edit BlazorFrontend/Program.cs and and update the BaseAddress with the address of the deployed WebAPI backend. In my case I changed the statement to:
BaseAddress = new Uri("https://backend-app.nicebush-967940f0.eastus.azurecontainerapps.io/")
docker build - --platform linux/amd64 t <ACR_NAME>.azurecr.io/frontend:latest ./frontend
docker build --platform linux/amd64 -t blapiacr.azurecr.io/blazorclient:latest ./BlazorFrontend
docker push <ACR_NAME>.azurecr.io/frontend:latest
docker push blapiacr.azurecr.io/blazorclient:latest
d. Deploy frontend Blazor app (public):
az containerapp create \--name frontend-app \--resource-group <RESOURCE_GROUP> \--environment <ENV_NAME> \--image <ACR_NAME>.azurecr.io/frontend:latest \--target-port 80 \--ingress external \--registry-server <ACR_NAME>.azurecr.ioaz containerapp create \--name frontend-app \--resource-group blazor-api-acr-RG \--environment blapi-container-app-env \--image blapiacr.azurecr.io/blazorclient:latest \--target-port 80 \--ingress external \--registry-server blapiacr.azurecr.io
e. Get the frontend app’s public URL:
az containerapp show --name frontend-app --resource-group <RESOURCE_GROUP> --query properties.configuration.ingress.fqdn
az containerapp show \--name frontend-app \--resource-group blazor-api-acr-RG \--query properties.configuration.ingress.fqdn
I got the following response for the Blazor frontend:
This meants that the deployed frontendcan be accessed at:
https://frontend-app.nicebush-967940f0.eastus.azurecontainerapps.io
I pointed by browser to the above URL and experienced this response:
Conclusion
In this article we learned how we can deploy a two project solution compring of a WebAPI backend and a Blazor frontend to Azure Container Apps. You can, similarly, deploy and combination of apps with a variety of technolopgies like Node, React, Java, etc....
No comments:
Post a Comment