.NET Core REST API Notes

In the realm of enterprise software, two programming languages have dominated: C# (c sharp) and Java. Java has a runtime environment – which must be installed on computers that want to run Java code – called the JVM, and likewise C# has a runtime environment called .NET. I have a lot of experience with Java/JVM, but not as much with C#/.NET. Of the two languages, Java is probably more popular. This is likely (at least in part) due to the fact that Java and the JVM were open source while C#/.NET’s source code had been Microsoft’s property and closed to the public.

Since June 2016, a version of .NET has been available as open source software. That version is .NET Core (.NET Framework is the closed source relative). After learning about the open source release, I wanted to work with the C#/.NET system. Here, I’m going to document my notes on building a simple REST API connected to an SQL database – in this case, PostgreSQL (which is case sensitive, and that matters).

Installation

Installation of .NET Core is pretty easy. Here’s a link to Microsoft’s website with details for different operation systems:

https://docs.microsoft.com/en-us/dotnet/core/install

Starting the Project

To start a new .NET Core REST API, we can issue a single command. After the command runs, we’ll have a C#/.NET project scaffold in our project directory.

dotnet new webapi -o [project-name]
ls [project-name]
appsettings.Development.json
bin
[project-name].csproj
Program.cs
Startup.cs
appsettings.json
Controllers
obj
Properties
WeatherForecast.cs

To start the application, we can issue this command:

dotnet run
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to bind to https://localhost:5001 on the IPv6 loopback interface: 'Cannot assign requested address'.
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to bind to http://localhost:5000 on the IPv6 loopback interface: 'Cannot assign requested address'.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app

HTTPS/HTTP and CORS

By default the application is configured for HTTPS. I’ll be hosting my project from a container service which will provide the HTTPS. So to make things easier, I’ll turn off HTTPS. To do this first specify the listen addresses (with port number), then tell the program not to redirect to HTTPS in Startup.cs.

Specify Listen Addresses

In Program.cs, call the UseUrls method on the WebBuilder object to specify the server’s listen addresses.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseUrls("http://+:5000");
                webBuilder.UseStartup<Startup>();
            });
}

In Startup.cs, simply comment out or remove the line in the Configure method with the call to app.UseHttpsRedirection.

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "lists_api v1"));
    }

    // app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Add CORS Policy

Add the following policy to the ConfigureService method in the Startup.cs class.

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "VtagApi", Version = "v1" });
            });

            services.AddCors(o => o.AddPolicy("AllowAll", builder =>
            {
                builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader();
            }));
        }

Apply CORS Policy

Call app.UseCors method to use AllowAll policy.

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "VtagApi v1"));
            }
            // app.UseHttpsRedirection();

            app.UseCors("AllowAll");

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

HTTP Requests and Responses

Entity Framework Core

Entity Framework Core is an Object Relational Mapping (ORM) tool that maps database entities to C# objects.

Case Sensitive Table Column Names

Install MongoDB Driver

dotnet add [ProjectName].csproj package MongoDB.Driver

Column/Property Value Converters

Often times, our C# and database data types don’t quite match up. Using a string for a VARCHAR, works “out of the box” with the EF, but matching enumerated types from the database is a bit trickier. We’ll also look at converting a TEXT type from the database into a C# List<string>. With this conversion in place we can store and work with CSV strings.

Namespaces

This section contains information about important Namespaces using in the creation of the REST API application.

Annotations

System.ComponentModel.DataAnnotations.Schema

  • Table – directs a Model class to use specified database table.
namespace MyApi
{
    [Table("entities")]
    public class Entity
    {
        // Model class defined here.
    }
}
  • Column – specify which column the Model property is mapped to.
namespace MyApi
{
    [Table("entities")]
    public class Entity
    {
        [Column("size")]
        public int Size { get; set; }

        // Model class defined here.
    }
}
  • NotMapped – tell Entity Framework not to map a property/column.
namespace MyApi
{
    [Table("entities")]
    public class Entity
    {
        [NotMapped]
        public int Name { get; set; }

        // Model class defined here.
    }
}

Docker

Using Docker with .NET Core apps is pretty simple.

Run Maven Project from CLI

This is just a quick not on how to run a maven project from the CLI – command line interface.

  1. cd into the root directory of the project.
  2. Run mvn compile.
  3. Then, to execute a class with a main method (usually the Application class) execute the following Maven command:
mvn exec:java -Dexec.mainClass=[your class here]

For example, let’s say you have a class with a main method in called Application, and that class is in a package named com.myname.myproject. Then the command to execute the main method would be:

mvn exec:java -Dexec.mainClass=com.myname.myproject.Application
Ubuntu Logo

Ubuntu System Services

This post about systemd has been published from a draft, so it’s not been proofed for publication. I find it useful, so I would like it readily available on-line. If you find anything wrong with it, please

Ubuntu, like many GNU/Linux distributions, uses systemd. systemd is a system used by the OS tor manage process, known as services. We can add our own services to systemd, and I’ll show how to do that in this post.

Services are useful, for example, if you start and run server. If we start that server as a user, the process running the server will be killed when the user logs out. If we want the server to run continuously – which is usually the case with servers – this is a problem.

We can be keep the service running by abandoning the process before logout, but it’s a hassle. In this set up, we would need to login, start the server, then abandoning processes. Instead, we can use systemd services and have the server started automatically (or on demand) and it runs in the background.

Systemd Unit Files

Our unit files go in the Systemd system directory.

/etc/systemd/system

Each service should have a name of the following form:

[name].service

Here’s an example of the format of the file.

[Unit]
Description=My Node.js Service

[Service]
WorkingDirectory=/home/jason/my-app
Restart=on-failure
ExecStart=/home/jason/my-app/dist/index.js

# Note: Debian/Ubuntu uses 'nogroup', RHEL/Fedora uses 'nobody'.
# Note: when 'ExecStart' is used, 'Type' defaults to 'simple'.

Environment=PATH=/usr/bin:/usr/local/bin
Environment=NODE_ENV='production'
Environment=ORACLE_HOME=/usr/lib/oracle/18.3/client64
Environment=LD_LIBRARY_PATH=/usr/lib/oracle/18.3/client64/lib

[Install]
WantedBy=multi-user.target

For more information on Service files, see the man page.

man systemd.service

Update Systemd Services

 sudo systemctl daemon-reload 
 
sudo systemctl enable my-webapp.service

Created symlink /etc/systemd/system/multi-user.target.wants/event-tracker-api.service → /etc/systemd/system/event-tracker-api.service.

 
sudo systemctl start my-webapp
sudo systemctl stop my-app
sudo systemctl status my-webapp

Command:

sudo service [name] {start,stop,restart,status}
Maven Logo

Maven: Setting Compiler Version

I recently started a new JavaFX project. I decided to use Maven because the tool is great for building Java projects in a standard way that conforms with best practices. Unfortunately, I’m not the most well versed in the tool. I’ve used it a few times, and “it just worked”.

This evening I got the following errors when attempting to compile my project:

Source option 5 is no longer supported. Use 7 or later.
Target option 5 is no longer supported. Use 7 or later.

I didn’t know what it meant, so I went to the Web. A search result later, I learned this had to do with the compiler. So I added the following directive to my pom.xml file.

    <properties>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
    </properties>

I’m using openjdk-13, so I told Maven that my source code (compiler.source) would be written in Java 13; and that my output code (compiler.target) should run on the Java 13 JVM.

If we were to compile directly on the command line, we could use -source and -target flags reach the same end, but I believe Maven leverages a javax.tools.JavaCompiler object that enables invocation of the Java compiler from within programs. From what I can tell, it’s the Maven Compiler Plugin which uses such an object – hence, the maven.compiler.* directives.

Mounting Windows File Shares in Docker

A network graph on top of an image of shipping containers

I recently had the need to mount a Windows network file share location within a Docker container. Thus, I tested the mounting of the share on my local machine. It worked, so I figured I’d simply set up the container to mimic the steps I took to mount the share on the host machine. Unfortunately, it wasn’t as straightforward as that. However, I was able to achieve the mount, and this post explains the steps I took to achieve the mount.

Continue reading