Thursday, March 15, 2018

SQL Server database in a Windows docker container with an ASP.NET Core 2.0 web app on host computer

This article shows how to use SQL Server Express in a Windows Docker container communicating with an ASP.NET Core 2.0 app running on the host computer.

The following is assumed:

1) You have .NET Core 2.0 installed on your computer. Go to https://www.microsoft.com/net to download and install .NET Core 2.0.
2) You have Docker for Windows installed on your computer. Refer to https://docs.docker.com/docker-for-windows/install/ for instructions on how to install "Docker for Windows".

Let's get started.

Right-click on the whale docker icon in the tray and make sure it is in Windows Container mode. This is the case if it displays “Switch to Linux containers…” as shown below:


Create a working directory somewhere on your computer's hard drive. I did so by creating a folder named AspCoreMySQL with the following terminal command:

mkdir AspCoreSqlExpress

Thereafter, go into the new folder with:

cd AspCoreSqlExpress

We will create an ASP.NET Core 2.0 application that uses individual authentication with Sql Server Local DB as follows:

dotnet new mvc --auth individual --use-local-db

To run the web application and see what it looks like, enter the following command:

dotnet run

You will see a message in the terminal window that resembles the following:

Hosting environment: Production
Content root path: E:\_aspnet\_docker\AspCoreSqlExpress
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

The above message indicates that the Kestrel web server is running and listening on port 5000. Start your browser and enter URL http://localhost:5000. You should see a page that looks like this:


Close your browser and stop the web server in the terminal window by hitting CTRL + C.

Let's look at the code to determine what database is being used by the application. Suitable IDEs for this tutorial are Visual Studio 2017 or Visual Studio Code. You can use any other editor of your choice. 

Open Startup.cs in an editor and look at the ConfigureServices() method. The following code confirms that SQL Server is being used:

services.AddDbContext<ApplicationDbContext>(options =>
   options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

The connection string is being read from the appsettings.json file, which looks like thois:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-AspCoreSqlExpress-22CD843B-79FF-412C-8F1D-6D6FE7CCC468;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

Add the following code to the Configure() method in the Startup.cs file right before app.UseMvc():

using (var srvc = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
   var context = srvc.ServiceProvider.GetService<ApplicationDbContext>();
   context.Database.Migrate();
}

The above code will process any outstanding Entity Framework migrations.

We shall now download a suitable Docker image for SQLServer Express. Run the following command from a terminal window:

docker pull microsoft/mssql-server-windows-express:2016 

This image is about 5 GB in size, so it will take some time to download. Once fully downloaded, then  create a container from the Docker image with the following command:

docker run -d -p 7000:1433 --env sa_password=Sql!Expre55 --env ACCEPT_EULA=Y microsoft/mssql-server-windows-express:2016

The above Docker command will create an image with the following parameters:

- inside the container, the SQL Server database will listen on its default port number 1433. This port number is mapped to port 7000 on the host computer
- sa password is Sql!Expre55
- environment variable ACCEPT_EULA is set to Y

Check that the SQL Server container is running by typing the following Docker command in a terminal window:

docker ps -a

This should show a message that looks like this:

CONTAINER ID        IMAGE                                         COMMAND                    CREATED             STATUS              PORTS                    NAMES
327d371b6f39        microsoft/mssql-server-windows-express:2016   "cmd /S /C 'powershe…"     6 minutes ago       Up 3 minutes        0.0.0.0:1433->1433/tcp   musing_hopper

According to the above message, the container status is Up 3 minutes, which means that it is indeed running.

Now let's try to connect to it from our ASP.NET Core 2.0 application. To do this, we will need to get the internal IP address of the container. Identify the first three characters of your Container ID. In the above example it is 327. This will be used to obtain the container IP address by executing the following command in a terminal window:

docker inspect 327

        Note: You will need to replace 327 above with the first three characters of your Container ID.

A JSON formatted listing of container parameters will be shown. Identify the value for Networks >> nat >> IPAddress. In my case, the container IP address is 172.18.211.55:



Edit appsettings.json and change the connection string with key DefaultConnection so that it has a value similar to the following:

Data Source=172.18.211.55;User ID=sa;Password=Sql!Expre55;Connect Timeout=30;Database=AspCoreSqlExpress;

        Note: The IP address should match the one you identified for the container earlier on.

The SQL Server database listens on port 7000 on the host machine. This means that we can have an alternative connection string that looks like this:

Data Source=localhost,7000;User ID=sa;Password=Sql!Expre55;Connect Timeout=30;Database=AspCoreSqlExpress;

After saving the appsettings.json file, you can test your web application. Run the following commands from a terminal window:

dotnet restore
dotnet run

If all goes well, you will see a message that indicates that the web server is listening on port 5000. Point your browser to http://localhost:5000. The same web page will appear as before. Click on the Register link on the top right side.

I entered an Email, Password and Confirm password then clicked on the Register button. I was then rewarded with the following confirmation that the credentials were indeed saved in the SQL Server Express database inside the container:



The message on the top right side confirms that a user was saved and that communication between the ASP.NET Core 2.0 application and SQL Server Express inside the container is working properly.

Let us directly access the container and list all the databases that exist. Using the first three characters of the Container ID, run the following Docker command in a separate terminal window:

docker exec -it 327 sqlcmd

        Note: You need to replace 327 above with the first three characters of your Container ID.

The –i switch is for interactive and –t is for pseudo-TTY.

You will see a window that looks like this:


Enter the following pair of commands to display existing databases:

EXEC sp_databases
go

The output will look like this:



Enter these commands to display tables & views in the AspCoreSqlExpress database:

USE AspCoreSqlExpress
EXEC sp_tables
go

You can, alternatively, execute this command to see only base tables:

SELECT  * FROM AspCoreSqlExpress.INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';
go

Enter these commands to display the content of the AspNetUsers table:

SELECT * FROM AspNetUsers 
go

To exit the sqlcmd terminal window type exit and hit ENTER.

In my next post I will demonstrate how we can also containerize our ASP.NET Core 2.0 web application.

Sunday, March 4, 2018

Dockerizing a Node Express API app with MongoDB in a Linux container

Node, Express and MongoDB work very well together. Put Docker into the mix and you have a very interesting cocktail.

In this post we shall develop a Node Express app from scratch and then containerize it using Docker. Before we start, you will need to ready your environment with the following pre-requisites for your specific operating system:

- Install Node.js from https://nodejs.org/en/download/
- Install MongoDB from https://www.mongodb.com/download-center#community
- Install Docker from https://docs.docker.com/install/
- Optionally, you can install Visual Studio Code from https://code.visualstudio.com/Download

In a terminal window, go to a suitable location on your computer's hard drive and create a directory named nodemongo:

mkdir nodemongo

Change into the nodemongo directory:

cd nodemongo

Let us initialize a node application. This is done by running the following command:

npm init

Respond as below:

package name: [Hit ENTER]
version: [Hit Enter]
description: Dockerizing node/express/mongo app
entry point: server.js
test command: [Hit Enter]
git repository: [Hit Enter]
keywords: [Hit Enter]
author: [Enter your name]
license: [Hit Enter]

The package.json file is displayed and you are asked to confirm. Enter y to confirm.

This is what my project.json file looks like:

{
  "name": "nodemongo",
  "version": "1.0.0",
  "description": "Dockerizing node/express/mongo app.",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Medhat Elmasry",
  "license": "ISC"
}

We will need two node packages: express and mongojs. Install these packages with the following command:

npm install express mongojs

I am using Visual Studio Code to edit my application. Use whatever editor you are familiar with like sublime, atom, brackets, etc....

Create a file named server.js in the nodemongo folder and add to it the following code:

var express = require("express");
var users = require("./routes/");
var config = require('./config');

var app = express();

app.use("/", users);

app.listen(config.port, function() {
    console.log("Server started on port " + config.port)
});

Lines 2 and 3 above suggest that we are missing folders /routes and /config. Therefore, create folders routes and config inside the nodemongo directory.

Inside the /config directory, create a JavaScript file named index.js and add to it the following code:

module.exports = {  
    // Setting port for server
    'port': process.env.PORT || 3000,
    
    // Setting mongo host name
    'mongo_host': process.env.MONGO_HOST || 'localhost',

    // Setting mongo port number
    'mongo_port': process.env.MONGO_PORT || 27017,

    // Setting mongo database name
    'mongo_database': process.env.MONGO_DATABASE_NAME || 'demodb',
};

The above code configures environment variables for: (1) the server listening port number, (2) the MongoDB host name, (3) the MongoDB listening port number and (4) the MongoDB database name. 

Inside the /routes directory, create another JavaScript  file named index.js and add to it the following code:

var express = require("express");
var mongojs = require('mongojs');
var config = require('../config');

var connStr = 'mongodb://' + config.mongo_host;
connStr += ':' + config.mongo_port;
connStr += '/' + config.mongo_database;

var db = mongojs(connStr, ['students']);
var usersCollection = db.collection('users');

var router = express.Router();

router.get('/', (req, res, next) => {
    usersCollection.count({},(err, docCount) => {
        if (docCount == 0) {
            const users = [
                {"name":"user_1","email":"user_1@bogus.com"},
                {"name":"user_2","email":"user_2@bogus.com"},
                {"name":"user_3","email":"user_3@bogus.com"}            
            ];
        
            // use the Event model to insert/save
            usersCollection.save(users, (err, data) => {
                if (err) {
                    res.send(err);
                }
            })
    
        }
    })

    usersCollection.find( (err, data) => {
        if (err)
            res.send(err);
        
        res.json(data);
    })

});

router.get('/add', (req, res, next) => {
    var name = 'user_'+ Math.floor(Math.random() * 1000);
    var email = name + '@bogus.com';

    var doc = {'name': name, 'email': email};
    usersCollection.save(doc, (err, data) => {
        if (err) {
            res.send(err);
        }
        res.json(data);
    })
});

// get single user
router.get("/users/:id", (req, res, next) => {
    usersCollection.findOne({_id: mongojs.ObjectId(req.params.id)},function(err, data){
        if (err) {
            res.send(err);
        }
        res.json(data);
    });
});

module.exports = router;

The above code sets up the following API endpoints:

http://localhost:3000/ Displays all documents in the users collection. If the users collection is empty then it will be seeded with three documents.
http://localhost:3000/add Adds a user document with a randomly generated name and email
http://localhost:3000/users/:id Displays a singe document by MongoDB object id

If the MongoDB database server has not started already, start it. At this point, we are able to test our application and make sure it is working as expected.

After starting the database, launch the application by executing the following terminal command:

npm start

You should see the following message in the terminal window:

> nodemongo@1.0.0 start D:\demo\_docker\nodemongo
> node server.js

Server started on port 3000

This indicates that our node web server  is running and listening on port number 3000. Point your browser to http://localhost:3000/. You should see the following seeded data comprising of three JSON objects in your browser:

[{"_id":"5a9b8f225e78082ddccba361","name":"user_1","email":"user_1@bogus.com"},{"_id":"5a9b8f225e78082ddccba362","name":"user_2","email":"user_2@bogus.com"},{"_id":"5a9b8f225e78082ddccba363","name":"user_3","email":"user_3@bogus.com"}]

Try adding a user by pointing to http://localhost:3000/add. It should randomly generate a user document similar to the following:

{"name":"user_20","email":"user_20@bogus.com","_id":"5a9b8fdd5e78082ddccba364"}

If you try http://localhost:3000/ again, you should see four JSON objects similar to the following:

[{"_id":"5a9b8f225e78082ddccba361","name":"user_1","email":"user_1@bogus.com"},{"_id":"5a9b8f225e78082ddccba362","name":"user_2","email":"user_2@bogus.com"},{"_id":"5a9b8f225e78082ddccba363","name":"user_3","email":"user_3@bogus.com"},{"_id":"5a9b8fdd5e78082ddccba364","name":"user_20","email":"user_20@bogus.com"}]

Lastly, you can get a document by MongoDB object ID with endpoint http://localhost:3000/users/:id. Copy one of the IDs and and it to the address line. I used one of my IDs and pointed my browser to the following endpoint: http://localhost:3000/users/5a9b8f225e78082ddccba361 and experienced the following response:

{"_id":"5a9b8f225e78082ddccba361","name":"user_1","email":"user_1@bogus.com"}

Of course, you will not have the same ID as I do and should use one of the IDs in your users documents.

At this stage, we have determined that our application works as expected. The next and final step is to containerize both MongoDB and our Node/Express API application.

Stop both the Node web server and the MongoDB database server.

Create a text file named .dockerignore in the root of your node project and add to it the following content. This instructs Docker not to copy the contents of node_modules into the container:

node_modules

Create a text file named DockerFile in the root folder of your application and add to it the following:

FROM node:9.7.1
WORKDIR /app
COPY package.json /app/package.json
RUN npm install
COPY . /app
EXPOSE 3000

Above are instructions to create a Docker image that will contain our Node/Express API web application. I describe each line below:

FROM node:9.7.1 Base image node:9.7.1 will be used
WORKDIR /app The working directory in the container is /app
COPY package.json /app/package.json package.json is copied to /app in the container
RUN npm install  This is run inside the containers /app directory
EXPOSE 3000 Port 3000 will be exposed in the container

Create another text file named docker-compose.yml in the root of your node project, and add to it the following content:

version: "3"

services:

  db:
    image: mongo:3.6.3
    ports:
      - "27017:27017"
    restart: always

  web:
    build: 
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    links:
      - db
    environment:
      - MONGO_HOST=db
      - MONGO_PORT=27017
      - MONGO_DATABASE_NAME=demodb
    command: node /app/server.js

Below is an explanation of what this file does.

We will be having two containers. Each container is considered to be a service. The first service is named db and will host MongoDB. The second service is named web and will host our Node/Express API app.

The most current version of docker-compose is version 3. This is the first line in our docker-compose.yml file.

The MongoDB Container

Image mongo version 3.6.3 will be used for the MongoDB container.

restart: always is so that if the container stops, it will be automatically restarted.

Port 27017 is the default MongoDB listening port. This port number, inside the container, is being mapped to the same port number outside the container.

Node/Express API app Container

The container will be built using the instructions in the Dockerfile file and the context used is the current directory.

Port 3000 in the web container is mapped to port 3000 on the host computer.

The environment variables needed by the web app are:

- MONGO_HOST pointing to the MongoDB service name
- MONGO_PORT sets the MongoDB listening port number
- MONGO_DATABASE_NAME is the name which we want to use for the database

Running the yml file

To find out if this all works, go to a terminal window and run the following command:

docker-compose -f docker-compose.yml up

Point your browser to http://localhost:3000/ and you should see three seeded users JSON objects as below:


You can view the two running containers by executing the following Docker command in a terminal window:

docker ps -a

You will see something similar to this:

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                      NAMES
979d458c9ba3        nodemongo_web       "node /app/server.js"    21 minutes ago      Up 20 minutes       0.0.0.0:3000->3000/tcp     nodemongo_web_1
ae80a1b9283c        mongo               "docker-entrypoint.s…"   21 minutes ago      Up 21 minutes       0.0.0.0:27017->27017/tcp   nodemongo_db_1

To shutdown and remove thes two containers, run the following Docker command:

docker-compose -f docker-compose.yml down

Thanks for coming this far in my tutorial and I hope you found it useful.


Wednesday, February 28, 2018

Dockerising an ASP.NET Core 2.0 and MySQL App

In a previous post I showed how you can have an ASP.NET Core 2.0 application work with a containerized MySQL database server. In this post I will take this one step further by also containerizing the ASP.NET Core 2.0 application.

Please go through my previous post before continuing with this tutorial.

We will generate the release version of the application by executing the following command from a terminal window in the root directory of your ASP.NET 2.0 project:

dotnet publish -o dist

The above command instructs the dotnet utility to produce the release version of the application in the dist directory. This results in output similar to the following:

Microsoft (R) Build Engine version 15.5.180.51428 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 315.96 ms for D:\demo\_aspnet\AspCoreMySQL\AspCoreMySQL.csproj.
  Restore completed in 409.11 ms for D:\demo\_aspnet\AspCoreMySQL\AspCoreMySQL.csproj.
  Restore completed in 448.93 ms for D:\demo\_aspnet\AspCoreMySQL\AspCoreMySQL.csproj.
  Restore completed in 476.7 ms for D:\demo\_aspnet\AspCoreMySQL\AspCoreMySQL.csproj.
  AspCoreMySQL -> D:\demo\_aspnet\AspCoreMySQL\bin\Debug\netcoreapp2.0\AspCoreMySQL.dll
  AspCoreMySQL -> D:\demo\_aspnet\AspCoreMySQL\dist\

If you inspect the dist directory, you will see content similar to the following:


The highlighted file in the above screen capture is my main DLL file that is the entry point into the web application.

Let us run the release version of your application. To do this, change directory to the dist directory with the following terminal instruction:

cd dist

You can then run your main DLL file. In my case, this file is AspCoreMySQL.dll. I executed the following command:

dotnet AspCoreMySQL.dll

This generates an understandable error because the application is unable to connect to a database:

Application startup exception: MySql.Data.MySqlClient.MySqlException (0x80004005): Unable to connect to any of the specified MySQL hosts.

This gives us a good idea about what ASP.NET Core 2.0 artifacts need to be copied into a container. We shall simply copy contents of the dist directory into a Docker container that has the dotnet core 2.0 runtime.

return to the root directory of your project by typing the following in a terminal window:

cd ..

We need to create a docker image that will contain the dotnet core 2.0 runtime. A suitable image for this purpose is: microsoft/aspnetcore:2-jessie

Create a text file named Dockerfile.mvc and add to it the following content:

FROM microsoft/aspnetcore:2-jessie
COPY dist /app
WORKDIR /app
EXPOSE 80/tcp
ENTRYPOINT ["dotnet", "AspCoreMySQL.dll"]

Above are instructions to create a Docker image that will contain our ASP.NET Core 2.0 application. I describe each line below:

FROM microsoft/aspnetcore:2-jessie Base image microsoft/aspnetcore:2-jessie will be used
COPY dist /app Contents of the dist directory on the host computer will be copied to directory /app in the container
WORKDIR /app The working directory in the container is /app
EXPOSE 80/tcp Port 80 will be exposed in the container
ENTRYPOINT ["dotnet", "AspCore4Docker.dll"] The main ASP.NET Core 2.0 web application will be launched by executing "dotnet AspCore4Docker.dll"

We will next compose a docker yml file that orchestrates the entire system which involves two containers: a MySQL database server container and a container that holds our application. In the root folder of your application, create a text file named docker-compose-aspnetcore-mysql.yml and add to it the following content:

version: '3'

volumes:
  datafiles:

services:
  db:
    image: mysql:8.0.0
    volumes:
      - datafiles:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_TCP_PORT: 3306

  mvc:
    build:
      context: .
      dockerfile: Dockerfile.mvc
    depends_on:
      - db
    ports:
      - "8888:80"
    restart: always
    environment:
      - DBHOST=db
      - DBPORT=3306
      - DBPASSWORD=secret
      - ASPNETCORE_ENVIRONMENT=Development


Below is an explanation of what this file does.

We will be having two containers. Each container is considered to be a service. The first service is named db and will host MySQL. The second service is named mvc and will host our ASP.NET Core 2.0 web app.

The most current version of docker-compose is version 3. This is the first line in our docker-compose file.

The MySQL Container

Image mysql:8.0.0 will be used for the MySQL container.

A volume named datafiles is declared that will host MySQL data outside of the container. This ensures that even if the MySQL container is decommissioned, data will not be lost.

restart: always is so that if the container stops, it will be automatically restarted.

The root password will be secret when MySQL is configured. This is set by the MYSQL_ROOT_PASSWORD environment variable.

The ASP.NET Core 2.0 Web Application Container

The container will be built using the instructions in the Dockerfile.aspnetcore2 file and the context used is the current directory.

depends_on indicated that the web app relies on the MySQL container (db) to properly function.

Port 80 in the mvc container is mapped to port 8888 on the host computer.

Just like in the db container, restart: always is so that if the container stops, it will be automatically restarted.

The environment variables needed by the web app are:

DBHOST pointing to the MySQL service and  
- ASPNETCORE_ENVIRONMENT set to Development more. In reality, you should change this to Production one you determine that your web app container works as expected.

Running the yml file

To find out if this all works, go to a terminal window and run the following command:

docker-compose -f  docker-compose-aspnetcore-mysql.yml up

Point your browser to http://localhost:8888/ and you should see the main web page. To ensure that the database works properly, register a user by clicking on the Register link in the top right corner.

In my case, I received confirmation that a user was indeed registered:

As you can see in the top-right corner, the user with email a@a.a has been successfully registered.

This opens up a whole new world for containerizing your ASP.NET Core 2.0 web applications.




Monday, February 26, 2018

Using MySQL database in a docker container with an ASP.NET Core 2.0 web app on your host computer

It is customary to develop ASP.NET Core 2.0 applications with either SQL Server or SQLite databases. How about if you want to use the popular MySQL database server? This article discussed one approach to making this possible by having your ASP.NET Core 2 development environment connect with MySQL running in a docker container.

This article assumes the following:

1) You have .NET Core 2.0 installed on your computer. Go to https://www.microsoft.com/net to download and install .NET Core 2.0.
2) You have Docker installed on your computer. Refer to https://docs.docker.com/docker-for-windows/install/ for instructions on how to install "Docker for Windows".

Let's get started.

The first step is to create a working directory somewhere on your computer's hard drive. I did so by creating a folder named AspCoreMySQL with the following terminal command:

mkdir AspCoreMySQL

Thereafter, go into the new folder with:

cd AspCoreMySQL 

We will create an ASP.NET Core 2.0 application that uses individual authentication with the following command:

dotnet new mvc --auth individual

To run the web application and see what it looks like, enter the following command:

dotnet run

You will see a message in the terminal window that resembles the following:

Hosting environment: Production
Content root path: D:\demo\_aspnet\AspCoreMySQL
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

The above message indicates that the Kestrel web server is running and listening on port 5000. Start your browser and enter URL http://localhost:5000. You should see a page that looks like this:


If you look into the root folder of the project, you will find a file named app.db. This is a SQLite database file. The web application that we scaffolded is configured to work with SQLite. We will change it so that it works with the popular MySQL database instead.

Close your browser and stop the web server in the terminal window by hitting CTRL + C.

We will be using an Entity Framework Core driver for MySQL named Pomelo. Add the Pomelo driver package with the following terminal command:

dotnet add package Pomelo.EntityFrameworkCore.MySql

Let us configure our web application to use MySQL instead of SQLite. Open the Startup.cs file in your favorite editor and comment out (or delete) the following statement found in the ConfigureServices() method:

// services.AddDbContext<ApplicationDbContext>(options =>
//    options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));

Replace the above code with the following:

var host = Configuration["DBHOST"] ?? "localhost";
var port = Configuration["DBPORT"] ?? "3306";
var password = Configuration["DBPASSWORD"] ?? "secret";

services.AddDbContext<ApplicationDbContext>(options =>
{
    options.UseMySql($"server={host}; userid=root; pwd={password};"
        + $"port={port}; database=products");
});

Three environment variables are used in the database connection string. These are: DBHOSTDBPORT and DBPASSWORD. If these environment variables are not found then they will take up default values: localhost, 3306 and secret respectively.

We can instruct our application to automatically process any outstanding Entity Framework migrations. This is done by adding the following argument to the Configure() method in Startup.cs:

ApplicationDbContext context

This means that our Configure() method will have the following signature:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ApplicationDbContext context)

Next, add the following code inside the Configure() method in Startup.cs right before 
app.UseMvc:

context.Database.Migrate();

This is all you need to do in order to change the database configuration of our application so that it uses MySQL instead of SQLite. There is, of course, something major that is missing I.E. we do not have a MySQL database server yet. Let us have a Docker container for our MySQL database server. Run the following command from a terminal window:

docker run -p 3306:3306 --name mysqldb -e MYSQL_ROOT_PASSWORD=secret -d mysql:8.0.0

Here's what this command does:

-p 3306:3306 maps port 3306 in the container to port 3306 on the host machine
--name mysqldb names the container
-e MYSQL_ROOT_PASSWORD=secret sets the root password for the MySQL database server
-d runs container in background and prints container ID
mysql:8.0.0 Image MySQL version 8.0.0 from hub.docker.com will be used

If you do not already have the docker image for MySQL version 8.0.0, it will be downloaded. Thereafter, a container will be made from that image and will run in background mode. To prove that the container was indeed created from the image and is active, run this command:

docker ps -a

You will see output that looks like this:

CONTAINER ID  IMAGE        COMMAND                  CREATED        STATUS         PORTS                    NAMES
b271f5bc4615  mysql:8.0.0  "docker-entrypoint.s…"   21 seconds ago Up 19 seconds  0.0.0.0:3306->3306/tcp   mysqldb

Now, let us test our application and see whether or not is is able to talk to the containerized MySQL database server. 

Run the web application with the following terminal command:

dotnet run

If all goes well, you will see a message that indicates that the web server is listening on port 5000. Point your browser to http://localhost:5000. The same web page will appear as before. Click on the Register link on the top right side.

I entered an Email, Password and Confirm password then clicked on the Register button. I was then rewarded with the following confirmation that the credentials were saved in the MySQL database server:

The message on the top right side confirmed that the user was saved and that communication between my ASP.NET Core 2.0 application and MySQL is working as expected.

In my next post I will demonstrate how we can also containerize our ASP.NET Core 2.0 web application.

Adding Swagger to an ASP.NET Core 2.0 app

Swagger is an API specification framework. It reminds me of WSDL in the SOAP days. In this article I will guide you in add Swagger documentation to an ASP.NET Core Web API app.

There are three components that we need to buckle-up in our Web API application. These are, not surprisingly, called Swashbuckle:
  1. Swashbuckle.AspNetCore.SwaggerGen : builds SwaggerDocument objects from your router, controllers and models.
  2. Swashbuckle.SwaggerUI : embedded version of Swagger UI tool which uses the above documents for a rich customizable experience for describing the Web API functionality and includes built in test harness capabilities for the public methods.
  3. Swashbuckle.AspNetCore.Swagger: Swagger object model and middleware to expose SwaggerDocument objects as JSON endpoints.
For starters, add package Swashbuckle to your project. This can be done in two ways:

1) If you are using Visual Studio 2017, execute the following command from the Package Manager Console:

install-package --version 2.1.0 Swashbuckle.AspNetCore

You can find the Package Manager Console in Visual Studio 2017 at Tools >>  NuGet Package Manager >> Package Manager Console.

2) From a terminal window in the main project folder, enter the following command:

dotnet add package Swashbuckle.AspNetCore -Version 2.1.0

Add the following using statement at the top of your Startup.cs file:

using Swashbuckle.AspNetCore.Swagger;

Next, add the Swagger generator to bottom of the ConfigureServices() method in Startup.cs:

// Register the Swagger generator, defining one or more Swagger 
// documents
services.AddSwaggerGen(c => {
  c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});

Add the following two lines of code to the Cofigure() method in Startup.cs just before app.UseMvc():

// Enable middleware to serve generated Swagger as JSON endpoint
app.UseSwagger();

// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.)
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
  c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});

NOTE: You can exclude the service generated by an API controller by annotating the controller class with: [ApiExplorerSettings(IgnoreApi = true)]

Run your application with CTRL + F5.

Point your browser to http://localhost:{port number}/swagger/v1/swagger.json and you will see a JSON document that describes the endpoints of your service.

image

You can also see the Swagger UI at URL http://localhost{port number}/swagger.

image

In the above example the API service is Studentapi. Your service will be under API V1. Click on it and you will see something similar to the following:

image

To test out GET, click on the first blue GET button and the section will expand describing to you the structure of your object:

image

Click on the “Try it out!” button to view the data coming back from the service for all items in the collection:

image

Click on the first blue GET button again to collapse the section. Now, click on the second blue GET button to test retrieval of an item by id.

image

Enter a value for ‘id’ then click the “Try it out!” button. It should get for you the item for that id.

image

In a similar manner, go ahead and test out all the other POSTDELETE, and PUT buttons.

The official Swagger website at http://swagger.io/. You can generate a Swagger client for most programming languages at http://editor.swagger.io/.