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
andServiceStack.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 Hostsnetstandard2.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 © {‎{ 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 |
razor | razor-netfx |
ServiceStack.Razor Bootstrap Template |
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 |
selfhost | selfhost-netfx |
Empty SelfHost Console App Template |
.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 |
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 |
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[]
AuthKeyRSAParameters
PrivateKeyRSAParameters
PublicKeyList<byte[]>
FallbackAuthKeysList<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 containnet45
andnetstandard2.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 supportsHttpResult
responsesConfig.DebugMode
is being initialized withenv.IsDevelopment()
in .NET CoreMapProjectPath()
usesenv.ContentRoot
in .NET CoreMetadataDebugTemplate
no longer has dependencies onjquip
andss-utils.js
IMeta
,IHasSessionId
andIHasVersion
interfaces are now exported in Add ServiceStack ReferenceHtml.IncludeFile()
API for embedding file contents in Razor viewsVirtualFiles
andVirtualFileSources
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));