Wednesday, 18 September 2019

JsonPatchDocument in ASP.NET Core web API to use PATCH verb

Recently got chance to work with PATCH verb in DotNetCore, which allow us to update model partially. Nice reference could be found on Official Microsoft and Dotnetcoretutorials.

In short, we just need to understand PATCH Operations, make valid JSON patch request and correct implementation of JsonPatchDocument in dotnetcore action.

Operation details could be found here ,


And make call to endpoint with valid request, like in below request I just want to update the userName as Raj.

JSON Request:
[
  {
    "op": "replace",
    "path": "/userName",
    "value": "Raj"
  }
]


JsonPatchDocument Implementation in dotnet core API

/// <summary>
         /// Jsons the state of the patch with model.
         /// </summary>
         /// <param name="userId">The user identifier.</param>
         /// <param name="userPatch">The user patch.</param>
         /// <returns>Updated user</returns>
         [HttpPatch]
         public async Task<IActionResultPatchUserAsync(int userId, [FromBodyJsonPatchDocument<UserDtouserPatch)
         {
             if (userPatch != null)
             {
                 //Get existing user from DB through service or repository.
                 var user = await _userService.GetUserAsync(userId); 
                 //Apply patch to existing user.
                 userPatch.ApplyTo(user);
  
                 //Again validate model
                 if (!ModelState.IsValid)
                 {
                     return BadRequest(ModelState);
                 }
  
                 //Update user through service or repository.
                 await _userService.UpdateUserAsync(UserId, user);
  
                 //Return updated user.
                 return new ObjectResult(user);
             }
             else
             {
                 return BadRequest(ModelState);
             }
         }

Tuesday, 6 August 2019

Execute command and get cmd output in C#

Given below code snippet will help us to execute any command line in background and trace output.
For example, we want to know git status of current working directory.


//Console.WriteLine(CommandOutput("git status"));
 
public static string CommandOutput(string command,
                                   string workingDirectory = null)
{
    try
    {
        ProcessStartInfo procStartInfo = new ProcessStartInfo("cmd""/c " + command);
 
        procStartInfo.RedirectStandardError = procStartInfo.RedirectStandardInput = procStartInfo.RedirectStandardOutput = true;
        procStartInfo.UseShellExecute = false;
        procStartInfo.CreateNoWindow = true;
        if (null != workingDirectory)
        {
            procStartInfo.WorkingDirectory = workingDirectory;
        }
 
        Process proc = new Process();
        proc.StartInfo = procStartInfo;
        proc.Start();
 
        StringBuilder sb = new StringBuilder();
        proc.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e)
        {
            sb.AppendLine(e.Data);
        };
        proc.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e)
        {
            sb.AppendLine(e.Data);
        };
 
        proc.BeginOutputReadLine();
        proc.BeginErrorReadLine();
        proc.WaitForExit();
        return sb.ToString();
    }
    catch (Exception objException)
    {
        return $"Error in command: {command}{objException.Message}";
    }
}

Monday, 15 July 2019

How to monitor DotNetCore App in ElasticSearch using APM?

Steps:
  1. Download & Install ElasticSearch
  2. Download and Install Kibana
  3. Download and install Apm-Server
  4. Add Apm-Agent in to DotnetCore App
  5. Monitor your app in Kibana under Apm Tab.

1. Download & Install ElasticSearch

Download latest ElasticSearch MSI from here
https://www.elastic.co/downloads/elasticsearch
Install it with default settings and verify service up on http://localhost:9200/


2. Download and Install Kibana

Download latest Kibana Zip from https://www.elastic.co/downloads/kibana
Extract Zip by using 7zip if you facing any issue while extracting, in folder C:\Program Files\ElasticSearch\
Run Kibana as service:

Open CMD as administrator
sc create "ElasticSearch Kibana 7.2.0" binPath= "C:\Program Files\ElasticSearch\kibana-7.2.0-windows-x86_64\bin\kibana.bat" depend= "Elasticsearch" start= auto

Verify service up on http://localhost:5601/



Remove Kibana from service():
Open CMD as administrator
sc Delete "ElasticSearch Kibana 7.2.0"


3. Download and install Apm-Server

Now in Kibana go to APM http://localhost:5601/app/apm#/services?_g=()
Follow Windows setup instructions:
Download APM from https://www.elastic.co/downloads/apm

Follow install instruction by using Powershell.

Verify service up on http://localhost:8200/


4. Add Apm-Agent into DotnetCore App

Now follow application setup instruction:
For Dotnet core install following Nuget package with a pre-release check as per your requirement
• Elastic.Apm
• Elastic.Apm.All
• Elastic.Apm.AspNetCore
• Elastic.Apm.EntityFrameworkCore

And Add middleware UseElasticApm in starup.cs

public class Startup
{
  public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  {
    app.UseElasticApm(Configuration);
    //…rest of the method
  }
  //…rest of the class
}


5. Monitor your app in Kibana under Apm Tab.

And verify tracing under APM







Thursday, 4 July 2019

Basic Docker Commands


Docker Image Pull/Run/remove command

Docker pull microsoft/mssql-server-linux:2017-latest

Docker run --name demosqlserver \
-p 1431:1433 \
-e "ACCEPT_EULA=Y" \
-e "SA_PASSWORD=password1234" \
-d microsoft/mssql-server-linux:2017-latest

Docker rm - f demosqlserver

Process a docker-compose.yaml command

Build and Run container
docker-compose up --build

Run container in background
docker-compose up -d

Above command run all services in background that could be verified by
Docker-compose ps


Building block
Docker Pull
Described above

Docker Build
Docker build . -t rajkrs/myappimage:latest

Docker Login
To login into docker hub account

Docker Push
Docker push rajkrs/myappimage:latest


Common commands:

docker container ls
List containers 
docker container rm ""
Remove one or more containers
docker rmi 
Removes one or more images. docker rmi my_image
docker container kill
Kill one or more running containers
docker ps
 Lists running containers
docker ps -a
List of all existing containers.
docker stop <imageid>
Stop specific image
docker kill $(docker ps -q)
kill all running containers
docker rm $(docker ps -a -q)
delete all stopped containers
Docker rm <containerid>
Remove a container from registry
docker rmi $(docker images -q)
delete all images
Docker start/stop <containerid>
Run a container

Sample Compose.yaml for asp.net core app with sql server

version: '3.4'

services:
  webapi:
    image: ${DOCKER_REGISTRY-}webapi
    build:
      context: .
      dockerfile: WebApi/Dockerfile
    depends_on:
      - db
  
  db:
    image: microsoft/mssql-server-linux:2017-latest
    container_name: webapisqldb
    environment:
        ACCEPT_EULA: Y
        SA_PASSWORD: "Bassword@123"
    ports:  
      - '1433:1433'  
    expose:  
      - 1433
    volumes:  
      - sql-data:/var/opt/mssql3  

volumes:
      sql-data:


Get complete example on GitHub

Wednesday, 3 July 2019

Open source Security Scan | Code analysis | Security suggestion | Open Source Security Guard Tools for .net, dotnetcore.


Find OWASP official open source tools list here.


As a dot.net programmer we can have look on following two which include security audits for .net, dotnetcore applications expose and suggest SQL injections, LDAP injections, CSRF/XSS, Cryptography weakness with Visual Studio Extension   :


How to use such tools?
Steps:
  1. Install Extension Puma Scan.
  2. Enable Full solution Analysis in C# text editor option
  3. Build your project and found info.





Thursday, 20 June 2019

VS Code Create Multiple DotnetCore App and Debug

Just mentioning few simple steps and command line by following which we can start working with Visual Studio Code. 

Open terminal for specific created folder.

Create a new solution file, same name as folder
dotnet new sln

Create a new folder for project
mkdir app.webapi

Go to project folder
cd app.webapi

Create a new dot net core web api project:
Dotnet new webapi

Back to root folder:
Cd ..

Create a new folder for 2nd project
Mkdir app.web

Go to folder:
Cd app.web

Create csproj for other dotnet core web project:
Dotnet new web

Back to root folder:
Cd ..

Add webapi project to solution:
dotnet sln VsCodeMultiAppLaunch.sln add .\app.webapi\app.webapi.csproj

Add web project to solution:
dotnet sln VsCodeMultiAppLaunch.sln add .\app.web\app.web.csproj

Back to root folder:
Cd ..

Create new folder for web api data access project:
Mkdir app.webapi.dal

Go to newly created folder:
Cd app.webapi.dal

Create a new class library:
Dotnet new classlib

Back to root folder:
Cd ..

Add app.webapi.dal project to solution:
dotnet sln VsCodeMultiAppLaunch.sln add .\app.web\app.web.csproj

After execution of above command we can verify content under workstation.



Add app.webapi.dal refrence to app.webapi project.
dotnet add .\app.webapi\app.webapi.csproj reference .\app.webapi.dal\app.webapi.dal.csproj

Or else add in app.webapi\app.webapi.csproj manually:
<ItemGroup>
<ProjectReference Include="..\app.webapi.dal\app.webapi.dal.csproj" />
</ItemGroup>

Build all project:
Dotnet build

Debug
Press F5
It will create a new launch.json under .vscode


Now press 5 to Debug app.webapi project.

If you want to debug multiple project together, copy and paste configuration and modify accordingly.

{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web api)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/app.webapi/bin/Debug/netcoreapp3.0/app.webapi.dll",
"args": [],
"cwd": "${workspaceFolder}/app.webapi",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "^\\s*Now listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "https://localhost:5000"
},
},
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/app.web/bin/Debug/netcoreapp3.0/app.web.dll",
"args": [],
"cwd": "${workspaceFolder}/app.web",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "^\\s*Now listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "https://localhost:5001"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}

Now you will be getting

You can click one by one on Launch and will able to debug each project.

Wednesday, 12 June 2019

Create a simple Retry Policy by using Polly in any fault.

Polly a .NET fault-handling wrapper that allows developers to express policies in thread safe manner.
Policies could be made over:

  • Retry
  • Circuit Breaker
  • Timeout
  • Bulkhead Isolation
  • Fallback.

More: <https://github.com/App-vNext/Polly

Simple Demo:

using Polly;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
 
namespace PollyRetry
{
 
    class Program
    {
        static void Main(string[] args)
        {
            HttpRequestDemo();
            Console.WriteLine("Done!");
            Console.ReadLine();
        }
 
        private static void HttpRequestDemo()
        {
 
 
            HttpStatusCode[] httpStatusCodesWorthRetrying = {
                HttpStatusCode.RequestTimeout, // 408
                HttpStatusCode.InternalServerError, // 500
                HttpStatusCode.BadGateway, // 502
                HttpStatusCode.ServiceUnavailable, // 503
                HttpStatusCode.GatewayTimeout, // 504
                HttpStatusCode.NotFound // 404
            };
 
            var httpRetryPolicy = Policy.HandleResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
            .OrResult(r => r.StatusCode == HttpStatusCode.BadGateway)
            .WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (extime) =>
            {
                Console.WriteLine($"Unable to complete operation after {time.TotalSeconds:n1}s ({ex.Result.StatusCode})");
            });
 
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri("http://localhost:51976/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                var response = httpRetryPolicy.ExecuteAndCapture(() =>
                {
                    return client.GetAsync("api/order").Result;
                }).FinalHandledResult;
 
                if (response != null && response.IsSuccessStatusCode)
                {
                    Console.WriteLine("Got valid response from API");
                }
            }
 
 
        }
    }
}


We can also create multiple policy to handle multiple faults, like server is down and we are unable to establish connection with the service. In that case at initial call above code will produce exception. So, I made some changes in code, created two policies and wrapped first one with 2nd once.

using Polly;
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
 
namespace PollyRetry
{
 
    class Program
    {
        static async System.Threading.Tasks.Task Main(string[] args)
        {
            await HttpRequestDemoAsync();
            Console.WriteLine("Done!");
            Console.ReadLine();
        }
 
        private static async System.Threading.Tasks.Task HttpRequestDemoAsync()
        {
 
            int retryCount = 5;
 
            HttpStatusCode[] httpStatusCodesWorthRetrying = {
                HttpStatusCode.RequestTimeout, // 408
                HttpStatusCode.InternalServerError, // 500
                HttpStatusCode.BadGateway, // 502
                HttpStatusCode.ServiceUnavailable, // 503
                HttpStatusCode.GatewayTimeout, // 504
                HttpStatusCode.NotFound // 404
            };
 
 
            var httpRetryPolicy = Policy.HandleResult<HttpResponseMessage>(r => httpStatusCodesWorthRetrying.Contains(r.StatusCode))
            .OrResult(r => r.StatusCode == HttpStatusCode.BadGateway)
            .WaitAndRetryAsync(retryCountretryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exceptioncalculatedWaitDuration) =>
            {
                Console.WriteLine($"Unable to complete operation after {calculatedWaitDuration.TotalSeconds:n1}s ({exception.Result.StatusCode})");
            });
 
            var exceptionPolicy = Policy
            .Handle<Exception>()
            .WaitAndRetryAsync(retryCountretryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (exceptioncalculatedWaitDuration) =>
            {
                Console.WriteLine($"Error while performing operation after {calculatedWaitDuration.TotalSeconds:n1}{exception.Message}");
            })
            .WrapAsync(httpRetryPolicy);
 
 
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri("http://localhost:51976/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                var response = (await exceptionPolicy.ExecuteAndCaptureAsync(() =>
                {
                    return client.GetAsync("api/order");
                })).FinalHandledResult;
 
                if (response != null && response.IsSuccessStatusCode)
                {
                    Console.WriteLine("Got valid response from API");
                }
            }
        }
    }
}

Now you can see variation in fault message

In case of DotNetCore we can use Microsoft.Extensions.Http.Polly.