App - Plugins

Up till now the Apps above only have only used functionality built into ServiceStack, to enable even greater functionality but still retain all the benefits of developing Web Apps you can drop .dll with custom functionality into your Web App's /plugins folder. The plugins support in Web Apps is as friction-less as we could make it, there's no configuration to maintain or special interfaces to implement, you're able to drop your existing implementation .dll's as-is into the App's /plugins folder.

Plugins allow "no touch" sharing of ServiceStack Plugins, Services, Script Methods Sharp Code Pages, Validators, etc. contained within .dll's or .exe's dropped in a Sharp App's /plugins folder which are auto-registered on startup. This source code for plugins used in this App were built from the .NET Core 3.1 projects in the /plugins/src folder.

Plugins Sharp App Screenshot

You can run this Gist Desktop App via URL Scheme from (Windows Desktop App):

app://plugins

Or via command-line:

$ app open plugins

Cross platform (Default Browser):

$ x open plugins

Registering ServiceStack Plugins

ServiceStack Plugins can be added to your App by listing it's Type Name in the features config entry in app.settings:

debug true
name Plugins Sharp App
features CustomPlugin, OpenApiFeature, PostmanFeature, CorsFeature, ValidationFeature
CustomPlugin { ShowProcessLinks: true }
ValidationFeature { ScanAppHostAssemblies: true }
CefConfig { width:1150, height:1050 }

All plugins listed in features will be added to your Web App's AppHost in the order they're specified. They can further customized by adding a separate config entry with the Plugin Name and a JavaScript Object literal to populate the Plugin at registration, e.g the config above is equivalent to:

Plugins.Add(new CustomPlugin { ShowProcessLinks = true });
Plugins.Add(new OpenApiFeature());
Plugins.Add(new PostmanFeature());
Plugins.Add(new CorsFeature());
Plugins.Add(new ValidationFeature { ScanAppHostAssemblies = true });

Custom Plugin

In this case it tells our CustomPlugin from /plugins/ServerInfo.dll to also show Process Links in its /metadata Page:

public class CustomPlugin : IPlugin
{
    public bool ShowDrivesLinks { get; set; } = true;
    
    public bool ShowProcessLinks { get; set; }

    public void Register(IAppHost appHost)
    {
        if (ShowDrivesLinks)
        {
            var diskFormat = Env.IsWindows ? "NTFS" : "ext2";
            appHost.GetPlugin<MetadataFeature>()
                .AddPluginLink("/drives", "All Disks")
                .AddPluginLink($"/drives?DriveFormatIn={diskFormat}", $"{diskFormat} Disks");
        }

        if (ShowProcessLinks)
        {
            appHost.GetPlugin<MetadataFeature>()
                .AddPluginLink("/processes", "All Processes")
                .AddPluginLink("/process/current", "Current Process");
        }
    }
}

Where as it was first registered in the list will appear before any links registered by other plugins:

Metadata screenshot

Built-in Plugins

It also tells the ValidationFeature to scan all Service Assemblies for Validators and to automatically register them which is how ServiceStack was able to find the ContactValidator used to validate the StoreContact request.

Other optional plugins registered in this App is the metadata Services required for Open API, Postman as well as support for CORS. You can check the Metadata Debug Inspector for all Plugins loaded in your AppHost.

.NET Extensibility

Plugins can also implement .NET Core's IStartup to be able to register any required dependencies without any coupling to any Custom AppHost.

To simplify configuration you can use the plugins/* wildcard in app.settings at the end of an ordered plugin list to register all remaining Plugins it finds in the apps /plugins folder:

features OpenApiFeature, PostmanFeature, CorsFeature, ValidationFeature, plugins/*
CustomPlugin { ShowProcessLinks: true }

Each plugin registered can continue to be furthered configured by specifying its name and a JavaScript object literal as seen above.

The /plugins2 App shows an example of this with the StartupPlugin registering a StartupDep dependency which is used by its StartupServices at runtime:

public class StartupDep
{
    public string Name { get; } = nameof(StartupDep);
}

public class StartupPlugin : IPlugin, IStartup
{
    public void Configure(IApplicationBuilder app) {}

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton(new StartupDep());
        return null;
    }

    public void Register(IAppHost appHost)
    {
        appHost.GetPlugin<MetadataFeature>()
            .AddPluginLink("/startup-dep", "Startup Service");
    }       
}

[Route("/startup-dep")]
public class GetStartupDep : IReturn<string> {}

public class StartupServices : Service
{
    public StartupDep StartupDep { get; set; }

    [AddHeader(ContentType = MimeTypes.PlainText)]
    public object Any(GetStartupDep request) => StartupDep.Name;
}

ServiceStack Ecosystem

All Services loaded by plugins continue to benefit from ServiceStack's rich metadata services, including being listed in the /metadata page, being able to explore and interact with Services using /swagger-ui/ as well as being able to generate Typed APIs for the most popular Mobile, Web and Desktop platforms.

made with by ServiceStack