Plinth.AspNetCore
1.4.7
Prefix Reserved
See the version list below for details.
dotnet add package Plinth.AspNetCore --version 1.4.7
NuGet\Install-Package Plinth.AspNetCore -Version 1.4.7
<PackageReference Include="Plinth.AspNetCore" Version="1.4.7" />
paket add Plinth.AspNetCore --version 1.4.7
#r "nuget: Plinth.AspNetCore, 1.4.7"
// Install Plinth.AspNetCore as a Cake Addin #addin nuget:?package=Plinth.AspNetCore&version=1.4.7 // Install Plinth.AspNetCore as a Cake Tool #tool nuget:?package=Plinth.AspNetCore&version=1.4.7
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 aPlinth.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.
- For MVC, use
- 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
- 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
- Adds an API at
e.AutoEndpoints.EnableClient();
- Adds a logging api at
/client/webapp
which the front end can use to log information and exceptions
- Adds a logging api at
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.
- Adds
.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()
- More options are available using
- Renew a token via
.Refresh(AuthenticationToken origToken)
- To retrieve the original token, use
this.GetAuthenticationToken()
, an extension available in thePlinth.AspNetCore.SecureToken
namespace
- To retrieve the original token, use
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()
returnsAuthenticationToken
this.GetAuthenticatedUserName()
returns user name fieldthis.GetAuthenticatedUserGuid()
returns user guidthis.GetAuthenticatedUserSessionGuid()
returns session guidthis.GetAuthenticatedUserRoles()
returns a set of user rolesthis.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 thePlinth.AspNetCore.Jwt
namespace
- To retrieve the original token, use
Extensions
These extensions are provided on ControllerBase
to retrieve token information:
this.GetJwt()
returns the JWT as a stringthis.GetAuthenticatedUserName()
returns user name fieldthis.GetAuthenticatedUserId()
returns user idthis.GetAuthenticatedUserSessionGuid()
returns session guidthis.GetAuthenticatedUserRoles()
returns a set of user rolesthis.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
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 | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. 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 was computed. 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. |
.NET Core | netcoreapp3.1 is compatible. |
-
.NETCoreApp 3.1
- Plinth.AspNetCore.NewtonsoftJson (>= 1.4.7)
- Plinth.Common (>= 1.4.7)
- Plinth.HttpApiClient.Common (>= 1.4.7)
- Plinth.Security (>= 1.4.7)
- Plinth.Security.Jwt (>= 1.4.7)
- Plinth.Serialization (>= 1.4.7)
-
net6.0
- Plinth.AspNetCore.NewtonsoftJson (>= 1.4.7)
- Plinth.Common (>= 1.4.7)
- Plinth.HttpApiClient.Common (>= 1.4.7)
- Plinth.Security (>= 1.4.7)
- Plinth.Security.Jwt (>= 1.4.7)
- Plinth.Serialization (>= 1.4.7)
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 | 65 | 11/12/2024 |
1.6.6 | 100 | 11/8/2024 |
1.6.5 | 441 | 8/31/2024 |
1.6.4 | 523 | 8/2/2024 |
1.6.3 | 1,087 | 5/15/2024 |
1.6.2 | 496 | 2/16/2024 |
1.6.1 | 3,499 | 1/5/2024 |
1.6.0 | 415 | 11/30/2023 |
1.5.10-b186.aca976b4 | 78 | 11/30/2023 |
1.5.9 | 213 | 11/29/2023 |
1.5.9-b174.64153841 | 80 | 11/23/2023 |
1.5.9-b172.dfc6e7bd | 71 | 11/17/2023 |
1.5.9-b171.4e2b92e2 | 72 | 11/4/2023 |
1.5.8 | 352 | 10/23/2023 |
1.5.7 | 5,297 | 7/31/2023 |
1.5.6 | 3,559 | 7/13/2023 |
1.5.5 | 308 | 6/29/2023 |
1.5.4 | 1,232 | 3/7/2023 |
1.5.3 | 491 | 3/3/2023 |
1.5.2 | 650 | 1/11/2023 |
1.5.2-b92.7c961f5f | 121 | 1/11/2023 |
1.5.0 | 945 | 11/9/2022 |
1.5.0-b88.7a7c20cd | 116 | 11/9/2022 |
1.4.7 | 4,215 | 10/20/2022 |
1.4.6 | 1,418 | 10/17/2022 |
1.4.5 | 1,527 | 10/1/2022 |
1.4.4 | 1,532 | 8/16/2022 |
1.4.3 | 1,795 | 8/2/2022 |
1.4.2 | 1,548 | 7/19/2022 |
1.4.2-b80.7fdbfd04 | 134 | 7/19/2022 |
1.4.2-b74.acaf86f5 | 118 | 6/15/2022 |
1.4.1 | 1,619 | 6/13/2022 |
1.4.0 | 1,389 | 6/6/2022 |
1.3.8 | 2,613 | 4/12/2022 |
1.3.7 | 1,350 | 3/21/2022 |
1.3.6 | 1,395 | 3/17/2022 |
1.3.6-b67.ca5053f3 | 144 | 3/16/2022 |
1.3.6-b66.4a9683e6 | 129 | 3/16/2022 |
1.3.5 | 1,361 | 2/23/2022 |
1.3.4 | 1,865 | 1/20/2022 |
1.3.3 | 965 | 12/29/2021 |
1.3.2 | 881 | 12/11/2021 |
1.3.1 | 779 | 11/12/2021 |
1.3.0 | 785 | 11/8/2021 |
1.2.3 | 2,013 | 9/22/2021 |
1.2.2 | 1,014 | 8/20/2021 |
1.2.1 | 1,501 | 8/5/2021 |
1.2.0 | 2,587 | 8/1/2021 |
1.2.0-b37.a54030b9 | 156 | 6/24/2021 |
1.1.6 | 4,686 | 3/22/2021 |
1.1.5 | 1,039 | 3/9/2021 |
1.1.4 | 1,983 | 2/27/2021 |
1.1.3 | 976 | 2/17/2021 |
1.1.2 | 1,000 | 2/12/2021 |
1.1.1 | 1,277 | 2/1/2021 |
1.1.0 | 1,024 | 12/16/2020 |
1.1.0-b27.b66c309b | 276 | 11/15/2020 |
1.0.12 | 2,860 | 10/18/2020 |
1.0.11 | 1,077 | 10/6/2020 |
1.0.10 | 1,051 | 9/30/2020 |
1.0.9 | 1,119 | 9/29/2020 |
1.0.8 | 1,211 | 9/26/2020 |
1.0.7 | 1,176 | 9/19/2020 |
1.0.6 | 1,132 | 9/3/2020 |
1.0.5 | 1,243 | 9/2/2020 |
1.0.4 | 1,436 | 9/1/2020 |
1.0.3 | 984 | 9/1/2020 |
1.0.2 | 1,098 | 8/29/2020 |
1.0.1 | 1,083 | 8/29/2020 |
1.0.0 | 1,112 | 8/29/2020 |
1.0.0-b1.c22f563d | 244 | 8/28/2020 |
added detailed readme