Category: NetCoreUsefullEndpoints

NetCoreUsefullEndpoints-part 13–adding runtime information

In the Nuget NetCoreUsefullEndpoints I have added information about the runtime information :

You can access by going to localhost:5027/api/usefull/runtimeinformationAll and the end result is

{
“frameworkDescription”: “.NET 8.0.8”,
“osDescription”: “Microsoft Windows 10.0.22631”,
“processArchitecture”: “X64”,
“osArchitecture”: “X64”
}

The code that returns this is

route.MapGet("api/usefull/runtimeinformation/", (HttpContext httpContext) =>
        {
            return TypedResults.Ok(new Helper().FromStaticRuntimeInformation());
        })

( I have used a Roslyn Code Generator , https://ignatandrei.github.io/RSCG_Examples/v2/docs/RSCG_Static#example–source-csproj-source-files- , that generates a class from a static class )

NetCoreUsefullEndpoints-part 12–adding url adresses

In the Nuget NetCoreUsefullEndpoints I have added information about the current process :

You can access by going to localhost:5027/api/usefull/adresses and the end result is

[
     “http://localhost:5027″
]

The code that returns this is

route.MapGet("api/usefull/adresses", (HttpContext httpContext, [FromServices] IServer server) =>
        {
            var adresses = server.Features.Get<IServerAddressesFeature>();
            var ret= adresses?.Addresses?.ToArray()??[] ;
            return TypedResults.Ok(ret);
        });

NetCoreUsefullEndpoints-part 11–adding process information

In the Nuget NetCoreUsefullEndpoints I have added information about the current process :

You can access by going to

http://localhost:5027/api/usefull/process

and this is the result

{
     “id”: 24064,
     “processName”: “TestUsefullEndpoints”,
     “startTime”: “2024-06-27T23:24:36.4003351+03:00”,
     “totalProcessorTime”: “00:00:01.0312500”,
     “threadsCount”: 39,
     “workingSet64”: 84385792,
     “privateMemorySize64”: 65458176,
     “pagedMemorySize64”: 65458176,
     “pagedSystemMemorySize64”: 384840,
     “peakPagedMemorySize64”: 67108864,
     “peakVirtualMemorySize64”: 2481013948416,
     “peakWorkingSet64”: 84733952,
     “virtualMemorySize64”: 2481005563904,
     “basePriority”: 8,
     “handleCount”: 592,
     “machineName”: “.”,
     “priorityClassName”: “Normal”,
     “priorityClass”: 32,
     “nonpagedSystemMemorySize64”: 91992,
     “fileName”: “D:\\gth\\NetCoreUsefullEndpoints\\src\\UsefullEndpoints\\TestUsefullEndpoints\\bin\\Debug\\net8.0\\TestUsefullEndpoints.exe”,
     “minWorkingSet”: 204800,
     “maxWorkingSet”: 1413120,
     “totalProcessorTimeSeconds”: 1.03125,
     “totalUserProcessorTimeSeconds”: 0.921875,
     “totalPrivilegedProcessorTimeSeconds”: 0.109375,
     “fileVersionInfoShort”: {
         “fileVersion”: “1.0.0.0”,
         “fileName”: “D:\\gth\\NetCoreUsefullEndpoints\\src\\UsefullEndpoints\\TestUsefullEndpoints\\bin\\Debug\\net8.0\\TestUsefullEndpoints.exe”,
         “fileDescription”: “TestUsefullEndpoints”,
         “originalFilename”: “TestUsefullEndpoints.dll”,
         “productVersion”: “1.0.0+7f426dfd54f515a95654044b725010b159c89b2f”
     },
     “fileVersionInfo”: {
         “comments”: “”,
         “companyName”: “TestUsefullEndpoints”,
         “fileBuildPart”: 0,
         “fileDescription”: “TestUsefullEndpoints”,
         “fileMajorPart”: 1,
         “fileMinorPart”: 0,
         “fileName”: “D:\\gth\\NetCoreUsefullEndpoints\\src\\UsefullEndpoints\\TestUsefullEndpoints\\bin\\Debug\\net8.0\\TestUsefullEndpoints.exe”,
         “filePrivatePart”: 0,
         “fileVersion”: “1.0.0.0”,
         “internalName”: “TestUsefullEndpoints.dll”,
         “isDebug”: false,
         “isPatched”: false,
         “isPrivateBuild”: false,
         “isPreRelease”: false,
         “isSpecialBuild”: false,
         “language”: “Language Neutral”,
         “legalCopyright”: ” “,
         “legalTrademarks”: “”,
         “originalFilename”: “TestUsefullEndpoints.dll”,
         “privateBuild”: “”,
         “productBuildPart”: 0,
         “productMajorPart”: 1,
         “productMinorPart”: 0,
         “productName”: “TestUsefullEndpoints”,
         “productPrivatePart”: 0,
         “productVersion”: “1.0.0+7f426dfd54f515a95654044b725010b159c89b2f”,
         “specialBuild”: “”
     }
}

And now , because it is version and calendar dependent it is now 8.2024.627.800 ( 8 means .net 8 , then year.monthday.hourminutes) . It is convenient, because you know what version to use ;

NetCoreUsefullEndpoints-part 10- adding LongRunningTasks

In the Nuget NetCoreUsefullEndpoints I have already an  endpoint to shutdown  –  see http://msprogrammer.serviciipeweb.ro/2023/02/20/netcoreusefullendpoints-part-9-adding-endpoints-for-shutdown/ .

However, this means that there are no ( or some … )  long running operations that are running … How to identify those ?

For the starting point , I was thinking at something simple, like an IDisposable that knows when the Long Running operation is finished

[HttpGet(Name = “GetWeatherForecast”)]
         public async Task<WeatherForecast[]> Get()
         {
             using var lr = UsefullExtensions.UsefullExtensions.AddLRTS(“weather”+DateTime.UtcNow.Ticks);
             await Task.Delay(5000);
             return Enumerable.Range(1, 5).Select(index => new WeatherForecast
             {
                 Date = DateTime.Now.AddDays(index),
                 TemperatureC = Random.Shared.Next(-20, 55),
                 Summary = Summaries[Random.Shared.Next(Summaries.Length)]
             })
             .ToArray();
         }

The implementation is very simple for adding:

internal static Dictionary<string, LongRunningTask> lrts = new();
public static LongRunningTask AddLRTS(string id, string? name = null)
{
     if(lrts.ContainsKey(id))
     {
         lrts[id].Dispose();
     }
     lrts.Add(id,new LongRunningTask(id, name ?? id));
     return lrts[id];
}

And for removing itself:

public record LongRunningTask(string id, string? name = “”) : IDisposable
{
     public void Dispose()
     {
         UsefullExtensions.lrts.Remove(id);
         GC.SuppressFinalize(this);
     }

}

Also, an endpoint will be registered for the user know what are the operations

var rh = route.MapGet(“api/usefull/LongRunningTasks/”,
             (HttpContext httpContext) =>
             {
                 var data=lrts.Select(it=>new { it.Key, it.Value.name }).ToArray();
                 return data;
             });

        var rhCount = route.MapGet(“api/usefull/LongRunningTasks/Count”,
             (HttpContext httpContext) =>
             {
                 return lrts.LongCount();
             });

And that will be to register and found what are the long running tasks in ASP.NET Core

NetCoreUsefullEndpoints-part 9- adding endpoints for shutdown

In my NuGet NetCoreUsefullEndpoints package  I want to can shutdown the application . Could be hard way, like calling Environment.Exit or the easy way , like calling a cancellation token. Also, what should happen with all the requests that are coming ? ( And I do not want to mention long running tasks – I do not found a useful registration / un-registration for those)

So I come with the following implementation for forced exit

//IEndpointRouteBuilder

var rhForced = route.MapPost(“api/usefull/shutdownForced/{id:int}”,
     (int id) =>
     {
         Environment.Exit(id);
     });

For the shutdown that is requested , I come with 3 modifications:

1.In Program.cs , I put a Middleware for not serving the HTTP

builder.Services.AddSingleton<MiddlewareShutdown>();

app.UseMiddleware<MiddlewareShutdown>();

await app.RunAsync(UsefullExtensions.UsefullExtensions.cts.Token);

2.  Register the routes in order to use the cancellation token

//IEndpointRouteBuilder

var rhSec = route.MapPost(“api/usefull/shutdownAfter/{seconds}”,
             (HttpContext httpContext, int seconds) =>
             {
                 RequestedShutdownAt = DateTime.UtcNow;
                 var h = cts.Token.GetHashCode();
                 cts?.CancelAfter(Math.Abs(seconds)*1000);
                 return h;

            });

3. Use a middleware to return 418 Status Code if the application is shutting down

public class MiddlewareShutdown : IMiddleware
{
     public async Task InvokeAsync(HttpContext context, RequestDelegate next)
     {
         if (UsefullExtensions.RequestedShutdownAt != null)
         {
             context.Response.StatusCode = 418;
             await context.Response.WriteAsync(“Service is stopping at ” + UsefullExtensions.RequestedShutdownAt!.Value.ToString(“s”));
             return;
         }
         await next(context);
         return;
     }
}

And with those you can shutdown gracefully any ASP.NET Core application ( without long running tasks!)

NetCoreUsefullEndpoints-part 8- adding start date

In my NuGet NetCoreUsefullEndpoints package  I have had already registered the actual date as

var rh = route.MapGet(“api/usefull/dateUTC”, (HttpContext httpContext) =>
            {
                return Results.Ok(DateTime.UtcNow);
            });

Now I want to register also the start date – the date where the application has been started.

1. How to do this  ?

2. What will be the route ?

For 1 can be a static constructor or, better , the singleton in C# :

private static DateTime startDateUTC = DateTime.UtcNow;

For 2 it is more complicated

My solution ( just break compatibility, since I have not a v1 and v2) was the following

var rh = route.MapGet(“api/usefull/date/startUTC”, (HttpContext httpContext) =>
{
     return Results.Ok(startDateUTC);
});

var rhUTC = route.MapGet(“api/usefull/date/nowUTC/”,
     (HttpContext httpContext) =>
     {
         return TypedResults.Ok(DateTime.UtcNow);
     });

rhUTC.AddDefault(corsPolicy, authorization);

So now I have same routing api/usefull/date

NetCoreUsefullEndpoints–part 7–restart application

In order to do the shutdown, I have added the following extension

public static CancellationTokenSource cts=new ();

public static void MapShutdown(this IEndpointRouteBuilder route, string? corsPolicy = null, string[]? authorization = null)
        {
            ArgumentNullException.ThrowIfNull(route);
            var rh = route.MapPost(“api/usefull/shutdown/”,
                (HttpContext httpContext) =>
                {
                    var h= cts.Token.GetHashCode();
                    cts?.Cancel();                   
                    return h;
                   
                });

           rh.AddDefault(corsPolicy, authorization);

        }

This code defines a static field called “cts” in the “UsefullExtensions” class. “cts” is an instance of the “CancellationTokenSource” class, which is used to create a CancellationToken that can be used to stop the application gracefully.

The “MapShutdown” method is an extension method for IEndpointRouteBuilder that creates a new endpoint for a POST request to the “api/usefull/shutdown/” URL. When the request is received, the method cancels the “cts” CancellationTokenSource if it is not null, and returns the hash code of the CancellationToken. The method also sets the “corsPolicy” and “authorization” parameters for the endpoint.

You can call with

app.MapUsefullAll();

or with

app.MapShutdown

Anyway , the runAsync should be modified with

await app.RunAsync(UsefullExtensions.UsefullExtensions.cts.Token);

The “RunAsync” method of the “app” object is used to start the application and listen for incoming requests. The method takes a CancellationToken as an argument, which allows the application to be stopped gracefully by cancelling the token. In this case, the token is provided by the “cts” field of the “UsefullExtensions” class. The code uses the “await” keyword to wait for the task returned by “RunAsync” to complete before continuing.

NetCoreUsefullEndpoints–part 6–passing to .NET 7

So  .NET 7 has appeared and I decided to pass NetCoreUsefullEndpoints to .NET 7 .

Also, for RateLimiter , I have considered that is good to know if the request is local or remote … so I decided to add connection ( remote IP, local IP, and more details) to the nuget package.

So I have created for .NET 6 this :

So , first things first : modify the version from  6.2022.1203.1551 to  7.2022.1203.1551 ( my versioning scheme is .NETCore version compliant of the package.year.MMdd.HHmm  – pretty stable and easy to decide what package you should add)

Then I want to profit to show in swagger the return type with TypedResults – so , as an example , I have modified from

route.MapGet(“api/usefull/httpContext/Connection”, (HttpContext httpContext) =>
{
var con = httpContext.Connection;
if (con == null)
{
     return Results.NoContent();
}
var conSerialize = new
{
     LocalIpAddress = con.LocalIpAddress?.ToString(),
     RemoteIpAddress = con.RemoteIpAddress?.ToString(),
     con.RemotePort,
     con.LocalPort,
     con.ClientCertificate,
     con.Id
};
return Results.Ok(conSerialize);

})

to

route.MapGet(“api/usefull/httpContext/Connection”,

Results<NoContent, Ok<object>>
(HttpContext httpContext) =>
{
var con = httpContext.Connection;
if (con == null)
{
     return TypedResults.NoContent();
}
var conSerialize = new
{
     LocalIpAddress = con.LocalIpAddress?.ToString(),
     RemoteIpAddress = con.RemoteIpAddress?.ToString(),
     con.RemotePort,
     con.LocalPort,
     con.ClientCertificate,
     con.Id
};
return TypedResults.Ok((object)conSerialize);
})

As you see , a pretty easy modification – indicating the INesteHttpResult Results<NoContent, Ok<object>>  ( so the swagger understand the 2 different return types )  and returning TypedResults instead of Results

Also the github ci must add the .NET Core and Azure App Service should be going to .NET 7 STS

[Nuget] dotnet-run-script

I found this awesome package – https://github.com/xt0rted/dotnet-run-script . It is good to make macros in global.json, then execute in a CICD scenario.

For example, NetCoreUsefullEndpoints used this in yaml ( pretty standard )

# – name: Restore dependencies

#   run: |

#     cd src

#     cd UsefullEndpoints

#     dotnet tool restore

#     dotnet pwsh readme.ps1

#     dotnet restore

# – name: Build

#   run: |

#     cd src

#     cd UsefullEndpoints

#     dotnet build –no-restore

# – name: Pack

#   run: |

#     cd src

#     cd UsefullEndpoints

#     cd UsefullExtensions

#     dotnet pack -o ../nugetPackages  –include-symbols –include-source

Now I have a global.json

{

“scripts”: {

“make_readme”:”dotnet pwsh readme.ps1″,

“prebuild”:”dotnet restore”,

“build”: “dotnet build –no-restore”,

“test”: “dotnet test –configuration Release”,

“prepack”:”dotnet r build”,

“pack”: “cd UsefullExtensions &&  dotnet pack -o ../nugetPackages  –include-symbols –include-source”

}

}

and the yaml is

– name: Restore dependencies

run: |

cd src

cd UsefullEndpoints

dotnet tool restore

dotnet r make_readme

dotnet r pack

Pretty easy  -and can be reproduced on local if you want, not just in CICD actions on source control…

NetCoreUsefullEndpoints-5 Endpoints summary

So for now the package https://www.nuget.org/packages/NetCoreUsefullEndpoints/ has the following endpoints:

GET=>/api/usefull/user/authorization

GET=>/api/usefull/user/noAuthorization

GET=>/api/usefull/environment

GET=>/api/usefull/errorWithILogger

GET=>/api/usefull/errorPure

GET=>/api/usefull/date

GET=>/api/usefull/endpoints/graph ( this calls https://github.com/dotnet/aspnetcore/blob/8bf447aa3f9719f6f162598708020dd4b420b49d/src/Http/Routing/src/Internal/DfaGraphWriter.cs#L22 )

GET=>/api/usefull/endpoints/text

GET=>/api/usefull/configuration ( this calls https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.configurationrootextensions.getdebugview?view=dotnet-plat-ext-6.0 )

You can see in practice at https://netcoreusefullendpoints.azurewebsites.net/swagger/

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.