RossWright.MetalNexus.Abstractions 8.0.0-beta016

This is a prerelease version of RossWright.MetalNexus.Abstractions.
dotnet add package RossWright.MetalNexus.Abstractions --version 8.0.0-beta016                
NuGet\Install-Package RossWright.MetalNexus.Abstractions -Version 8.0.0-beta016                
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="RossWright.MetalNexus.Abstractions" Version="8.0.0-beta016" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add RossWright.MetalNexus.Abstractions --version 8.0.0-beta016                
#r "nuget: RossWright.MetalNexus.Abstractions, 8.0.0-beta016"                
#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 RossWright.MetalNexus.Abstractions as a Cake Addin
#addin nuget:?package=RossWright.MetalNexus.Abstractions&version=8.0.0-beta016&prerelease

// Install RossWright.MetalNexus.Abstractions as a Cake Tool
#tool nuget:?package=RossWright.MetalNexus.Abstractions&version=8.0.0-beta016&prerelease                

Ross Wright's Metal Nexus

by Ross Wright

Copyright 2023 Pross Co. All Rights Reserved.

Description

MetalNexus allows MediatR requests to magically tranverse the client-server connection. Imagine, sending a request in your Blazor client code and have it handled by a RequestHandler on the server with almost no extra code complete with Open API / Swagger document generation.

A note about quickstart

MetalNexus has been set with defaults to work as easily as possible out of the box for evaluation, but if you do decide to seriously use it, there are two recommendations you should really do to optimize the performance of initialization. Please checkout the Recommendations section near the end of this document. Otherwise, enjoy these quickstart instructions...

Setup

To setup MetalNexus and auto-detect request handlers on an ASP.NET Core project, add the RossWright.MetalNexus.Server package and call AddMetalNexusServer on the ServiceCollection in your program.cs file and call app.UseMetalNexusServer() after the Build call but before the Run call,

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMetalNexusServer(_ => _.ScanAssembliesStartingWith("MyCorp.MyApp");
...
var app = builder.Build();
...
app.UseMetalNexusServer();
...
app.Run();

For a client project (Blazor WASM or an ASP.NET Core Server using handlers on another server), add the RossWright.MetalNexus package and call AddMetalNexusClient on the ServiceCollection in your program.cs file:

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddMetalNexusClient(_ => _.ScanAssembliesStartingWith("MyCorp.MyApp"));

Defining API Requests

To specify a Request should be handled on the server add the RossWright.MetalNexus.Abstractions package and decorate the class with the ApiRequest attribute as follows,

[ApiRequest]
public class MyRequest : IRequest<MyResponse[]>

Without parameters, an HTTP Verb and serialization will be chosen based on the complexity of the Request class and path is generated based on the Request. You can specify the HTTP Verb, serialization (Query or Body) and path explicitly using the protocol and path parameters of the ApiRequest attribute:

[ApiRequest(HttpProtocol.PostViaBody, "/api/request")]
public class MyRequest : IRequest<MyResponse>

Note Body is only supported for POST, PUT and PATCH verbs. Get and Delete verbs are also available. Serializing the request as query parameters limits the allowable complexity of the Request object considerably. Note when transmissing sensitive data you should always specify a body serialization protocol.

Anonymous and Authenticated attributes can be used with MetalNexus ApiRequest as normal. Such as:

[Anonymous, ApiRequest(HttpProtocol.Get, "/api/request")]
public class MyRequest : IRequest<MyResponse[]>

Open API and Swagger documentation

To enable Swagger generation of the MetalNexus endpoints, call UseMetalNexus on the AddSwaggerGen options object,

builder.Services.AddSwaggerGen(options => options.UseMetalNexus());

The swagger tag (commonly shows as the controller name) for ApiRequests can be customized using the swaggerTag paramter of the ApiRequest attribute,

[ApiRequest(path: "/api/request", swaggerTag: "My Special Requests")]
public class MyRequest : IRequest<MyResponse>

Using MetalNexus with multiple servers

To connect to APIs on multiple servers, setup names HttpClients for each server and specify those names in the ApiRequest attribute. When the request is marshaled, the call will be made using the specified named client

[ApiRequest(path: "doTheThing", httpClientName: "ApiOne")]
public class MyFirstRequest : IRequest<MyFirstResponse>
...
[ApiRequest(path: "doTheThing", httpClientName: "ApiTwo")]
public class MySecondRequest : IRequest<MySecondResponse>
...
//in program.cs
services.AddHttpClient("ApiOne", _ => _.BaseAddress = new Uri("https://server1.com/api"));
services.AddHttpClient("ApiTwo", _ => _.BaseAddress = new Uri("https://server2.com/api"));
...
var firstResponse = await mediator.Send(new MyFirstRequest()); //calls server1.com/api/doTheThing
var secondResponse = await mediator.Send(new MySecondRequest()); //calls server2.com/api/doTheThing

Endpoint Schema customization

On initialization MetalNexus discovers ApiRequests and defines API Endpoints in an Endpoint Schema. This schema specifies the protocol, path, authorization and Open API tag for each endpoint. The default behavior is to simply use the information specified in attributes. This behavior can be customized or completely overridden.

Customizing Endpoint Schema

To customize the schema building behavior, when configuring MetalNexus call ConfigureEndpointSchema with a closure to configure the default endpoint schema. For example,

builder.Services.AddMetalNexusServer(_ =>
{
    _.ScanAssembliesStartingWith("MyCorp.MyApp");
    _.ConfigureEndpointSchema(_ =>
    {
        _.ApiPathPrefix = "/api/v1";
        _.RequiresAuthenticationByDefault = true;
        _.PathStrategy = new TrimFixedPreamblePathStrategy("MyCorp.MyApp.Endpoints");
    });
});

Path Strategies are used to determine the API path from the Request class's namespace and name. Completely custom Path Strategies can be implemented and specified, but MetalNexus comes with 5 strategies out of the box. A request object with full type name MyCorp.MyApp.Endpoints.Users.GetUsers is used to illustrate the strategies. They are:

  • NoNamespacePathStrategy - the typename is used and the namespace is discarded. ("/GetUsers")
  • UseFullNamePathStrategy - the full namespace and class name is used. ("/MyCorp/MyApp/Endpoints/Users/GetUsers")
  • TrimFixedPreamblePathStrategy - a specified prefix is trimmed if present. ("/Users/GetUsers" assuming "MyCorp.MyApp.Endpoints" was specified)
  • TrimDefaultNamespacePathStrategy - the most common root namespace for your projects is determined, assumed to be your root namespace and trimmed from request paths ("/Endpoints/Users/GetUsers" assuming "MyCorp.MyApp" is the root namespace of most of your types)
  • TrimRequestNamespacePathStrategy - similar to TrimDefaultNamespacePathStrategy, but only classes decorated with the ApiRequest attribute are considered when determining the root namespace. Note this is the default strategy and will often have the same result as if you configured TrimFixedPreamblePathStrategy with your api request's namespace root, just slower since it uses reflection to try to figure out the root namespace. ("/Users/GetUsers")

Overriding Endpoint Schema

To completely override the endpoint, when configuring MetalNexus call UseCustomEndpointSchema and pass an instance of your IEndpointSchema implementation. IEndpointSchema has one method to implement, DefineEndpoints which takes a collection of IDiscoveredApiRequest objects detailing information collected in the discovery process. The Endpoint Schema exposes a collection of IEndpoint objects your must contruct that is used by the MetalNexus middleware to call and/or handle requests. Note that IDiscoveredApiRequest has a property RequestMustUseBody, and if you specify a protocol that does not allow for a request body or specify the endpoint uses query parameters for the request, it will fail on use.

Recommendations

  1. Organize your solution with a project containing your API Request class definitions that is referenced by both your Blazor client project and ASP.NET Core server project. This is a common pattern for defining data transfer objects anyway no matter what method you use for APIs.

  2. AddMetalNexusServer and AddMetalNexusClient use the same configuration object and in order for the system to function correctly the configuration for both sides of your client-server must be identical. The examples above use the closure override to configure the client and server, but there is an override that takes an instance of IMetalNexusConfiguration. The recommended pattern is to define your implementation of the configuration in a shared libary with your Api Requests and use an instance of that same class when configuring both the client and server.

  3. Use the TrimFixedPreamblePathStrategy and configure it with the root namespace for your API requests rather than rely on the default behavior provided by TrimRequestNamespacePathStrategy. In the end they likely produce in the same result, but TrimRequestNamespacePathStrategy uses reflection which can slow down your initialization quite a bit if your project is large.

  4. Be selective with what assemblies you scan. In the examples above I used ScanAssembliesStartingWith which is better than the default behavior of scanning all assemblies, but can usually be reduced to the single shared project containing your API Request definitions using ScanAssemblies, ScanOnlyThisAssembly or ScanAssemblyContaining. Be very cautious using ScanAssembliesInFolderStartingWith as it looks for assmblies in your executable's file folder - which is useful for plug-in architectures, but should definitely be considered an attack vector to your program.

Licensing

A license must be purchased to use RossWright.Metal libaries in a production environment. For development enviroments, using the libraries without a license will show a console message on initialization and cease functioning after one hour. To install your license file include it in the executable project with the Build Action set to Embedded Resource. The file can be renamed as needed, but must end with the extension .license.

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on RossWright.MetalNexus.Abstractions:

Package Downloads
RossWright.MetalNexus

Client-side package for MetalNexus

RossWright.MetalGuardian.MetalNexus

MetalGuardian MetalNexus Requests

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
8.0.0-beta016 32 10/23/2024
8.0.0-beta015 33 10/23/2024
8.0.0-beta014 51 10/19/2024
8.0.0-beta007 63 9/28/2024