Plinth.AspNetCore 1.7.0

Prefix Reserved
dotnet add package Plinth.AspNetCore --version 1.7.0                
NuGet\Install-Package Plinth.AspNetCore -Version 1.7.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Plinth.AspNetCore" Version="1.7.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Plinth.AspNetCore --version 1.7.0                
#r "nuget: Plinth.AspNetCore, 1.7.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Plinth.AspNetCore as a Cake Addin
#addin nuget:?package=Plinth.AspNetCore&version=1.7.0

// Install Plinth.AspNetCore as a Cake Tool
#tool nuget:?package=Plinth.AspNetCore&version=1.7.0                

README

Plinth.AspNetCore

Utility framework and enhancements for ASP.Net Core Projects

Adds many enhancements and facilities for ASP.Net Core API projects including logging, security, diagnostics, front-end logging, background tasks, swagger/openapi, and more.

ConfigureServices()

1. Add Plinth Controller Defaults

  • Start with adding Plinth controller defaults
	services.AddControllers()
	        .AddPlinthControllerDefaults();

This will enable

  • Newtonsoft.Json as the JSON engine along with reasonable JSON serialization defaults such as ignoring null values
  • Adds a background task service IBackgroundTaskQueue for running fire and forget tasks
  • Adds a filter which will call an Upgrade() method automatically if an incoming model object is a Plinth.HttpApiClient.Models.BaseApiModel

👉 It is recommended that APIs be authorized by default. Add the middle line to treat every API by default as if [Authorize] is present. To then have an open API, add [AllowAnonymous].

        services.AddControllers()
                .AddGlobalAuthorizedFilter()
                .AddPlinthControllerDefaults();

2. Add Plinth Services

	services.AddPlinthServices(e =>
	{
		// use 'e' to enable services
	});

Available services:

  • e.EnableServiceLogging();
    • Adds incoming request and outgoing response logging. This is core to Plinth's logging and tracing framework.
    • Various overloads allow configuration of how much body content to log, which path prefixes should log, which HTTP methods should log, and what log level to use.
    • The defaults are:
      • Max 25,000 bytes of the request and response bodies will be logged
      • Only paths prefixed by /api will log
      • All HTTP methods (GET, POST, HEAD, etc) will log
      • Will use LogLevel.Information
  • e.EnableServiceExceptionHandling();
    • Adds middleware that will catch unhandled exceptions thrown from controller methods and turn them into appropriate API errors with codes and an error response body. By default, it will convert all Plinth.Common.Exceptions.Logical**Exception to their corresponding HTTP error codes, as well as a few other standard exception types.
    • Also supported is supplying a function to add custom exception to response mapping
    • Additional versions are best for certain types of projects
      • For MVC, use e.EnableLogOnlyExceptionHandling() which will only log and not affect the response
      • For Public APIs, use e.EnableWebExceptionHandling() which wraps errors in an easy to use error format for front end frameworks.
  • Auto Endpoints
    • Enables what Plinth calls "Auto Endpoints", essentially automated controller methods for specific purposes.
    • e.AutoEndpoints.SetComponentName("MyComponent");
      • Sets the product name that is used in various auto endpoints
    • e.AutoEndpoints.EnableVersion();
      • Adds an API at /version which returns information about the build, process, and if configured, the database
      • You can disable specific fields if desired by using .SetDisabledFields()
    • e.AutoEndpoints.EnableClient();
      • Adds a logging api at /client/webapp which the front end can use to log information and exceptions
    • e.AutoEndpoints.EnableDiagnostics()
      • Adds /diagnostics/ping which will simply return an HTTP 200 OK, for liveness checks
      • Adds /diagnostics/connectivity which will run through configured dependency checks and return a result containing which are accessible and which are not. Example below.
                .EnableDiagnostics(c =>
                {
                    c.SetConnectivityTesters(
                        new ConnectivityTesters.HttpHead("google", "https://www.google.com")
                        new ConnectivityTesters.SqlDatabase("db", "connectionString")
                    );
                });

3. Use Plinth Services

At the top of Configure(), add this method to verify that all controllers can be instantiated by DI. This allows a missing DI registration to be caught at startup rather than when calling the API.

	app.VerifyControllers();

👉 after app.UseRouting and before app.UseEndpoints, add this to activate the Plinth services added in the prior section.

	app.UsePlinthServices();

4. Security - Plinth Tokens

Plinth has its own secure token mechanism which can be used as a bearer token to authorize endpoints. These tokens contain user identity information as well as custom data attributes. They can also be renewed. The tokens are encrypted using AES and authenticated using HMAC.

To enable Plinth secure tokens, import the Plinth.AspNetCore.SecureTokens namespace and add when configuring services:

	var secureData = new SecureData("{64 char hex string}")
	services.AddSecureTokenAuth(c =>
	{
		c.SecureData = secureData;
	});
	services.AddSingleton(new SecureTokenGenerator(secureData, TimeSpan.FromMinutes(15)));

👉 be sure to add these to Configure()

	app.UseAuthentication();
	app.UseAuthorization();

To generate tokens, inject SecureTokenGenerator into your controller.

  • Generate a token via .Generate(Guid userGuid, string userName, IEnumerable<string>? roles = null)
    • More options are available using .GetBuilder()
  • Renew a token via .Refresh(AuthenticationToken origToken)
    • To retrieve the original token, use this.GetAuthenticationToken(), an extension available in the Plinth.AspNetCore.SecureToken namespace

Tokens can contain:

  • User GUID
  • User Name
  • Issue Timestamp
  • Expiration
  • Original Issue Timestamp
  • Session Identifier
  • List of Roles
  • Arbitrary data fields

Extensions

These extensions are provided on ControllerBase to retrieve token information:

  • this.GetAuthenticationToken() returns AuthenticationToken
  • this.GetAuthenticatedUserName() returns user name field
  • this.GetAuthenticatedUserGuid() returns user guid
  • this.GetAuthenticatedUserSessionGuid() returns session guid
  • this.GetAuthenticatedUserRoles() returns a set of user roles
  • this.GetSecureTokenInfo() returns an object containing all of the above

👉 All of the above will throw if the user is not authenticated. There are also versions prefixed with Try which will return null instead if the user is not authenticated. For example:

this.TryGetAuthenticatedUserName();

5. Security - JWT

Plinth also supports standard JWT based authentication using both Signed and Encrypted tokens (JWS and JWE). It supports signing via HMAC and RSA, and encrypting via AES and RSA-OAEP.

To enable Plinth JWT Authentication, import the Plinth.AspNetCore.Jwt namespace and add when configuring services:

	services.AddPlinthJwtAuth(c =>
	{
	    c.Audience = "PlinthTest";
	    c.Issuer = "Plinth.AspNetCore";
	    c.TokenLifetime = TimeSpan.FromMinutes(10);
	    c.MaxTokenLifetime = TimeSpan.FromHours(2);
	    c.SecurityMode = new JwtSecurityModeHmacSignature(
	        HexUtil.ToBytes("{64-char-hex-string}"));
	});

Audience and Issuer are optional, but recommended to ensure that the token is used for the purpose that is intended.

👉 be sure to add these to Configure()

	app.UseAuthentication();
	app.UseAuthorization();

To generate tokens, inject JwtManager into your controller.

  • Generate a token via .GetBuilder()
    • This returns a builder that can be used to add fields and custom claims to the token
  • Renew a token via .Refresh(string origToken)
    • To retrieve the original token, use this.GetJwt(), an extension available in the Plinth.AspNetCore.Jwt namespace

Extensions

These extensions are provided on ControllerBase to retrieve token information:

  • this.GetJwt() returns the JWT as a string
  • this.GetAuthenticatedUserName() returns user name field
  • this.GetAuthenticatedUserId() returns user id
  • this.GetAuthenticatedUserSessionGuid() returns session guid
  • this.GetAuthenticatedUserRoles() returns a set of user roles
  • this.GetAuthTokenInfo() returns an object containing all of the above

👉 All of the above will throw if the user is not authenticated. There are also versions prefixed with Try which will return null instead if the user is not authenticated. For example:

this.TryGetAuthenticatedUserName();

6. Authorization

If using .AddGlobalAuthorizedFilter(), every controller method by default will behave as if it has [Authorize] on it. If not, you can add it yourself. This will require that the caller provide an authenticated user.

	[Authorize]
	public async Task<ActionResult> MyApi()
	{
	}

To restrict by role (if you are supplying roles in your tokens)

	[Authorize(Roles = "Admin")]
	public async Task<ActionResult> MyApi()
	{
	}

👉 To restrict an endpoint to users who have one of a set of roles, put them all in the string: I.e. this user is either Admin, Support, or User.

[Authorize(Roles = "Admin,Support,User")]

👉 To restrict an endpoint to users who have multiple roles, stack the attributes: I.e. this user is both Admin and Support.

[Authorize(Roles = "Admin")]
[Authorize(Roles = "Support")]

7. Other Utilities

CORS

Plinth has two handy extensions for easy setup of CORS headers to support front-end API callers.

  1. .AddCorsWithOrigins() is an extension method on IServiceCollection for setting up CORS headers to support a list of origins. Simply pass a comma delimited list of origins. 👉 If you pass simply "*" as the origin list, it will open up CORS to all origins. See note below.
services.AddCorsWithOrigins("http://localhost:4200,https://mysite.com");
services.AddCorsWithOrigins("*");
  1. .AddCorsFullOpen() is an extension method on IServiceCollection for setting up CORS headers to support all origins. 👉 This is not recommended for anything other than local development.
services.AddCorsFullOpen();

Both Extensions accept a Action<CorsPolicyBuilder> as an optional parameter for apply custom configuration to the CORS policy such as exposed headers.

Background Tasks

To run fire-and-forget tasks that a slight more durable than spawning threads or not awaiting tasks, first inject IBackgroundTaskQueue into your controller or manager class.

The object you will receive has this method, which you can use to queue an async function that will execute in the background.

void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);

If the host begins to shutdown gracefully, the queued tasks will be given as much time as the host allows before shutting down. This is typically about 10 seconds, but could be longer. Note that the cancellation token will be cancelled immediately when the host begins to shut down.

DI Utilities

In the Plinth.AspNetCore.DI names is a static class called PlinthActivatorUtilities. It is similar to the Microsoft version (ActivatorUtilities), but is enhanced to create objects which use the most parameters from the DI container and supply the rest from passed in parameters.

For example, this class has four parameters.

class TwoInjectedTwoRuntime
{
    public readonly Injected1 _p1;
    public readonly Injected2 _p2;
    public readonly string _p3;
    public readonly int _p4;
    public TwoInjectedTwoRuntime(Injected1 p1, Injected2 p2, string p3, int p4) { _p1 = p1; _p2 = p2; _p3 = p3; _p4 = p4; }
}

In the example below, you can see how some parameters come from the service provider, and the other two come from the passed parameters.

	var sc = new ServiceCollection();
	sc.AddSingleton(new Injected1());
	sc.AddSingleton(new Injected2());
	var sp = sc.BuildServiceProvider();
	var x = PlinthActivatorUtil.CreateInstance<TwoInjectedTwoRuntime>(sp, "hello", 8);

This facility can be used to instantiate objects with parameters from the container and others at runtime. This mechanism can be used as below to create factories for types that expect parameters (such as this one which expects a connection and a user parameter at runtime, but may have injected parameters as well.

private static Factory<T> NewManagerFactory<T>(this IServiceProvider sp)
	=> (conn, user) => PlinthActivatorUtil.CreateInstance<T>(sp, conn, user);

services.AddSingleton(sp => sp.NewManagerFactory<ThingManager>());

class ThingManager
{
	public ThingManager(IBackgroundTaskQueue bgQueue, IEmailSender emailer, ISqlConnection conn, string user)
	{
	}
}
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Plinth.AspNetCore:

Package Downloads
Plinth.Hangfire

Plinth Hangfire framework

Plinth.AspNetCore.Swagger

Plinth Swagger for ASP.NET CORE

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.7.0 195 11/12/2024
1.6.6 134 11/8/2024
1.6.5 580 8/31/2024
1.6.4 526 8/2/2024
1.6.3 1,122 5/15/2024
1.6.2 499 2/16/2024
1.6.1 3,513 1/5/2024
1.6.0 418 11/30/2023
1.5.10-b186.aca976b4 88 11/30/2023 1.5.10-b186.aca976b4 is deprecated because it is no longer maintained.
1.5.9 222 11/29/2023 1.5.9 is deprecated because it is no longer maintained.
1.5.9-b174.64153841 89 11/23/2023 1.5.9-b174.64153841 is deprecated because it is no longer maintained.
1.5.9-b172.dfc6e7bd 80 11/17/2023 1.5.9-b172.dfc6e7bd is deprecated because it is no longer maintained.
1.5.9-b171.4e2b92e2 82 11/4/2023 1.5.9-b171.4e2b92e2 is deprecated because it is no longer maintained.
1.5.8 363 10/23/2023 1.5.8 is deprecated because it is no longer maintained.
1.5.7 5,472 7/31/2023 1.5.7 is deprecated because it is no longer maintained.
1.5.6 3,634 7/13/2023 1.5.6 is deprecated because it is no longer maintained.
1.5.5 317 6/29/2023 1.5.5 is deprecated because it is no longer maintained.
1.5.4 1,243 3/7/2023 1.5.4 is deprecated because it is no longer maintained.
1.5.3 500 3/3/2023 1.5.3 is deprecated because it is no longer maintained.
1.5.2 661 1/11/2023 1.5.2 is deprecated because it is no longer maintained.
1.5.2-b92.7c961f5f 130 1/11/2023 1.5.2-b92.7c961f5f is deprecated because it is no longer maintained.
1.5.0 954 11/9/2022 1.5.0 is deprecated because it is no longer maintained.
1.5.0-b88.7a7c20cd 125 11/9/2022 1.5.0-b88.7a7c20cd is deprecated because it is no longer maintained.
1.4.7 4,226 10/20/2022 1.4.7 is deprecated because it is no longer maintained.
1.4.6 1,429 10/17/2022 1.4.6 is deprecated because it is no longer maintained.
1.4.5 1,536 10/1/2022 1.4.5 is deprecated because it is no longer maintained.
1.4.4 1,543 8/16/2022 1.4.4 is deprecated because it is no longer maintained.
1.4.3 1,806 8/2/2022 1.4.3 is deprecated because it is no longer maintained.
1.4.2 1,559 7/19/2022 1.4.2 is deprecated because it is no longer maintained.
1.4.2-b80.7fdbfd04 145 7/19/2022 1.4.2-b80.7fdbfd04 is deprecated because it is no longer maintained.
1.4.2-b74.acaf86f5 129 6/15/2022 1.4.2-b74.acaf86f5 is deprecated because it is no longer maintained.
1.4.1 1,630 6/13/2022 1.4.1 is deprecated because it is no longer maintained.
1.4.0 1,398 6/6/2022 1.4.0 is deprecated because it is no longer maintained.
1.3.8 2,624 4/12/2022 1.3.8 is deprecated because it is no longer maintained.
1.3.7 1,361 3/21/2022 1.3.7 is deprecated because it is no longer maintained.
1.3.6 1,404 3/17/2022 1.3.6 is deprecated because it is no longer maintained.
1.3.6-b67.ca5053f3 153 3/16/2022 1.3.6-b67.ca5053f3 is deprecated because it is no longer maintained.
1.3.6-b66.4a9683e6 138 3/16/2022 1.3.6-b66.4a9683e6 is deprecated because it is no longer maintained.
1.3.5 1,370 2/23/2022 1.3.5 is deprecated because it is no longer maintained.
1.3.4 1,877 1/20/2022 1.3.4 is deprecated because it is no longer maintained.
1.3.3 978 12/29/2021 1.3.3 is deprecated because it is no longer maintained.
1.3.2 893 12/11/2021 1.3.2 is deprecated because it is no longer maintained.
1.3.1 791 11/12/2021 1.3.1 is deprecated because it is no longer maintained.
1.3.0 797 11/8/2021 1.3.0 is deprecated because it is no longer maintained.
1.2.3 2,027 9/22/2021 1.2.3 is deprecated because it is no longer maintained.
1.2.2 1,026 8/20/2021 1.2.2 is deprecated because it is no longer maintained.
1.2.1 1,513 8/5/2021 1.2.1 is deprecated because it is no longer maintained.
1.2.0 2,599 8/1/2021 1.2.0 is deprecated because it is no longer maintained.
1.2.0-b37.a54030b9 170 6/24/2021 1.2.0-b37.a54030b9 is deprecated because it is no longer maintained.
1.1.6 4,699 3/22/2021 1.1.6 is deprecated because it is no longer maintained.
1.1.5 1,051 3/9/2021 1.1.5 is deprecated because it is no longer maintained.
1.1.4 1,995 2/27/2021 1.1.4 is deprecated because it is no longer maintained.
1.1.3 990 2/17/2021 1.1.3 is deprecated because it is no longer maintained.
1.1.2 1,012 2/12/2021 1.1.2 is deprecated because it is no longer maintained.
1.1.1 1,291 2/1/2021 1.1.1 is deprecated because it is no longer maintained.
1.1.0 1,038 12/16/2020 1.1.0 is deprecated because it is no longer maintained.
1.1.0-b27.b66c309b 290 11/15/2020 1.1.0-b27.b66c309b is deprecated because it is no longer maintained.
1.0.12 2,874 10/18/2020 1.0.12 is deprecated because it is no longer maintained.
1.0.11 1,091 10/6/2020 1.0.11 is deprecated because it is no longer maintained.
1.0.10 1,063 9/30/2020 1.0.10 is deprecated because it is no longer maintained.
1.0.9 1,131 9/29/2020 1.0.9 is deprecated because it is no longer maintained.
1.0.8 1,225 9/26/2020 1.0.8 is deprecated because it is no longer maintained.
1.0.7 1,188 9/19/2020 1.0.7 is deprecated because it is no longer maintained.
1.0.6 1,144 9/3/2020 1.0.6 is deprecated because it is no longer maintained.
1.0.5 1,255 9/2/2020 1.0.5 is deprecated because it is no longer maintained.
1.0.4 1,449 9/1/2020 1.0.4 is deprecated because it is no longer maintained.
1.0.3 998 9/1/2020 1.0.3 is deprecated because it is no longer maintained.
1.0.2 1,110 8/29/2020 1.0.2 is deprecated because it is no longer maintained.
1.0.1 1,095 8/29/2020 1.0.1 is deprecated because it is no longer maintained.
1.0.0 1,124 8/29/2020 1.0.0 is deprecated because it is no longer maintained.
1.0.0-b1.c22f563d 256 8/28/2020 1.0.0-b1.c22f563d is deprecated because it is no longer maintained.

net9.0 support