Generate SSH Key Pair in Linux

Following command creates SSH key pair:

ssh-keygen

It will then prompt you for the location (the default ~/.ssh/id_rsa) and for a passphrase. The result will be two files, id_rsa (the private key) and id_rsa.pub (the public key). The public key will be store on this server while the private key will be copied into the client.

More details can be found here

Following command needs to be run on the server to push generated public key to SSH key repo:

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

This will append newly generated public key to existing authorized keys.

Finally appropriate permissions need to be set on the server:

chmod 600 ~/.ssh/authorized_keys

More details on SSH configuration can be found here

Adventurous: Angular & .NET Core on Linux

Note: This article is not aiming on digging into details of setting up CI pipeline. It’s sole purpose is to explain my experience with deploying and running .NET Core app in Linux environment.

Another note: For the purpose of this article I created simple HelloWorld Web API (https://github.com/mattuu/HelloWorld.NetCoreApi) and simple HelloWorld Angular app (https://github.com/mattuu/HelloWorld.Angular) that calls this API.

Setup

This article will use following file system paths:

/usr/helloworld
|--> api (directory for .NET Core Web API)
|--> www (directory for Angular app)
|--> nginx.conf (config file for Nginx)

Get you code over to Linux server

The CI pipeline should push your code to file system on Linux machine.

Deploying .NET Core 2.1 Web API to CentOS7

Install .NET Core runtime on CentOS server

This guide describes steps to install .NET Core: https://www.microsoft.com/net/download/linux-package-manager/centos/runtime-current

To check if .NET Core installed successfully, simply run:

dotnet --version

Now you should be able to start API by typing:

dotnet /usr/helloworld/api/HelloWorld.dll
Create dotnet daemon

Every process in Linux needs to run it’s own daemon. In order to accomplish this, we will use SystemD utility.

First we create service file helloworld.service with following content:

[Unit]
Description=Hello World .NET Core App Service
After=network.target

[Service]
ExecStart=/usr/bin/dotnet /usr/helloworld/helloworld.dll 5000
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

Next we copy this file to SystemD location:

sudo cp helloworldapi.service /lib/systemd/system

Now we need to reload SystemD and enable the service:

sudo systemctl daemon-reload 
sudo systemctl enable helloworldapi

Finally we start the service and check the status:

sudo systemctl start helloworldapi 
systemctl status helloworldapi

More details of how to configure SystemD can be found here: https://www.freedesktop.org/wiki/Software/systemd/ or here: https://pmcgrath.net/running-a-simple-dotnet-core-linux-daemon

Install Proxy Server

Since .NET Core runs on Kestrel, we need to setup a web server that will act as reverse proxy for the API. Most commonly used are Nginx or Apache. I will use Nginx for this article.

Full instructions on how to install Nginx can be found here: http://nginx.org/en/docs/install.html

Configure Nginx to act as reverse proxy

Now we need to tell Nginx to forward requests to .NET Core application. By default .NET Core runs on port 5000 so below configuration should work fine (path: /usr/helloworld/nginx.conf)

location /api/ {
proxy_pass http://localhost:5000; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
}

This ensures forwarding all requests to http://localhost/api are forwarded to http://localhost:5000 which is where .NET Core Web API is hosted.

Note: Nginx needs to be configured to include our custom configuration file so the following change needs to be done to Nginx default configuration (most likely to be in /etc/nginx/nginx.conf):

include /usr/helloworld/nginx.conf;

More on Nginx configuration options can be found here: https://nginx.org/en/docs/

Deploying Angular app to Centos7 OS

Our Angular app will be located in following directory: /usr/helloworld/www

Configure Nginx to serve static pages

Since Angular app is simple index.html, we need to tell Nginx to serve it as static content. Following article describes how to achieve this: https://medium.com/@jgefroh/a-guide-to-using-nginx-for-static-websites-d96a9d034940

Sample configuration may look like this (path: /usr/helloworld/nginx.conf)

location / {
root /usr/helloworld/www;
try_files /index.html =404;
}

location ~* .(js|jpg|png|css|woff|eot|svg|ttf|ico)$ {
root /usr/helloworld/www;
expires 30d;
}

First block ensures index.html is default document served by Nginx. This is needed to load Angular app properly.

Second line tells Nginx what to do with assets like JavaScript, CSS, images, fonts etc.

Once that’s done we should be able to navigate to http://localhost and see Angular app successfully loading:

If we now change browser url to http://localhost/?name=Matt, our application should display personalized greeting:

Our Angular app successfully passed name parameter to API and displayed greeting message generated by .NET Core.

Wrap-up

I really think .NET Core on Linux has great potential and will be exploring it further in future. I am planning on creating some bash scripts for dealing with this setup.

Useful links

PowerShell Cheatsheet

Operation cmd PowerShell
Get a simple directory listing
dir
get-childitem
alias: dir
Get a recursive directory listing
dir /s
get-childitem -recurse
alias: dir -r
Get a wide directory list
dir /w
dir | format-wide 
alias: dir | fw
List built-in commands
help
get-command
alias: help
Copy a file
copy foo.txt bar.txt
copy-item foo.txt bar.txt
alias: copy foo.txt bar.txt
Move a file
move foo.txt c:\
move-item foo.txt d:\
alias: move foo.txt d:\
Rename a file
ren foo.txt bar.txt
rename-item foo.txt bar.txt
alias: ren foo.txt bar.txt
Batch rename
ren *.one *.two
dir *.pdf | rename
  -newname {$_.name -rep ".one",".two"}
Set the current directory to d:\
d:
cd \
set-location d:\
alias: cd d:\
Clear the screen
cls
clear-host
alias: cls
List only directories
dir /ad
dir | where { $_.MshIsContainer }
Directory list, sorted by date
dir /od
dir | sort-object LastWriteTime
Directory list, sorted by date, descending order
dir /o-d
dir | sort-object LastWriteTime -desc
Show the current directory
cd
get-location
alias: pwd
See a command’s help
dir /?
get-help get-command 
or: get-help get-command -detailed
or: get-help get-command -full
or: dir -?
List environment variables
set
dir env:
Delete a file
del foo.txt
remove-item foo.txt
alias: del foo.txt
Find all *.txt files
dir /s *.txt
get-childitem -recurse -include *.txt
alias: dir -r -i *.txt
Find all *.txt files containing a particular string
findstr "foo" *.txt
dir *.txt | select-string "foo"
Show a list of services
net start
get-service
Start a service
net start MyService
start-service MyService
Stop a service
net stop MyService
stop-service MyService
Show network shares
net share
gwmi Win32_Share
Show a list of running processes
tasklist
get-process alias: ps
Kill all notepad.exe processes
taskkill /im notepad.exe /f
ps notepad | kill

 

Useful patterns for DateTime.ToString()

Custom DateTime.ToString() formats

d day of the month as a number from 1 through 31 – a single-digit day is formatted without a leading zero
dd day of the month as a number from 01 through 31 – a single-digit day is formatted with a leading zero
ddd abbreviated name of the day of the week (Mon, Tues, Wed etc)
dddd full name of the day of the week (Monday, Tuesday etc)
h 12-hour clock hour (e.g. 7)
hh 12-hour clock, with a leading 0 (e.g. 07)
H 24-hour clock hour (e.g. 19)
HH 24-hour clock hour, with a leading 0 (e.g. 19)
m minutes
mm minutes with a leading zero
M month number
MM month number with leading zero
MMM abbreviated Month Name (e.g. Dec)
MMMM full month name (e.g. December)
s seconds
ss seconds with leading zero
t abbreviated AM / PM (e.g. A or P)
tt AM / PM (e.g. AM or PM
y year, no leading zero (e.g. 2001 would be 1)
yy year, leadin zero (e.g. 2001 would be 01)
yyy year, (e.g. 2001 would be 2001)
yyyy year, (e.g. 2001 would be 2001)
K time zone information of a date and time value (e.g. +05:00)
z with DateTime values, represents the signed offset of the local operating system’s time zone from Coordinated Universal Time (UTC), measured in hours. (e.g. +6)
zz as z but with leadin zero (e.g. +06)
zzz with DateTime values, represents the signed offset of the local operating system’s time zone from UTC, measured in hours and minutes. (e.g. +06:00)
f most significant digit of the seconds fraction; that is, it represents the tenths of a second in a date and time value.
ff two most significant digits of the seconds fraction; that is, it represents the hundredths of a second in a date and time value
fff three most significant digits of the seconds fraction; that is, it represents the milliseconds in a date and time value
ffff four most significant digits of the seconds fraction; that is, it represents the ten thousandths of a second in a date and time value
fffff five most significant digits of the seconds fraction; that is, it represents the hundred thousandths of a second in a date and time value
ffffff six most significant digits of the seconds fraction; that is, it represents the millionths of a second in a date and time value
the seven most significant digits of the seconds fraction; that is, it represents the ten millionths of a second in a date and time value
F most significant digit of the seconds fraction; that is, it represents the tenths of a second in a date and time value. Nothing is displayed if the digit is zero.
: the time separator defined in the current DateTimeFormatInfo..::.TimeSeparator property. This separator is used to differentiate hours, minutes, and seconds.
/ date separator defined in the current DateTimeFormatInfo..::.DateSeparator property. This separator is used to differentiate years, months, and days.
quoted string (quotation mark). Displays the literal value of any string between two quotation marks (“). Your application should precede each quotation mark with an escape character (\).
quoted string (apostrophe). Displays the literal value of any string between two apostrophe (‘) characters.
%c the result associated with a c custom format specifier, when the custom date and time format string consists solely of that custom format specifier. That is, to use the d, f, F, h, m, s, t, y, z, H, or M custom format specifier by itself, the application should specify %d, %f, %F, %h, %m, %s, %t, %y, %z, %H, or %M. For more information about using a single format specifier, see Using Single Custom Format Specifiers.

The patterns for DateTime.ToString ( ‘d’ ) :

0 MM/dd/yyyy 08/22/2006

The patterns for DateTime.ToString ( ‘D’ ) :

0 dddd, dd MMMM yyyy Tuesday, 22 August 2006

The patterns for DateTime.ToString ( ‘f’ ) :

0 dddd, dd MMMM yyyy HH:mm Tuesday, 22 August 2006 06:30
1 dddd, dd MMMM yyyy hh:mm tt Tuesday, 22 August 2006 06:30 AM
2 dddd, dd MMMM yyyy H:mm Tuesday, 22 August 2006 6:30
3 dddd, dd MMMM yyyy h:mm tt Tuesday, 22 August 2006 6:30 AM

The patterns for DateTime.ToString ( ‘F’ ) :

0 dddd, dd MMMM yyyy HH:mm:ss Tuesday, 22 August 2006 06:30:07

The patterns for DateTime.ToString ( ‘g’ ) :

0 MM/dd/yyyy HH:mm 08/22/2006 06:30
1 MM/dd/yyyy hh:mm tt 08/22/2006 06:30 AM
2 MM/dd/yyyy H:mm 08/22/2006 6:30
3 MM/dd/yyyy h:mm tt 08/22/2006 6:30 AM

The patterns for DateTime.ToString ( ‘G’ ) :

0 MM/dd/yyyy HH:mm:ss 08/22/2006 06:30:07

The patterns for DateTime.ToString ( ‘m’ ) :

0 MMMM dd August 22

The patterns for DateTime.ToString ( ‘r’ ) :

0 ddd, dd MMM yyyy HH’:’mm’:’ss ‘GMT’ Tue, 22 Aug 2006 06:30:07 GMT

The patterns for DateTime.ToString ( ‘s’ ) :

0 yyyy’-‘MM’-‘dd’T’HH’:’mm’:’ss 2006-08-22T06:30:07

The patterns for DateTime.ToString ( ‘u’ ) :

0 yyyy’-‘MM’-‘dd HH’:’mm’:’ss’Z’ 2006-08-22 06:30:07Z

The patterns for DateTime.ToString ( ‘U’ ) :

0 dddd, dd MMMM yyyy HH:mm:ss Tuesday, 22 August 2006 06:30:07

The patterns for DateTime.ToString ( ‘y’ ) :

0 yyyy MMMM 2006 August

AutoFixture customization for ASP.Net Core Web API controller

Lets start with simple controller:

[Route("api/[controller]")
public class TestController : Controller
{
	[HttpGet("")]
	public IActionResult Get()
	{
		return Ok();
	}
}

Now we want to create unit test for this method using AutoFixture/Moq/Shouldly/XUnit stack:

public class TestControllerTests 
{
	[Theory, AutoMoqData]
	public void Get_ShouldReturn_HttpStatusOk(TestController sut)
	{
		// act..
		var actual = sut.Get();

		// assert..
		actual.ShouldNotBeNull();
		actual.ShouldBeOfType()
	}
}

Test runner will crash saying:

AutoFixture.ObjectCreationException : AutoFixture was unable to create an instance from Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary because creation unexpectedly failed with exception. Please refer to the inner exception to investigate the root cause of the failure.

Request path:
	  J2BI.Holidays.TravelDocs.Api.Controllers.AccommodationController sut --> 
	   J2BI.Holidays.TravelDocs.Api.Controllers.AccommodationController --> 
	    Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary ViewData --> 
	     Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary


---- System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
-------- Castle.DynamicProxy.InvalidProxyConstructorArgumentsException : Can not instantiate proxy of class: Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.
Could not find a parameterless constructor.

The problem lies in lack of parameterless constructor for ModelMetadata class.
This can easily be fixed with AutoFixture customization:

public class ApiControllerCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Register(() => new CustomCompositeMetadataDetailsProvider());
        fixture.Inject(new ViewDataDictionary(fixture.Create(), fixture.Create()));
    }

    private class CustomCompositeMetadataDetailsProvider : ICompositeMetadataDetailsProvider
    {
        public void CreateBindingMetadata(BindingMetadataProviderContext context)
        {
            throw new System.NotImplementedException();
        }

        public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
        {
            throw new System.NotImplementedException();
        }

        public void CreateValidationMetadata(ValidationMetadataProviderContext context)
        {
            throw new System.NotImplementedException();
        }
    }
}

This now has to be wrapped in data attribute:

public class AutoApiMoqDataAttribute : AutoDataAttribute
{
    public AutoApiMoqDataAttribute()
        : base(() =>
        {
            var fixture = new Fixture();

            fixture.Customize(new ApiControllerCustomization())
                   .Customize(new AutoMoqCustomization())
                   .Behaviors.Add(new OmitOnRecursionBehavior());

            return fixture;
        })
    {
    }
}

Voila! Now the test will pass.

Check out this gist

Best Practices for RESTful API

1. Use nouns but no verbs

For an easy understanding use this structure for every resource:

Resource GET
read
POST
create
PUT
update
DELETE
/cars Returns a list of cars Create a new car Bulk update of cars Delete all cars
/cars/711 Returns a specific car Method not allowed (405) Updates a specific car Deletes a specific car

2. GET method and query parameters should not alter the state

Use PUT, POST and DELETE methods  instead of the GET method to alter the state.

3. Use plural nouns

Keep it simple and use only plural nouns for all resources.

/users instead of /user

4. Use sub-resources for relations

If a resource is related to another resource use subresources.

GET /cars/711/drivers/ Returns a list of drivers for car 711

5. Use HTTP headers for serialization formats

Both, client and server, need to know which format is used for the communication. The format has to be specified in the HTTP-Header.

Content-Type defines the request format.
Accept defines a list of acceptable response formats.

6. Handle Errors with HTTP status codes

It is hard to work with an API that ignores error handling. Pure returning of a HTTP 500 with a stacktrace is not very helpful.

Use HTTP status codes

The HTTP standard provides over 70 status codes to describe the return values. We don’t need them all, but  there should be used at least a mount of 10.

200 – OK – Eyerything is working
201 – OK – New resource has been created
204 – OK – The resource was successfully deleted

304 – Not Modified – The client can use cached data

400 – Bad Request – The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. „The JSON is not valid“
401 – Unauthorized – The request requires an user authentication
403 – Forbidden – The server understood the request, but is refusing it or the access is not allowed.
404 – Not found – There is no resource behind the URI.
422 – Unprocessable Entity – Should be used if the server cannot process the enitity, e.g. if an image cannot be formatted or mandatory fields are missing in the payload.

500 – Internal Server Error – API developers should avoid this error. If an error occurs in the global catch blog, the stracktrace should be logged and not returned as response.

7. Provide filtering, sorting, field selection and paging for collections

Filtering:

Use a unique query parameter for all fields or a query language for filtering.

GET /cars?color=red Returns a list of red cars
GET /cars?seats<=2 Returns a list of cars with a maximum of 2 seats

Sorting:

Allow ascending and descending sorting over multiple fields.

GET /cars?sort=-manufactorer,+model

This returns a list of cars sorted by descending manufacturers and ascending models.

Field selection

Mobile clients display just a few attributes in a list. They don’t need all attributes of a resource. Give the API consumer the ability to choose returned fields. This will also reduce the network traffic and speed up the usage of the API.

GET /cars?fields=manufacturer,model,id,color

Paging

Use limit and offset. It is flexible for the user and common in leading databases. The default should be limit=20 and offset=0

GET /cars?offset=10&limit=5

To send the total entries back to the user use the custom HTTP header: X-Total-Count.

Three simple steps to enable Swagger on ASP.NET Core WebAPI

Step 1. Install NuGet package:

Install-Package Swashbuckle.AspNetCore

Step 2. Navigate to Startup.cs and add following code inside ConfigureServices() method


services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});

Step 3. Add following code inside Configure() method


app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
);

Voila!

Based on https://docs.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio

MS SQL Server object types

List of MS SQL Server object types:

  • C – CHECK_CONSTRAINT
  • D – DEFAULT_CONSTRAINT
  • F – FOREIGN_KEY_CONSTRAINT
  • FN- SQL_SCALAR_FUNCTION
  • IF – SQL_INLINE_TABLE_VALUED_FUNCTION
  • IT – INTERNAL_TABLE
  • P – SQL_STORED_PROCEDURE
  • PK – PRIMARY_KEY_CONSTRAINT
  • S – SYSTEM_TABLE
  • SQ – SERVICE_QUEUE
  • TR – SQL_TRIGGER
  • U – USER_TABLE
  • UQ – UNIQUE_CONSTRAINT
  • V – VIEW

This can be obtained with simple query:

select distinct type, type_desc from sys.objects order by 1