ServiceStack v5

v5.0.2 Update

Happy New 2018 All! We hope you've all had a long, relaxing break over the holidays and are re-energized for a productive start to the new year. As we expect most Customers will have held off upgrading to v5 until the start of the new year we wanted to get the latest release out before then so Customers can immediately upgrade to the best release available.

Future Versioning Scheme

Historically ServiceStack releases are focused around "Major release schedules" published along with detailed Release Notes describing new features and changes added in each release. All packages are published together in "lockstep" with the same version number so the effort to upgrade ServiceStack projects can be done all at same time, with low frequency.

Whilst we want to minimize the effort for Customers to upgrade we also want to make any fixes or enhancements to the previous release available sooner as there are often fixes reported and resolved immediately after each release and made available in our pre-release packages that most Customers wont get until the next major Release on NuGet.

Enhancement Releases

To deliver updates sooner we're going to dedicate time immediately after each release to resolving issues and adding enhancements to existing features so we can publish update releases before starting work on new major features. Update releases will be primarily additive and minimally disruptive so they're safe to upgrade.

To distinguish updates from normal releases we'll use the {PATCH} version to indicate an Enhancement Release and use the {MINOR} version for normal major releases:

{MAJOR}.{MINOR}.{PATCH}

The {MAJOR} is reserved for Major releases like v5 containing structural changes that may require changes to environment and/or project configurations. For v5 these included:

  • Merge .Core packages into main NuGet packages and upgrade to NuGet v3 nuspec
  • Sign all net45 .dll's and merge .Signed packages into main NuGet packages
  • Upgrading to .NET Standard 2.0 and dropping PCL support
  • Upgrading minimum .NET Framework version requirements for ServiceStack.RabbitMq and ServiceStack.Azure

Major structural releases should be few and far between with currently no plans for any in the immediate future.

A new {MINOR} version will be used for normal "Major" releases which will have a {PATCH} version of 0. An even {PATCH} version number indicates an "Update" release published to NuGet whilst an odd version number indicates a "pre-release" version that's only available on Pre Release NuGet packages, e.g:

  • v5.0.0 - Current Major Release with structural changes
    • v5.0.2 - Enhancement of Major v5.0.0 Release
    • v5.0.3 - Pre-release packages published to MyGet only
    • v5.0.4? - Enhancement of Major v5.0.0 Release (if any)
  • v5.1.0 - Next Major Release
    • v5.1.1 - Pre-release packages published to MyGet only
    • v5.1.2? - Enhancement of Major v5.1.0 Release (if any)
  • ...
  • v6.0.0 - Next Major Release with structural changes

Run ASP.NET Core Apps on the .NET Framework

A primary use-case affected from unifying the .Core packages into the main NuGet packages was being able to run ASP.NET Core Apps on the .NET Framework which stems from the 2 different builds contained in ServiceStack NuGet packages:

  • net45 - Contains support for running ASP.NET Web or Self-Hosting HttpListener App Hosts
  • netstandard2.0 - Contains support for only running on ASP.NET Core App Hosts

Where the net45 builds always get used when they're added to any .NET Framework project. To allow .NET v4.6.1+ Clients to share .NET Standard 2.0 assemblies with .NET Core Servers we continued to publish .Core Client packages but we were also quickly made aware there's several Customers who prefer to create ASP.NET Core Apps running on the .NET Framework in order to be able to use existing .NET Framework assemblies at the expense of not being able to run on the faster and multi platform .NET Core 2.0 runtime.

To support this use-case we're re-publishing .Core NuGet packages which contains only the .NET Standard 2.0 builds in order to force .NET Framework projects to use .NET Standard 2.0 builds which contains support for running ASP.NET Core Apps. Currently the complete list of .Core packages which contains .NET Standard 2.0 builds include:

  • ServiceStack.Text.Core
  • ServiceStack.Interfaces.Core
  • ServiceStack.Client.Core
  • ServiceStack.HttpClient.Core
  • ServiceStack.Core
  • ServiceStack.Common.Core
  • ServiceStack.Mvc.Core
  • ServiceStack.Server.Core
  • ServiceStack.Redis.Core
  • ServiceStack.OrmLite.Core
  • ServiceStack.OrmLite.Sqlite.Core
  • ServiceStack.OrmLite.SqlServer.Core
  • ServiceStack.OrmLite.PostgreSQL.Core
  • ServiceStack.OrmLite.MySql.Core
  • ServiceStack.OrmLite.MySqlConnector.Core
  • ServiceStack.Aws.Core
  • ServiceStack.Azure.Core
  • ServiceStack.RabbitMq.Core
  • ServiceStack.Api.OpenApi.Core
  • ServiceStack.Admin.Core
  • ServiceStack.Stripe.Core
  • ServiceStack.Kestrel

Ultimately support for whether a .NET Standard 2.0 library will run on the .NET Framework depends on whether external dependencies also support this scenario which as it's a more niche use-case, will be a less tested scenario.

There are also other inconsistencies like not being able to reference the Microsoft.AspNetCore.All meta package which only supports .NET Core 2.0 projects, instead you would need to reference the packages individually. To make it as easy as possible to get started we've created a new NetFrameworkCoreTemplates GitHub Organization containing popular starting templates for running ASP.NET Core Apps on .NET Framework (default v4.7) which as a convention all have the -corefx suffix:

  • web-corefx - .NET Framework ASP.NET Core Website
  • empty-corefx - .NET Framework ASP.NET Core Single Project Website
  • selfhost-corefx - .NET Framework ASP.NET Core self-hosting Console App
  • mvc-corefx - .NET Framework ASP.NET Core MVC Website
  • razor-corefx - .NET Framework ASP.NET Core Website with ServiceStack.Razor
  • templates-corefx - .NET Framework ASP.NET Core Templates Bootstrap Website

The latest @servicestack/cli v1.0.2 has been updated to include this additional config source, including them in the list of available templates:

$ dotnet-new

Which will let you create an ASP.NET Core App running on the .NET Framework v4.7 with:

$ npm install -g @servicestack/cli

$ dotnet-new web-corefx AcmeNetFx

Which can then be opened in your preferred VS.NET or Project Rider C# IDE.

ServiceStack Mobile and Desktop Apps

The HelloMobile project has been rewritten to use the latest v5 .NET Standard 2.0 and .NET Framework clients and contains multiple versions of the same App demonstrating a number of different calling conventions, service integrations and reuse possibilities for each of the following platforms:

  • WPF
  • UWP
  • Xamarin.Android
  • Xamarin.iOS
  • Xamarin.OSX
  • Xamarin.Forms
    • iOS
    • Android
    • UWP

ServiceStack Server App

Each App calls the same simple ServiceStack Server WebServices.cs implementation that thanks to ServiceStack's versatility, can be hosted on any of .NET's popular HTTP Server hosting configurations:

Server.NetCore

The AppHost for hosting the ServiceStack Services in a ASP.NET Core 2.0 App:

public class AppHost : AppHostBase
{
    public AppHost() : base(nameof(Server.NetCore), typeof(WebServices).Assembly) { }
    public override void Configure(Container container) => SharedAppHost.Configure(this);
}

Server.NetCoreFx

The same source code can be used to run a ServiceStack ASP.NET Core App on the .NET Framework:

public class AppHost : AppHostBase
{
    public AppHost() : base(nameof(Server.NetCoreFx), typeof(WebServices).Assembly) { }
    public override void Configure(Container container) => SharedAppHost.Configure(this);
}

The difference between a .NET Framework v4.7 and a .NET Core 2.0 ASP.NET Core App is in Server.NetCoreFx.csproj where it references ServiceStack.Core NuGet package to force using the .NET Standard 2.0 version of ServiceStack that contains the support for hosting ASP.NET Core Apps.

Server.AspNet

The same source code is also used for hosting classic ASP.NET Web Applications:

public class AppHost : AppHostBase
{
    public AppHost() : base(nameof(Server.AspNet), typeof(WebServices).Assembly) { }
    public override void Configure(Container container) => SharedAppHost.Configure(this);
}

Server.HttpListener

Alternatively to host in a .NET Framework Self-Hosting HttpListener, the AppHost needs to inherit from AppSelfHostBase:

public class AppHost : AppSelfHostBase
{
    public AppHost() : base(nameof(Server.HttpListener), typeof(WebServices).Assembly) {}
    public override void Configure(Container container) => SharedAppHost.Configure(this);
}

Shared ServiceStack Services

All AppHost's above are configured to run the same Web Services implementation below:

public static class SharedAppHost
{
    public static void Configure(IAppHost appHost)
    {
        appHost.Config.DefaultRedirectPath = "/metadata";

        appHost.Plugins.Add(new CorsFeature());

        appHost.Plugins.Add(new AuthFeature(() => new AuthUserSession(),
            new IAuthProvider[] {
                new CustomCredentialsAuthProvider(),
                new JwtAuthProvider
                {
                    AuthKeyBase64 = Config.JwtAuthKeyBase64,
                    RequireSecureConnection = false,
                },
            }));

        appHost.Plugins.Add(new EncryptedMessagesFeature
        {
            PrivateKeyXml = Config.PrivateKeyXml
        });
    }

    public class CustomCredentialsAuthProvider : CredentialsAuthProvider
    {
        public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
        {
            return userName == "user" && password == "pass";
        }
    }
}

public class WebServices : Service
{
    public object Any(Hello request) => new HelloResponse { Result = "Hello, " + request.Name };
}

[Authenticate]
public class AuthServices : Service
{
    public object Any(HelloAuth request)
    {
        return new HelloResponse { Result = "Hello, " + request.Name };
    }
}

Which are all configured to listen on the same http://localhost:5000 address.

Client Apps

Once the server is running you can run any of the included Client Apps by selecting it as the StartUp Project in the context menu and running it with Ctrl+F5.

Each of the Client Apps have the same functionality demonstrating a number of popular use-cases scenarios for accessing ServiceStack Services. Whilst the event handler registrations in each App can vary, the source code used to call ServiceStack Services remains the same with all calls following the same approach to call Services by sending a populated Request DTOs in the ServiceModel.dll, sent utilizing different APIs and features built into ServiceStack's Service Clients.

WPF

Here is WPF's complete MainWindow.xaml.cs implementation below:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private const string BaseUrl = Config.BaseUrl;
    public IServiceClient CreateClient() => new JsonServiceClient(BaseUrl);

    private void btnSync_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var client = CreateClient();
            var response = client.Get(new Hello { Name = "Sync" });
            lblResults.Text = response.Result;
        }
        catch (Exception ex)
        {
            lblResults.Text = ex.ToString();
        }
    }

    private async void btnAsync_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var client = CreateClient();
            var response = await client.GetAsync(new Hello { Name = "Async" });
            lblResults.Text = response.Result;
        }
        catch (Exception ex)
        {
            lblResults.Text = ex.ToString();
        }
    }

    private async void btnGateway_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var gateway = new SharedGateway(BaseUrl);
            var greeting = await gateway.SayHello("Gateway");
            lblResults.Text = greeting;
        }
        catch (Exception ex)
        {
            lblResults.Text = ex.ToString();
        }
    }

    private async void btnAuth_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var client = CreateClient();
            await client.PostAsync(new Authenticate
            {
                provider = "credentials",
                UserName = "user",
                Password = "pass",
            });

            var response = await client.GetAsync(new HelloAuth { Name = "Auth" });

            lblResults.Text = response.Result;
        }
        catch (Exception ex)
        {
            lblResults.Text = ex.ToString();
        }
    }

    private async void btnJwt_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var authClient = CreateClient();
            var authResponse = await authClient.PostAsync(new Authenticate
            {
                provider = "credentials",
                UserName = "user",
                Password = "pass",
            });

            var client = new JsonServiceClient(BaseUrl)
            {
                BearerToken = authResponse.BearerToken //JWT
            };

            var response = await client.GetAsync(new HelloAuth { Name = "JWT Auth" });

            lblResults.Text = response.Result;
        }
        catch (Exception ex)
        {
            lblResults.Text = ex.ToString();
        }
    }

    private async void btnEncrypted_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            var client = (IJsonServiceClient)CreateClient();
            var encryptedClient = client.GetEncryptedClient(Config.PublicKeyXml);

            var response = await encryptedClient.SendAsync(new Hello { Name = "Encrypted Client" });

            lblResults.Text = response.Result;
        }
        catch (Exception ex)
        {
            lblResults.Text = ex.ToString();
        }
    }
}

UWP

Xamarin.Android

Xamarin.iOS

OSX

Xamarin.Forms

Xamarin.Forms enables maximum reuse possible in C# Apps by letting you develop and share a Single UI across all your Apps.

For Basic Apps with Simple UI's like this no specialization was needed as you can define both the UI and its complete implementation in a shared Client.XamarinForms project using an Xamarin.Forms MainPage.xaml markup to develop its UI using an XAML dialect that closely resembles the XAML used in UWP and WPF Apps:

<Grid Margin="10,20,0,100">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <StackLayout Grid.Row="0" Orientation="Horizontal">
        <Button x:Name="btnSync" Text="Sync" />
        <Button x:Name="btnAsync" Text="Async" />
        <Button x:Name="btnGateway" Text="Gateway" />
        <Button x:Name="btnAuth" Text="Auth" />
        <Button x:Name="btnJwt" Text="JWT" />
        <Button x:Name="btnEncrypted" Text="Encrypted" />
    </StackLayout>

    <Label Grid.Row="1" x:Name="lblResults" Text="" FontSize="36"
           HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />

</Grid>

XamarinForms iOS

XamarinForms UWP

XamarinForms Android

.NET Standard 2.0 Logging Providers

Whilst our recommendation is to use .NET Core's Logging Abstraction some Customers prefer to avoid this abstraction and configure logging directly with ServiceStack. To support this we've included .NET Standard 2.0 builds to the following logging providers:

  • ServiceStack.Logging.Serilog
  • ServiceStack.Logging.Slack

Async Error Handling

This is an enhancement to our Expanded Async Support where there's now the option to register async exception handlers, e.g:

this.ServiceExceptionHandlersAsync.Add(async (httpReq, request, ex) =>
{
    await LogServiceExceptionAsync(httpReq, request, ex);

    if (ex is UnhandledException)
        throw ex;

    if (request is IQueryDb)
        return DtoUtils.CreateErrorResponse(request, new ArgumentException("AutoQuery request failed"));

    return null;
});

this.UncaughtExceptionHandlersAsync.Add(async (req, res, operationName, ex) =>
{
    await res.WriteAsync($"UncaughtException '{ex.GetType().Name}' at '{req.PathInfo}'");
    res.EndRequest(skipHeaders: true);
});

If you were instead inheriting OnServiceException or OnUncaughtException in your AppHost they now return a Task type.

AutoQuery DISTINCT

AutoQuery added support querying DISTINCT fields by prefixing the custom fields list with DISTINCT , example using QueryString:

?Fields=DISTINCT Field1,Field2

Examle using C# Client:

var response = client.Get(new QueryCustomers { 
    Fields = "DISTINCT Country"
})

We can use this feature with Northwinds existing AutoQuery Request DTOs:

[Route("/query/customers")]
public class QueryCustomers : QueryDb<Customer> { }

To return all unique City and Countries of Northwind Customers with:

Or to just return their unique Countries they're in:

OrmLite commandFilter

An optional Func<IDbCommand> commandFilter has been added to OrmLite's INSERT and UPDATE APIs to allow customization and inspection of the populated IDbCommand before it's run. This feature is utilized in the new Conflict Resolution Extension methods where you can specify the conflict resolution strategy when a Primary Key or Unique constraint violation occurs:

db.InsertAll(rows, dbCmd => dbCmd.OnConflictIgnore());

//Equivalent to: 
db.InsertAll(rows, dbCmd => dbCmd.OnConflict(ConflictResolution.Ignore));

In this case it will ignore any conflicts that occurs and continue inserting the remaining rows in SQLite, MySql and PostgreSQL, whilst in SQL Server it's a NOOP.

SQLite offers additional fine-grained behavior that can be specified for when a conflict occurs:

  • ROLLBACK
  • ABORT
  • FAIL
  • IGNORE
  • REPLACE

ServiceStack.Text

The XmlSerializer.XmlWriterSettings and XmlSerializer.XmlReaderSettings for controlling the default XML behavior is now publicly accessible with DTD Processing now disabled by default.

Support for leading zeros in integers was restored.

v5 Release Notes

The theme of this major v5 release is integration and unification with the newly released .NET Standard 2.0 which offers the broadest compatibility and stabilized API surface for .NET Core and the version we've chosen to standardize around.

To this end we've upgraded all existing .NET Standard builds to .NET Standard 2.0, all .NET Core Apps and Test projects to .NET Core 2.0 and merged our .NET Standard .Core NuGet packages into the main ServiceStack NuGet packages - unifying them into a single suite of NuGet packages and release cadence.

If you were waiting for .NET Core to stabilize before migrating or depending on it for new green field projects, we believe .NET Core 2.0 and .NET Standard 2.0 is the best time to jump in! It offers a leap in compatibiilty over its predecessors, simplified package versioning with a stable and simplified MSBuild project format that's well supported by most C# IDEs.

This release also unifies the .Signed NuGet package variants where now all .NET 4.5 builds are Strong Named by default using the servicestack.snk signing key that's in the /src folder of each Project. The .NET Standard builds continue to remain unsigned so they can be built on each platform with .NET Core's dotnet build command.

Consolidating all NuGet package variants into the main NuGet packages simplifies the development experience for Customers using .Signed packages who now have access to a first-class experience utilizing the Project Templates and IDE Integration available in ServiceStackVS's VS.NET Extension.

As this is a major version upgrade please review the v5 Changes and Migration Notes before upgrading. Whilst the user-facing source code impact is minimal, we've taken the opportunity of a major version window to perform some logical re-structuring.

One ServiceStack

An unfortunate side-effect of the multi-year development churn and multiple breaking versions being done in the open for what we now know as .NET Core is the trail of outdated blog posts and invalid documentation which has resulted in a lot of confusion and poor messaging about .NET Core 2.0's current polished and capable form and how it differentiates to the classic ASP .NET Framework.

With the amount of accumulated misinformation one could be forgiven for thinking they're 2 completely different fragmented worlds shrouded in a myriad of incompatible dependencies. Whilst that may be true of some libraries and frameworks it's not reflective of ServiceStack which maintains a single code base implementation, a single set of NuGet packages, single collection of documentation that's relevant for all supported ASP.NET, HttpListener and .NET Core hosts.

This is possible due to ServiceStack's high-level host agnostic API and our approach to decouple from concrete HTTP abstractions behind lightweight IRequest interfaces, which is what allows the same ServiceStack Services to be able to run on ASP.NET, HttpListener SelfHosts, SOAP Endpoints, multiple MQ Hosts and .NET Core Apps.

The primary advantage of this is simplicity, in both effort and cognitive overhead for creating Services that target multiple platforms, reuse of existing knowledge and investments in using ServiceStack libraries and features as well as significantly reduced migration efforts for porting existing .NET Framework code-bases to run on .NET Core where it enjoys near perfect source code compatibility.

ServiceStack's exceptional source compatibility is visible in our new .NET Core 2.0 and .NET Framework project templates where all templates utilize the same recommended Physical Project Structure, reference the same NuGet packages, share the same source code for its Server and Client App implementations as well as Client and Server Unit and Integration Tests.

The primary difference between the .NET Core and .NET Framework project templates is how ServiceStack's AppHost is initialized, in ASP.NET it's done in Global.asax whilst for .NET Core it's registered in .NET Core's pipeline as standard. The .csproj are also different with .NET Core using MSBuild's new and minimal human-friendly format and the ASP.NET Framework templates continuing to use VS.NET's classic project format for compatibility with older VS .NET versions.

New .NET Core 2.0 and .NET Framework Project Templates!

Now that ServiceStack v5 has merged into a single unified set of NuGet packages that's consolidated around .NET Standard 2.0, we've developed 11 new .NET Core 2.0 project templates for each of ServiceStack's most popular starting templates. Each .NET Core 2.0 template has an equivalent .NET Framework template except for ServiceStack's Sharp Apps which is itself a pre-built .NET Core 2.0 App that lets you develop Web Applications and HTTP APIs on-the-fly without any compilation.

All .NET Core 2.0 Templates can be developed using your preferred choice of either VS Code, VS.NET or JetBrains Project Rider on your preferred Desktop OS. Given the diverse ecosystem used to develop .NET Core Applications, the new Project Templates are being maintained on GitHub and made available via our new dotnet-new command-line utility, installable from npm with:

$ npm install -g @servicestack/cli

This makes the dotnet-new command globally available which can be run without arguments to view all templates available:

That can be used to create new projects with:

$ dotnet-new <template-name> <project-name>

Example of creating a new Vue SPA project called Acme:

$ dotnet-new vue-spa Acme

The resulting Acme.sln can be opened in VS 2017 which will automatically restore and install both the .NET and npm packages upon first load and build. This can take a while to install all client and server dependencies, once finished the wwwroot folder will be populated with your generated Webpack App contained within a /dist folder alongside a generated index.html page. After these are generated you can run your App with F5 to run your project as normal:

If using JetBrains Rider the npm packages can be installed by opening package.json and clicking on the "npm install" tooltip on the bottom right. In VS Code you'll need to run npm install manually from the command-line.

ServiceStackVS VS.NET Templates Updated

The VS.NET Templates inside ServiceStackVS have also been updated to use the latest .NET Framework templates which you can continue to use to create new projects within VS.NET. For all other IDEs and non-Windows Operating Systems you can use the cross-platform dotnet-new tooling to create new .NET Core 2.0 Projects.

In future we'll also be looking to make these templates available in .NET Core's dotnet new template system.

.NET Core 2.0 TypeScript Webpack Templates

There's a project template for each of the most popular Single Page Application JavaScript frameworks, including a new Angular 5.1 template built and managed using Angular's new angular-spa tooling. All other SPA Templates (inc. Angular 4) utilize a modernized Webpack build system, pre-configured with npm scripts to perform all necessary debug, production and live watched builds and testing. The included gulpfile.js provides a Gulp script around each npm script so they can be run without the command-line, by running them using VS.NET's built-in Task Runner Explorer GUI.

TypeScript

All Templates are configured to use TypeScript to provide both compile-time safety, productivity and maintainability as well as access to JavaScript's latest ES6/7 features without additional configuration. They're also configured to use Typed DTOs from ServiceStack's TypeScript Add Reference and generic @servicestack/client to provide an end-to-end Typed API to call your Services that can be synced with your Server DTOs by running the npm (or Gulp) dtos script.

All templates are configured to use a JsonServiceClient with concrete Type Definitions except for the Angular 5 template which uses Angular's built-in Rx-enabled HTTP Client with ServiceStack's ambient TypeScript declarations, as it's often preferable to utilize Angular's built-in dependencies when available.

Angular 5 HTTP Client

ServiceStack's ambient TypeScript interfaces are leveraged to enable a Typed API, whilst the createUrl(route,args) helper lets you reuse your APIs Route definitions (emitted in comments above each Request DTO) to provide a pleasant UX for making API calls using Angular's HTTP Client:

import { createUrl } from '@servicestack/client';
...

this.http.get<HelloResponse>(createUrl('/hello/{Name}', { name })).subscribe(r => {
    this.result = r.result;
});

All Single Page App Templates are available for both .NET Core 2.0 and ASP.NET Framework projects which can be live-previewed and used to create new projects using the template names below:

.NET Core 2.0 .NET Framework Single Page App Templates
angular-spa angular-spa-netfx

Angular 5 CLI Bootstrap Template

angular-lite-spa angular-lite-spa-netfx

Angular 4 Material Design Lite Template

react-spa react-spa-netfx

React 16 Webpack Bootstrap Template

vue-spa vue-spa-netfx

Vue 2.5 Webpack Bootstrap Template

Optimal Dev Workflow with Hot Reloading

The Webpack templates have been updated to utilize Webpack's DllPlugin which splits your App's TypeScript source code from its vendor dependencies for faster incremental build times. With the improved iteration times our recommendation for development is to run a normal Webpack watched build using the dev npm (or Gulp) script:

$ npm run dev

Which will watch and re-compile your App for any changes. These new templates also include a new hot-reload feature which works similar to #Script Pages hot-reloading where in DebugMode it will long poll the server to watch for any modified files in /wwwroot and automatically refresh the page. This provides a hot-reload alternative to npm run dev-server to run a Webpack Dev Server proxy on port http://localhost:3000

Configured with #Script

Hot Reloading works by leveraging #Script which works seamlessly with Webpack's generated index.html where it evaluates server Template Expressions when returning the SPA home page. This is leveraged to enable Hot Reloading support by including the expression:

{‎{ ifDebug | select: <script>{ '/js/hot-fileloader.js' | includeFile }</script> }‎}

Which renders the contents of /js/hot-fileloader.js when running the Web App during development.

Although optional, #Script is useful whenever you need to render any server logic in the SPA home page, e.g:

<div>Copyright &copy; {‎{ now | dateFormat('yyyy') }‎}</div>

Will be evaluated on the server and render the expected:

Copyright © 2017

Deployments

When your App is ready to deploy, run the publish npm (or Gulp) script to package your App for deployment:

npm run publish

Which generates a production Webpack client build and dotnet publish release Server build to package your App ready for an XCOPY, rsync or MSDeploy deployment. We used rsync and supervisord to deploy each packaged Web template to our Ubuntu Server at the following URL:

http://<template-name>.web-templates.io

/wwwroot WebRoot Path for .NET Framework Templates

To simplify migration efforts of ServiceStack projects between .NET Core and .NET Framework, all SPA and Website Templates are configured to use .NET Core's convention of /wwwroot for its public WebRoot Path. The 2 adjustments needed to support this was configuring ServiceStack to use the /wwwroot path in AppHost:

SetConfig(new HostConfig {
    WebHostPhysicalPath = MapProjectPath("~/wwwroot"),
});

Then instructing MSBuild to include all wwwroot\**\* files when publishing the project using MS WebDeploy which is contained in the Properties/PublishProfiles/PublishToIIS.pubxml of each project:

<PropertyGroup>
  <CopyAllFilesToSingleFolderForMSDeployDependsOn>
    IncludeFiles;
    $(CopyAllFilesToSingleFolderForMSDeployDependsOn);
  </CopyAllFilesToSingleFolderForMSDeployDependsOn>
</PropertyGroup>
<Target Name="IncludeFiles">
<ItemGroup>
  <PublishFiles Include="wwwroot\**\*" />
  <FilesForPackagingFromProject Include="@(PublishFiles)">
  <DestinationRelativePath>wwwroot\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
  </FilesForPackagingFromProject>
</ItemGroup>
</Target>

Website Templates

There are 3 templates for each of the different technologies that can be used with ServiceStack to develop Server HTML Generated Websites and HTTP APIs:

ASP.NET MVC

The mvc template differentiates the most between .NET Core and ASP.NET versions as ASP.NET Core MVC and ASP.NET MVC 5 are completely different implementations. With mvc ServiceStack is configured within the same .NET Core pipeline and shares the same request pipeline and "route namespace" but in ASP.NET MVC 5, ServiceStack is hosted at the /api Custom Path. Use MVC if you prefer to create different Controllers and View Models for your Website UI independently from your HTTP APIs or if you prefer to generate server HTML validation errors within MVC Controllers.

ServiceStack.Razor

The razor Template is configured to develop Websites using ServiceStack.Razor for developing server-generated Websites using Razor without MVC Controllers which lets you create Content Razor Pages that can be called directly or View Pages for generating HTML Views for existing Services. The source code for .NET Core and ASP.NET Framework projects are nearly identical despite being completely different implementations with the .NET Core version being retrofitted on top of .NET Core MVC Views. Use razor templates if you like Razor and prefer the API First Development model or plan on developing Websites for both .NET Core and ASP.NET and would like to be easily able to migrate between them.

#Script

The sharp Project Template is configured to develop Websites using #Script, a simpler and cleaner alternative to Razor that lets you utilize simple Template Expressions for evaluating Server logic in .html pages. Templates doesn't require any precompilation, is easier to learn and more intuitive for non-programmers that's more suitable for a number of use-cases. Use templates if you want an alternative to Razor syntax and the heavy machinery required to support it.

Hot Reloading

Both razor and templates project enjoy Hot Reloading where in development a long poll is used to detect and reload changes in the current Template Page or static files in /wwwroot.

.NET Core 2.0 .NET Framework Single Page App Templates
mvc mvc-netfx

MVC Bootstrap Template

mvc.web-templates.io

razor razor-netfx

ServiceStack.Razor Bootstrap Template

razor.web-templates.io

script script-netfx

#Script Pages Bootstrap Template

Empty Web and SelfHost Templates

Those who prefer starting from an Empty slate can use the web template to create the minimal configuration for a Web Application whilst the selfhost template can be used to develop Self-Hosting Console Apps. Both templates still follow our recommended physical project layout but are configured with the minimum number of dependencies, e.g. the selfhost Console App just has a dependency on Microsoft.AspNetCore.Server.Kestrel and ServiceStack, in contrast most templates have a dependency on the all-encompassing Microsoft.AspNetCore.All meta package.

.NET Core 2.0 .NET Framework Empty Project Templates
web web-netfx

Empty Web Template

web.web-templates.io

selfhost selfhost-netfx

Empty SelfHost Console App Template

selfhost.web-templates.io

.NET Core 2.0 ServiceStack WebApp Template

The only .NET Core 2.0 project template not to have a .NET Framework equivalent is bare-app as it's a pre-built .NET Core 2.0 App that dramatically simplifies .NET Wep App development by enabling Websites and APIs to be developed instantly without compilation.

.NET Core 2.0 Sharp Apps
bare-app

bare-app.web-templates.io

See sharpscript.net/docs/sharp-apps to learn the different use-cases made possible with Web Apps.

.NET Framework Templates

Likewise there are 2 .NET Framework Templates without .NET Core 2.0 equivalents as they contain Windows-only .NET Framework dependencies. This includes our React Desktop Template which supports packaging your Web App into 4 different ASP.NET, Winforms, OSX Cocoa and cross-platform Console App Hosts:

.NET Framework React Desktop Apps Template
react-desktop-apps-netfx

react-desktop-apps-netfx.web-templates.io

In future we also intend on developing a Desktop Apps template for .NET Core 2.0 based on Electron.

Windows Service Template

You can use winservice-netfx to create a Windows Service but as this requires Visual Studio it's faster to continue creating new Windows Service projects within VS.NET using the ServiceStack Windows Service Empty Project Template.

.NET Core Apps Upgraded

As we've stabilized on .NET Core 2.0 and .NET Standard 2.0 as our long-term supported .NET Core platform, we've upgraded all our existing .NET Core 1.x projects to .NET Core 2.0 and ServiceStack v5, including all .NET Core Live Demos.

Multi-stage Docker Builds

We've also updated the .NET Core Apps deployed using Docker to use the ASP.NET Team's recommended multi-stage Docker Builds where the App is built inside an aspnetcore-build Docker container with its published output copied inside a new aspnetcore runtime Docker container:

FROM microsoft/dotnet:2.1-sdk AS build-env
COPY src /app
WORKDIR /app

RUN dotnet restore --configfile NuGet.Config
RUN dotnet publish -c Release -o out

# Build runtime image
FROM microsoft/dotnet:2.1-aspnetcore-runtime
WORKDIR /app
COPY --from=build-env /app/Chat/out .
ENV ASPNETCORE_URLS http://*:5000
ENTRYPOINT ["dotnet", "Chat.dll"]

The smaller footprint required by the aspnetcore runtime reduced the footprint of .NET Core Chat from 567MB to 126MB whilst continuing to run flawlessly in AWS ECS at chat.netcore.io.

ServiceStack WebApps Updated

Likewise all .NET Core ServiceStack WebApps including the pre-built /web binaries have been updated to use ServiceStack v5.

Changes from the previous version includes switching to use the default WebHost.CreateDefaultBuilder() builder to bootstrap WebApp's letting you use ASPNETCORE_URLS to specify which URL and port to bind on, simplifying deployment configurations.

The ASPNETCORE_ENVIRONMENT Environment variable can also be used to configure WebApp's to run in Production mode. If preferred you can continue using the existing bind, port and debug options in your app.settings to override the default configuration.

.NET Core IAppSettings Adapter

Most .NET Core Templates are also configured to use the new NetCoreAppSettings adapter to utilize .NET Core's new IConfiguration config model in ServiceStack by initializing the AppHost with .NET Core's pre-configured IConfiguration that's injected into the Startup.cs constructor, e.g:

public class Startup
{
    public IConfiguration Configuration { get; }
    public Startup(IConfiguration configuration) => Configuration = configuration;

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseServiceStack(new AppHost {
            AppSettings = new NetCoreAppSettings(Configuration)
        });
    }
}

This lets you use appsettings.json and .NET Core's other Configuration Sources from ServiceStack's IAppSettings API where it continues to resolve both primitive values and complex Types, e.g:

bool debug = AppSettings.Get<bool>("DebugMode", false);
MyConfig myConfig = AppSettings.Get<MyConfig>();
List<string>  ghScopes = AppSettings.Get<List<string>>("oauth.github.Scopes");
IList<string> fbScopes = AppSettings.GetList("oauth.facebook.Permissions");

But instead of a single JSV string value, you'll need to use the appropriate JSON data type, e.g:

{
    "DebugMode": true,
    "MyConfig": {
        "Name": "Kurt",
        "Age": 27
    },
    "oauth.facebook.Permissions": ["email"],
    "oauth.github.Scopes": ["user"]
}

PBKDF2 Password Hashing implementation

ServiceStack now uses the same PBKDF2 password hashing algorithm ASP.NET Identity v3 uses to hash passwords by default for both new users and successful authentication logins where their password will automatically be re-hashed with the new implementation.

This also means if you wanted to switch, you'll be able to import ASP.NET Identity v3 User Accounts and their Password Hashes into ServiceStack.Auth's UserAuth tables and vice-versa.

Retain previous Password Hashing implementation

If preferred you can revert to using the existing SaltedHash implementation with:

SetConfig(new HostConfig { 
    UseSaltedHash = true
});

This also supports "downgrading" passwords that were hashed with the new IPasswordHasher provider where it will revert to using the older/weaker SaltedHash implementation on successful authentication.

Override Password Hashing Strength

The new PasswordHasher implementation can also be made to be computationally stronger or weaker by adjusting the iteration count (default 10000), e.g:

container.Register<IPasswordHasher>(new PasswordHasher(1000));

Versionable Password Hashing

The new IPasswordHasher interface includes support for versioning future Password Hashing algorithms and rehsashing:

public interface IPasswordHasher
{
    // First byte marker used to specify the format used. The default implementation uses format:
    // { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
    byte Version { get; }

    // Returns a boolean indicating whether the providedPassword matches the hashedPassword
    // The needsRehash out parameter indicates whether the password should be re-hashed.
    bool VerifyPassword(string hashedPassword, string providedPassword, out bool needsRehash);

    // Returns a hashed representation of the supplied password
    string HashPassword(string password);
}

Which is implemented in all ServiceStack Auth Repositories where it will rehash passwords that used a different version or weaker strength, by utilizing the new API for verifying passwords:

if (userAuth.VerifyPassword(password, out var needsRehash))
{
    this.RecordSuccessfulLogin(userAuth, needsRehash, password);
    return true;
}

If you're using a Custom Auth Repository it will also need to be updated to use the new password verification APIs, please refer to OrmLiteAuthRepository for a complete concrete example.

Fallback PasswordHashers

The list of Config.FallbackPasswordHashers can be used for migrating to a new Password Hashing algorithm by registering older Password Hashing implementations that were previously used to hash Users passwords. Failed password verifications will fallback to see if the password was hashed with any of the registered FallbackPasswordHashers, if any are valid the password attempt will succeed and the password re-hashed with the registered IPasswordHasher implementation.

Digest Auth Hashes only created when needed

Digest Auth Hashes are now only populated if the DigestAuthProvider is registered. If you ever intend to support Digest access authentication in future but don't want to register the DigestAuthProvider just yet, you can force ServiceStack to continue to maintain Digest Auth Hashes with:

new AuthFeature {
    CreateDigestAuthHashes = true
}

Users that don't have Digest Auth Hashes will require logging in again in order to have it populated. If you don't intend to use Digest Auth you can clear the DigestHa1Hash column in your UserAuth table which is otherwise unused.

JWT AuthProvider

Previously in order to be able to utilize RefreshToken's you would need to be also be using an Auth Repository as it's the source used to populate the JWT Token.

Now Users who are not using an IAuthRepository can instead implement the IUserSessionSource interface:

public interface IUserSessionSource
{
    IAuthSession GetUserSession(string userAuthId);
}

On either their Custom AuthProvider, or if preferred register it as a dependency in the IOC as an alternative source for populating Sessions in new JWT Tokens created using RefreshToken's. The implementation should only return a populated IAuthSession if the User is allowed to sign-in, e.g. if their account is locked or suspended it should throw an Exception:

throw HttpError.Forbidden("User is suspended");

Send JWTs in HTTP Params

The JWT Auth Provider can opt-in to accept JWT's via the Query String or HTML POST FormData with:

new JwtAuthProvider {
    AllowInQueryString = true,
    AllowInFormData = true
}

This is useful for situations where it's not possible to attach the JWT in the HTTP Request Headers or ss-tok Cookie.

For example if you wanted to authenticate via JWT to a real-time Server Events stream from a token retrieved from a remote auth server (i.e. so the JWT Cookie isn't already configured with the SSE server) you can call the /session-to-token API to convert the JWT Bearer Token into a JWT Cookie which will configure it with that domain so the subsequent HTTP Requests to the SSE event stream contains the JWT cookie and establishes an authenticated session:

var client = new JsonServiceClient(BaseUrl);
client.setBearerToken(JWT);
await client.post(new ConvertSessionToToken());

var sseClient = new ServerEventsClient(BaseUrl, ["*"], {
    handlers: {
        onConnect: e => { 
            console.log(e.isAuthenticated /*true*/, e.userId, e.displayName);
        }
    }
}).start();

Unfortunately this wont work in node.exe Server Apps (or in integration tests) which doesn't support a central location for configuring domain cookies. One solution that works everywhere is to add the JWT to the ?ss-tok query string that's used to connect to the /event-stream URL, e.g:

var sseClient = new ServerEventsClient(BaseUrl, ["*"], {
    resolveStreamUrl: url => appendQueryString(url, { "ss-tok": JWT }),
    handlers: {
        onConnect: e => { 
            console.log(e.isAuthenticated /*true*/, e.userId, e.displayName);
        }
    }
}).start();

JWT FormData POST

The stateless nature of JWTs makes it highly versatile to be able to use in a number of difference scenarios, e.g. it could be used to make stateless authenticated requests across different domains without JavaScript (HTTP Headers or Cookies), by embedding it in a HTML Form POST:

<form action="https://remote.org/secure" method="post">
    <input type="hidden" name="ss-tok" value="{JWT}" />
    ...
</form>

Although as this enables cross-domain posts it should be enabled with great care.

Runtime JWT Configuration

To allow for dynamic per request configuration as needed in Multi Tenant applications we've added a new IRuntimeAppSettings API which can be registered in your AppHost to return custom per request configuration.

E.g. this can be used to return a custom AuthKey that should be used to sign JWT Tokens for that request:

container.Register<IRuntimeAppSettings>(c => new RuntimeAppSettings { 
    Settings = {
        { nameof(JwtAuthProvider.AuthKey), req => (byte[]) GetAuthKey(GetTenantId(req)) }
    }
});

The following JwtAuthProvider properties can be overridden by IRuntimeAppSettings:

  • byte[] AuthKey
  • RSAParameters PrivateKey
  • RSAParameters PublicKey
  • List<byte[]> FallbackAuthKeys
  • List<RSAParameters> FallbackPublicKeys

Registration

The JWT BearerToken and RefreshToken properties added to RegisterResponse are now populated on Registrations configured to AutoLogin=true.

Previously users could update their info after they've registered using the same built-in /register Service used at registration. This feature is now disabled by default as it's not an expected capability for a Registration Service, if needed it can be re-enabled with:

ServiceStack.Auth.RegisterService.AllowUpdates = true;

Routes with Custom Rules

The new Matches property on [Route] and [FallbackRoute] attributes lets you specify an additional custom Rule that requests need to match. This feature is used in all SPA projects to specify that the [FallbackRoute] should only return the SPA index.html for unmatched requests which explicitly requests HTML, i.e:

[FallbackRoute("/{PathInfo*}", Matches="AcceptsHtml")]
public class FallbackForClientRoutes
{
    public string PathInfo { get; set; }
}

This works by matching the AcceptsHtml built-in RequestRules below where the Route will only match the Request if it includes the explicit text/html MimeType in the HTTP Request Accept Header. The AcceptsHtml rule prevents the home page from being returned for missing resource requests like favicon which returns a 404 instead.

The implementation of all built-in Request Rules:

SetConfig(new HostConfig {
  RequestRules = {
    {"AcceptsHtml", req => req.Accept?.IndexOf(MimeTypes.Html, StringComparison.Ordinal) >= 0 },
    {"AcceptsJson", req => req.Accept?.IndexOf(MimeTypes.Json, StringComparison.Ordinal) >= 0 },
    {"AcceptsXml", req => req.Accept?.IndexOf(MimeTypes.Xml, StringComparison.Ordinal) >= 0 },
    {"AcceptsJsv", req => req.Accept?.IndexOf(MimeTypes.Jsv, StringComparison.Ordinal) >= 0 },
    {"AcceptsCsv", req => req.Accept?.IndexOf(MimeTypes.Csv, StringComparison.Ordinal) >= 0 },
    {"IsAuthenticated", req => req.IsAuthenticated() },
    {"IsMobile", req => Instance.IsMobileRegex.IsMatch(req.UserAgent) },
    {"{int}/**", req => int.TryParse(req.PathInfo.Substring(1).LeftPart('/'), out _) },
    {"path/{int}/**", req => {
        var afterFirst = req.PathInfo.Substring(1).RightPart('/');
        return !string.IsNullOrEmpty(afterFirst) && int.TryParse(afterFirst.LeftPart('/'), out _);
    }‎},
    {"**/{int}", req => int.TryParse(req.PathInfo.LastRightPart('/'), out _) },
    {"**/{int}/path", req => {
        var beforeLast = req.PathInfo.LastLeftPart('/');
        return beforeLast != null && int.TryParse(beforeLast.LastRightPart('/'), out _);
    }‎},
 }
})

Routes that contain a Matches rule have a higher precedence then Routes without. We can use this to define multiple idential matching routes to call different Service depending on whether the Path Segment is an integer or not, e.g:

// matches /users/1
[Route("/users/{Id}", Matches = "**/{int}")]
public class GetUser
{
    public int Id { get; set; }
}

// matches /users/username
[Route("/users/{Slug}")]
public class GetUserBySlug
{
    public string Slug { get; set; }
}

Other examples utilizing {int} Request Rules:

// matches /1/profile
[Route("/{UserId}/profile", Matches = @"{int}/**")]
public class GetProfile { ... }

// matches /username/profile
[Route("/{Slug}/profile")]
public class GetProfileBySlug { ... }

// matches /users/1/profile/avatar
[Route("/users/{UserId}/profile/avatar", Matches = @"path/{int}/**")]
public class GetProfileAvatar { ... }

// matches /users/username/profile/avatar
[Route("/users/{Slug}/profile/avatar")]
public class GetProfileAvatarBySlug { ... }

Another popular use-case is to call different services depending on whether a Request is from an Authenticated User or not:

[Route("/feed", Matches = "IsAuthenticated")]
public class ViewCustomizedUserFeed { ... }

[Route("/feed")]
public class ViewPublicFeed { ... }

This can also be used to call different Services depending if the Request is from a Mobile browser or not:

[Route("/search", Matches = "IsMobile")]
public class MobileSearch { ... }

[Route("/search")]
public class DesktopSearch { ... }

Instead of matching on a pre-configured RequestRule you can instead specify a Regular Expression using the format:

{Property} =~ {RegEx}

Where {Property} is an IHttpRequest property, e.g:

[Route("/users/{Id}", Matches = @"PathInfo =~ \/[0-9]+$")]
public class GetUser { ... }

An exact match takes the format:

{Property} = {Value}

Which you could use to provide a tailored feed for specific clients:

[Route("/feed", Matches = @"UserAgent = specific-client")]
public class CustomFeedView { ... }

#Script Pages

#Script Pages gains support for the last missing feature from ServiceStack.Razor with its new View Pages support which lets you use .html Template Pages to render the HTML for Services Responses.

It works similarly to Razor ViewPages where it uses first matching View Page with the Response DTO is injected as the Model property. The View Pages can be in any folder within the /Views folder using the format {PageName}.html where PageName can be either the Request DTO or Response DTO Name, but all page names within the /Views folder need to be unique.

Just like ServiceStack.Razor you can also specify to use different Views or Layouts by returning a custom HttpResult, e.g:

public object Any(MyRequest request)
{
    ...
    return new HttpResult(response)
    {
        View = "CustomPage",
        Template = "_custom-layout",
    };
}

Or add the [ClientCanSwapTemplates] Request Filter attribute to allow clients to specify which View and Template to use via the query string, e.g: ?View=CustomPage&Template=_custom-layout.

Additional examples of dynamically specifying the View and Template are available in TemplateViewPagesTests.

Cascading Layouts

One difference from Razor is that it uses a cascading _layout.html instead of /Views/Shared/_Layout.cshtml.

So if your view page was in:

/Views/dir/MyRequest.html

It will use the closest _layout.html it can find starting from:

/Views/dir/_layout.html
/Views/_layout.html
/_layout.html

Logging with Context

Rolf Kristensen added support for contextual logging with the new ILogWithContext interface and PushProperty extension method which lets you attach additional data to log messages, e.g:

using (log.PushProperty("Hello", "World"))
{
    log.InfoFormat("Message");
}

Support for the additional context was added to Log4net, NLog and Serilog logging providers.

The new ILogWithException interface and extension methods provides additional overloads for logging Exceptions with separate message format and args.

V5 Changes and Migration Notes

Most ServiceStack releases are in-place upgrades containing new features and enhancements with very minimal disruption to existing code-bases. Our major versions are very few and far between which we use to implement any structural changes that were held off due to requiring external project or server configuration changes, e.g. the last v4.5.0 major version was for upgrading all packages from .NET v4.0 to .NET v4.5.

The theme for ServiceStack v5 was to restructure the code-base to lay the groundwork for parallel first-class support for .NET Core. Whilst some .NET Framework only implementations have moved to different projects there should still be minimal user-facing source code changes other than most deprecated APIs and dead-code having been removed, as such the primary task before upgrading to V5 will be to move off deprecated APIs. Most [Obsolete] APIs specify which APIs to move to in their deprecated messages which you can find in your build warning messages.

Other major structural changes include:

  • Upgraded all NuGet packages to a 5.0.0 major version
  • Removed all PCL builds and upgraded all client libraries and .NET Standard builds to target .NET Standard 2.0
  • Merged .Core packages into main ServiceStack packages which now contain net45 and netstandard2.0 builds
  • Strong-named all .NET v4.5 builds which also only have Strong named dependencies
  • Upgrade ServiceStack.RabbitMQ to use the latest RabbitMQ.Client v5.0.1 NuGet dependency which requires a .NET v4.5.1 minimum
  • Upgrade ServiceStack.Azure to use the latest NuGet dependencies which requires a .NET v4.5.2 minimum
  • Upgraded to the latest Fluent Validation 7.2

Migration notes:

  • Move off deprecated APIs before upgrading to v5
  • If you're using an outdated NuGet v2 client you'll need to upgrade to NuGet v3
  • If you were using .Core or .Signed NuGet packages you'll need to strip the suffixes to use the main NuGet packages

If using the new MSBuild project format, using a 5.* wildcard version enables upgrading to the latest release of ServiceStack with a NuGet restore.

Strong Naming Notes

All .NET 4.5 builds are now strong named with the servicestack.snk key that's in the /src of each repo. ServiceStack.Logging.Elmah is the only .NET 4.5 dll left unsigned as it's elmah.corelibrary dependency is unsigned. .NET Standard builds remain unsigned as it would've impeded building ServiceStack libraries on non Windows platforms which require msbuild.

PCL Client Library Notes

The PCL client builds have been rewritten to use .NET Standard 2.0 APIs. We've added new test suites for Xamarin.IOS and Xamarin.Android to ensure the new clients run smoothly. We've also included UWP in our .NET Standard 2.0 client test suites but this does require upgrading to Windows 10 Fall Creators Update.

There's no longer any Silverlight or Windows8 App builds which end at v4.5.14 on NuGet. v5 remains wire-compatible with existing versions so if you're still maintaining legacy Silverlight or Windows8 Apps your v4.5.14 clients will be able to consume v5 Services as before.

The new .NET Standard 2.0 clients are much nicer to use then the previous PCL clients which needed to utilize the PCL Bait and Switch trick to overcome limitations in PCL's limited API Surface by delegating to platform specific implementations - required for each supported platform. The PCL limitations also meant you couldn't reference ServiceStack.Text package on its own as it needed the platform specific implementations in ServiceStack.Client which you would need to manually wire-up in iOS by hard-coding IosPclExportClient.Configure();.

By contrast .NET Standard 2.0 builds can be treated like regular .dlls. The existing PCL hacks, implementation-specific builds, polyfills and wrappers have all been removed. API and Code-reuse is also much broader as previously only the PCL compatible surface area could be shared amongst multiple projects, now the entire API surface is available.

.NET Standard Client packages for .NET Framework Projects

One difference from before was previously ServiceStack.Interfaces.dll was a single PCL .dll referenced by all .NET v4.5, .NET Core and PCL projects. It now contains .NET 4.5 and .NET Standard 2.0 build like all other packages. One consequence of this is that you wont be able to have binary coupling of your Server .NET Core ServiceModel.dll inside .NET Framework clients despite both referencing the same ServiceStack.Client NuGet package. For this reason we're continuing to publish .Core versions of the client packages:

  • ServiceStack.Text.Core
  • ServiceStack.Interfaces.Core
  • ServiceStack.Client.Core
  • ServiceStack.HttpClient.Core

These contain the exact same .NET Standard 2.0 builds included in the main ServiceStack.Client packages but by having them in separate packages, you can force .NET v4.6.1+ Framework projects to use .NET Standard versions which will let you share your server .NET Standard 2.0 ServiceModel.dll in .NET v4.6.1+ clients. Alternatively you can avoid any binary coupling by using Add ServiceStack Reference to import the source code of your Typed DTOs.

This doesn't work the other way around where ServiceStack .NET Framework projects still cannot reference .NET Standard only projects. In order to create projects that can be shared from both .NET Core and ASP.NET Framework projects they'll need to target both frameworks, e.g:

<PropertyGroup>
    <TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
</PropertyGroup>

.NET Framework specific features

A goal for the v5 restructure was to restrict the primary ServiceStack.dll and NuGet Package to contain core functionality that's applicable to all ASP.NET, HttpListener and .NET Core Hosts. To this end we've moved ASP.NET and HttpListener specific features to different packages, but they remain source-compatible and continue to use the same Type names and namespaces.

In addition both .NET Framework and .NET Core ServiceStack AppHost's are initialized with the same minimal feature set. The .NET Framework SOAP Support, Mini Profiler and Markdown Razor format features are now opt-in, if needed they'll need to be explicitly added with:

Plugins.Add(new SoapFormat());
Plugins.Add(new MarkdownFormat());       // In ServiceStack.Razor
Plugins.Add(new MiniProfilerFeature());  // In ServiceStack.NetFramework

The MiniProfilerFeature plugin needs to be registered before ProfiledDbConnection if it's used on Startup, e.g:

Plugins.Add(new MiniProfilerFeature());

container.Register<IDbConnectionFactory>(
    c => new OrmLiteConnectionFactory("~/db.sqlite".MapHostAbsolutePath(), SqliteDialect.Provider){
        ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
    });

using (var db = container.Resolve<IDbConnectionFactory>().Open()) 
{
    //...
}

ServiceStack.Razor

The MVC HTML Helpers for ASP.NET ServiceStack.Razor has been moved to the ServiceStack.Razor project. This should be a transparent change as it needed ServiceStack.Razor to make use of them.

Markdown Razor and its MarkdownFormat was also moved to ServiceStack.Razor as they were built on older Code DOM technology which isn't available on .NET Core and saw its feature-set stripped down to bare Markdown functionality.

#Script Pages

If you were using Markdown Razor we recommend using the superior, cleaner, faster and more flexible alternative in #Script Pages which easily supports Markdown Razor's most redeeming feature of being able to mix .NET in Markdown by utilizing its Transformers feature.

Markdown Config

The new MarkdownConfig API lets you specify which Markdown implementation to use in Razor and ServiceStack Template Markdown partials. By default it uses the built-in fast and lightweight MarkdownDeep implementation:

MarkdownConfig.Transformer = new MarkdownDeep.Markdown();

Providing a static API that can be used to transform Markdown to HTML with:

var markdown = MarkdownConfig.Transform(html);

Alternative Markdown implementations can be used by providing an adapter for the IMarkdownTransformer interface:

public interface IMarkdownTransformer
{
    string Transform(string markdown);
}

ServiceStack.NetFramework

The remaining .NET Framework and HttpListener specific features that were previously in ServiceStack.dll have moved to ServiceStack.NetFramework - a new project and NuGet package for maintaining .NET Framework only features.

PM> Install-Package ServiceStack.NetFramework

It currently contains MiniProfiler and SmartThreadPool which is an internal ThreadPool originally imported to provide a faster alternative to .NET's built-in ThreadPool. The fastest self-hosting option is now to use a .NET Core App instead of .NET's HttpListener which also benefits from running flawlessly cross-platform so it's our recommended option for new projects when you don't have requirements that need the .NET Framework.

The primary result of this is that now AppSelfHostBase is implemented on top of AppHostHttpListenerPoolBase. To have your HttpListener AppHost go back to utilizing a SmartThreadPool implementation, change your AppHost to inherit either SmartThreadPool.AppSelfHostBase or AppHostHttpListenerSmartPoolBase, e.g:

public class AppHost : SmartThreadPool.AppSelfHostBase {}
public class AppHost : AppHostHttpListenerSmartPoolBase {}

Validation

Our internal implementation of FluentValidation has been upgraded to the latest 7.2 version which will let you take advantage of new features like implementing Custom Validators, e.g:

public class CustomValidationValidator : AbstractValidator<CustomValidation>
{
    public CustomValidationValidator()
    {
        RuleFor(request => request.Code).NotEmpty();
        RuleFor(request => request)
            .Custom((request, context) => {
                if (request.Code?.StartsWith("X-") != true)
                {
                    var propName = context.ParentContext.PropertyChain.BuildPropertyName("Code");
                    context.AddFailure(new ValidationFailure(propName, error:"Incorrect prefix") {
                        ErrorCode = "NotFound"
                    });
                }
            });
    }
}

Validators in ServiceAssemblies auto-wired by default

The ValidationFeature plugin now scans and auto-wires all validators in the AppHost.ServiceAssemblies that's injected in the AppHost constructor so you'll no longer need to manually register validators maintained in your ServiceInterface.dll project:

//container.RegisterValidators(typeof(UserValidator).Assembly);

This default behavior can be disabled with:

Plugins.Add(new ValidationFeature {
    ScanAppHostAssemblies = false
});

Expanded Async Support

To pre-emptively support .NET Core when they disable Sync Response writes by default in a future version, we've rewritten our internal implementations to write to Responses asynchronously.

New Async filters are now available to match existing sync filters. It's very unlikely the classic ASP.NET Framework will ever disable sync writes, but if you're on .NET Core you may want to consider switching to use the newer async API equivalents on IAppHost below:

GlobalRequestFiltersAsync
GlobalResponseFiltersAsync
GatewayRequestFiltersAsync
GatewayResponseFiltersAsync
GlobalMessageRequestFiltersAsync
GlobalMessageResponseFiltersAsync

Async Attribute Filters

Async Filter Attributes are available by inheriting the new RequestFilterAsyncAttribute or ResponseFilterAsyncAttribute base classes if you need to call async APIs within Filter Attributes.

All async equivalents follow the same Order of Operations and are executed immediately after any registered sync filters with the same priority.

Async Request and Response Converters

As they're not commonly used, the RequestConverters and ResponseConverters were just converted to utilize an Async API.

Async ContentTypes Formats

There's also new async registration APIs for Content-Type Formats which perform Async I/O, most serialization formats don't except for HTML View Engines which can perform Async I/O when rendering views, so they were changed to use the new RegisterAsync APIs:

appHost.ContentTypes.RegisterAsync(MimeTypes.Html, SerializeToStreamAsync, null);
appHost.ContentTypes.RegisterAsync(MimeTypes.JsonReport, SerializeToStreamAsync, null);
appHost.ContentTypes.RegisterAsync(MimeTypes.MarkdownText, SerializeToStreamAsync, null);

Async HttpWebRequest Service Clients

The Async implementation of the HttpWebRequest based Service Clients was rewritten to use the newer .NET 4.5 Async APIs as the older APM APIs were found to have some async request hanging issues in the .NET Standard 2.0 version of Xamarin.iOS.

.NET Reflection APIs

One of the primary areas where .NET Standard and .NET Framework source code differed was in Reflection APIs. To work around this the most popular Reflection APIs were abstracted behind wrapper extension methods. These wrappers have now all been deprecated as they're no longer needed now that .NET Standard 2.0 added back support of .NET's standard Reflection APIs.

Minor Features

  • ToOptimizedResult() now supports HttpResult responses
  • Config.DebugMode is being initialized with env.IsDevelopment() in .NET Core
  • MapProjectPath() uses env.ContentRoot in .NET Core
  • MetadataDebugTemplate no longer has dependencies on jquip and ss-utils.js
  • IMeta, IHasSessionId and IHasVersion interfaces are now exported in Add ServiceStack Reference
  • Html.IncludeFile() API for embedding file contents in Razor views
  • VirtualFiles and VirtualFileSources properties added to base Razor View

@servicestack npm packages

ServiceStack's npm packages are now being maintained in npm organization scoped @servicestack packages, if you were using the previous packages you should uninstall them and use the new scoped packages instead:

$ npm uninstall servicestack-client
$ npm install @servicestack/client

$ npm uninstall -g servicestack-cli
$ npm install -g @servicestack/cli

You'll also need to update your source code references to use the new packages:

import { JsonServiceClient } from "@servicestack/client";

ServiceStack.OrmLite

Support for MySqlConnector ADO.NET Provider

@Naragato from the ServiceStack Community contributed a MySqlConnector for OrmLite providing a true async ADO.NET Provider option for MySql, from their website:

This is a clean-room reimplementation of the MySQL Protocol and is not based on the official connector. It's fully async, supporting the async ADO.NET methods added in .NET 4.5 without blocking (or using Task.Run to run synchronous methods on a background thread). It's also 100% compatible with .NET Core.

To use it, install:

PM> Install-Package ServiceStack.Ormlite.MySqlConnector

Then initialize your DB Factory with:

var dbFactory = new OrmLiteConnectionFactory(connectionString, MySqlConnectorDialect.Provider);

using (var db = dbFactory.Open()) 
{
    ...
}

Parametrized IN Values

You can now provide a collection of values and OrmLite will automatically modify the SQL statement and split the values into multiple DB parameters to simplify executing parameterized SQL with multiple IN Values, e.g:

var ids = new[]{ 1, 2, 3};
var results = db.Select<Table>("Id in (@ids)", new { ids });

var names = new List<string>{ "foo", "bar", "qux" };
var results = db.SqlList<Table>("SELECT * FROM Table WHERE Name IN (@names)", new { names });

RowVersion Byte Array

To improve reuse of OrmLite's Data Models in Dapper, @daleholborow added support for allowing byte[] RowVersion as an alternative to OrmLite's ulong RowVersion which lets you use OrmLite Data Models with byte[] RowVersion properties in Dapper queries.

OnOpenConnection Filter

The OnOpenConnection filter lets you run custom commands after opening a new DB Connection. This feature can be used to easily enable Write-Ahead Logging in SQLite:

var dbFactory = new OrmLiteConnectionFactory("sqlite.db", SqliteDialect.Provider);
SqliteDialect.Provider.OnOpenConnection = db => db.ExecuteSql("PRAGMA journal_mode=WAL;");

using (var db = dbFactory.Open()) 
{
    ...
}

OpenAsync APIs

The new OpenDbConnectionAsync() and OpenAsync() alias APIs can be used to Open DB connections asynchronously.

ServiceStack.Aws

PocoDynamo was updated to utilize AWS's recommended Exponential Backoff And Jitter algorithm.

This is also available to be used independently with:

Thread.Sleep(ExecUtils.CalculateFullJitterBackOffDelay(retriesAttempted));

await Task.Delay(ExecUtils.CalculateFullJitterBackOffDelay(retriesAttempted));

v4.5.14 Release Notes