Wednesday, December 16, 2020

Containerizing ASP.NET WebAPI & Client-side Blazor apps with nginx

This tutorial is about containerizing both an ASP.NET WebAPI and a client-side (Web Assembly) Blazor application. We will be using Visual Studio Code in this walkthrough. At the time of writing this post, the current .NET version is 5.0. Also, the client-side Blazor application will be hosted by the nginx web-server running inside a docker container.

Source code: https://github.com/medhatelmasry/BlazorWithAPI.git

Creating and running the applications

Start with creating a working directory named BlazorWithAPI and a solution file:

mkdir BlazorWithAPI
cd BlazorWithAPI
dotnet new sln

 Next, create a WebAPI project without HTTPS and it to the solution file.

dotnet new webapi --no-https -o WeatherAPI
dotnet sln add WeatherAPI/WeatherAPI.csproj

 Finally, create a client-side Blazor project without HTTPS and it to the same solution file.

dotnet new blazorwasm --no-https -o BlazorClient
dotnet sln add BlazorClient/BlazorClient.csproj

Let us run the WebAPI application.

cd WeatherAPI
dotnet run

If you point your browser to http://localhost:5000/Weatherforecast, you will see the default WeatherForecast service:

WeatherForecast service

Before we start working on our client-side Blazor application, that will consume the API service, we need to make two changes to our WeatherAPI application:

1) Let us change the default port number for the API from 5000 to 3000 so that it does not conflict with the Blazor app. 

2) Enable CORS in the WeatherAPI application

Go through these steps to accomplish the above two required changes:

- Stop the WeatherAPI application by hitting CTRL C in the terminal window

- Open the WeatherAPI project in VS Code.

- Edit Properties/launchSettings.json. Around line 25, change 5000 to 3000.

- Open Startup.cs in the editor.

- Add the following code to the ConfigureServices() method

// Add Cors
services.AddCors(o => o.AddPolicy("CorsPolicy", builder => {
  builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
}));

- Add the following code to the Configure() method in Startup.cs just before app.UseAuthorization():

app.UseCors(); 

Open Controllers/WeatherForecastController.cs in the editor and add the following annotation to the class declaration just under [ApiController]:

[EnableCors("CorsPolicy")]

- Restart the WeatherAPI application with 'dotnet run'.


You will see that the port number is now 3000.

We will next focus on consuming our WeatherAPI service from a client-side Blazor application. Open the BlazorClient project in VS Code. Edit Pages/FetchData.razor. Around line 42 . . .

CHANGE: 

forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");

TO: 

forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("http://localhost:3000/Weatherforecast");

We can start testing our client-side Blazor app to make sure that it is reading weather data from our service. Start the BlazorClient app by typing the following in a terminal window at the root of the project:

dotnet run

Note: The Blazor app listens on port 5000 and the API app listens on port 3000. 

Point your browser to http://localhost:5000. When the Blazor app loads in the browser, click on 'Fetch data" on the left-side navigation. You should see data being read from the API service:

fetch data

Docker-izing both applications

To docker-ize our applications, we will need to add a Dockerfile to each project. Let's start with the WeatherAPI project. 

Dockerizing WeatherAPI application

Create a Dockerfile at the root of the WeatherAPI project and add to it the following code:

FROM mcr.microsoft.com/dotnet/core/aspnet:5.0-bionic
COPY WeatherAPI/dist /app
WORKDIR /app
EXPOSE 80/tcp
EXPOSE 443/tcp
ENTRYPOINT ["dotnet", "WeatherAPI.dll"]

NOTE: Adjust the name WeatherAPI based on whatever you named your project.

 Dockerizing BlazorClient application

We will be using the nginx web server to host the static client-side Blazor artifacts inside the container. Therefore, we will be using an nginx based docker container.

At the root of the BlazorClient project, create two files: one named nginx.conf and the other named Dockerfile. 

This configuration file is used to set the nginx document root and also to enable the delivery of .wasm files. Add the following code to nginx.conf:

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;
        }
     }
}

Add the following code to Dockerfile:

FROM nginx:alpine 
WORKDIR /var/www/web
COPY BlazorClient/dist/wwwroot .
COPY BlazorClient/nginx.conf /etc/nginx/nginx.conf
EXPOSE 80

Make sure you stop all running applications.

Before we can containerize our applications, we must first publish the release version of each app separately. To facilitate this process, I created a batch file in the solution folder that publishes each application to a folder named dist. Therefore, inside the folder that contains the .sln file, create a file named pblsh.cmd in Windows. You can use the same ideas for other operating systems like macOS or Linux.

Content of pblsh.cmd on Windows

cd WeatherAPI
rmdir /Q /S dist
dotnet publish -o dist
cd ..
cd BlazorClient
rmdir /Q /S dist
dotnet publish -o dist
cd ..

Content of pblsh.sh on mac

cd WeatherAPI 

rm -rf dist

dotnet publish -o dist

cd ..

cd BlazorClient

rm -rf dist

dotnet publish -o dist

cd ..

NOTE: Adjust the project names WeatherAPI and BlazorClient to suit your own project names.

docker-compose.yml file

We can now orchestrate multi-container Docker applications with docker-compose.yml. In the same solution director, create a text file named docker-compose.yml and add to it the following content:

version: "3.4"
services:
  webapi:
    build:
      context: .
      dockerfile: WeatherAPI/Dockerfile
    ports:
      - "3000:80"
    restart: always
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
  wasm:
    build:
      context: .
      dockerfile: BlazorClient/Dockerfile
    depends_on:
      - webapi
    ports:
      - 8888:80

Running pblsh.cmd & docker-compose-yml

At a terminal window inside the solution folder, run the following command to publish each application separately:

'pblsh.cmd' on Windows or 'bash ./pblst.sh' on mac

Next, run docker-compose by simply entering the following instruction in the same terminal window:

docker-compose up

Wait until the terminal window settles down. To view the containerized client-side Blazor application, point your browser to https://localhost:8888 then click on "Fetch data". You should see your container hosted app being displayed in your browser:

Containerized client-side blazor app

Cleanup

To cleanup your environment by stopping and removing the running containers, hit CTRL C in the terminal window. Thereafter, enter the following command:

docker-compose down

Conclusion

I hope you found what you are looking for in this short article about how to docker-ize a client-side Blazor application under the nginx web server.


No comments:

Post a Comment