We’ve got a another feature-packed release with a number of exciting new features new support for the Open API specification enabling new integration possibilities with Azure API Management and Azure AutoRest, our real-time Server Events solution extending to new Mobile, Desktop and Server platforms, more flexible Authentication options to dramatically simplify integration with Mobile, Desktop and Web clients, effortless JWT Refresh Tokens, enhanced support for TypeScript and Java client libraries, new compression options, upgrades to the latest Fluent Validation with support for Async validators, Async Request Filters, a number of new quality OSS ServiceStack projects developed by the community and lots more use-case driven features and refinements across the entire ServiceStack suite!
Please see the v4.5.8 Release Notes for the full details, for a quick summary we’ll highlight the major features below:
Open API
We’ve added support for the Open API Specification which opens up several new integration possibilities including importing your Services into Azure API Management and generating native clients with Azure’s AutoRest. The Open API plugin also embeds Swagger UI letting you quickly explore and interact with your services.
Server Events
We’ve made great strides in this release towards our goal of expanding the Server Event ecosystem in popular Mobile, Desktop and Server platforms with new first-class implementations for Android, Java and TypeScript which now includes:
- C# Server Events Client
- Xamarin.iOS
- Xamarin.Android
- UWP
- .NET Framework 4.5+
- .NET Core (.NET Standard 1.3+)
- TypeScript Server Events Client
- Web
- Node.js Server
- React Native
- iOS
- Android
- Java Server Events Client
- Android
- JVM 1.7+ (Java, Kotlin, Scala, etc)
- Java Clients
- Java Servers
- JavaScript (jQuery plugin)
- Web
Android Java Chat
To showcase real-world usage of AndroidServerEventsClient
in an Android App we’ve ported C# Xamarin Android Chat into Java 8 using Google’s recommended Android Studio Development Environment.
In addition to retaining the same functionality as the original C# Xamarin.Android Chat App, it also leverages the native Facebook, Twitter and Google SDK’s to enable seamless and persistent authentication when signing in with Facebook, Twitter or Google User accounts.
Web, Node.js and React Native ServerEvents Apps
The TypeScript servicestack-client npm package is a cross-platform library enabling a rich, productive end-to-end Typed development experience on Web, node.js Server projects, node.js test suites, React Native iOS and Android Mobile Apps - written in either TypeScript or plain JavaScript.
To help getting started using servicestack-client in each of JavaScript’s most popular platforms we’ve developed a simple Server Events Web, Node.js and React Native Mobile iOS App that can connect to any Server Events Server and listen to messages published on the subscribed channel. The App also maintains a live synchronized list of Users in the channel that’s automatically updated whenever Users join or leave the channel:
Each App uses the optimal starting configuration that enables a productive workflow on each platform and uses the minimum runtime dependencies - essentially just servicestack-client and its es6-shim and W3C EventSource
polyfills on Node.js and React Native where it’s missing a native implementation.
TypeScript servicestack-client improvements
New APIs have been added to TypeScript’s servicestack-client to catch up with the additional flexibility and features available in C#/.NET Service Clients:
Sending additional arguments with Typed API Requests
Many AutoQuery Services utilize implicit conventions to query fields that aren’t explicitly defined on AutoQuery Request DTOs, these can now be queried by specifying additional arguments with the typed Request DTO, e.g:
const request = new FindTechStacks();
client.get(request, { VendorName: "ServiceStack" })
.then(r => { }) // typed to QueryResponse<TechnologyStack>
Calling APIs with Custom URLs
In addition to Typed API Requests you can now also call Services using relative or absolute urls, e.g:
client.get<GetTechnologyResponse>("/technology/ServiceStack")
client.get<GetTechnologyResponse>("http://techstacks.io/technology/ServiceStack")
// GET http://techstacks.io/technology?Slug=ServiceStack
client.get<GetTechnologyResponse>("/technology", { Slug: "ServiceStack" })
Relaxed TypeScript Definitions
The metadata requirements in TypeScript Ambient Interface definitions generated by Add ServiceStack Reference’s /types/typescript.d
have been relaxed so they can be used to Type Object Literals:
let request:QueryContracts = { accountId: 1234 };
But as metadata in Request DTO interface definitions are no longer inferrable, their return Type and route will need to be supplied on each call-site, e.g:
let request:FindTechnologies = { vendorName: "ServiceStack" };
client.get<QueryResponse<Technology>>("/technology/search", request)
.then(r => { }); //typed response
Authentication via OAuth AccessTokens
To improve OAuth Sign In integration from native Mobile or Desktop Apps we’ve added support for direct Authentication via AccessTokens which can dramatically simplify the Development and User Experience by being able to leverage the Native Facebook, Twitter and Google Client SDK’s to Sign In users locally then reuse their local AccessToken to Authenticate with back-end ServiceStack Servers.
This feature is what’s used to enable Integrated Facebook, Twitter and Google Logins in Android Java Chat and be able to Automatically Sign In users with saved AccessTokens.
This capability is now available on the popular OAuth Providers below:
FacebookAuthProvider
- Sign in with FacebookTwitterAuthProvider
- Sign in with TwitterGithubAuthProvider
- Sign in with GithubGoogleOAuth2Provider
- Sign in with Google
Client Authentication with AccessToken
Clients can utilize this feature with the new AccessToken
and AccessTokenSecret
properties on the existing Authenticate
request DTO, sent with the provider that the AccessToken is for, e.g:
var response = client.Post(new Authenticate {
provider = "facebook",
AccessToken = facebookAccessToken,
RememberMe = true,
});
JWT Refresh Tokens
Support for Refresh Tokens is now available in the JWT Auth Provider where new JWT Tokens can now be generated from a longer-lived RefreshToken
which can be used to fetch new JWT Tokens without needing to re-authenticate with a separate Auth Provider.
Accessing Refresh Tokens
Just like JWT Tokens, Refresh Tokens are populated on the AuthenticateResponse
DTO after successfully authenticating via any registered Auth Provider, e.g:
var response = client.Post(new Authenticate {
provider = "credentials",
UserName = userName,
Password = password,
});
var jwtToken = response.BearerToken;
var refreshToken = response.RefreshToken;
Lifetimes of tokens
The default expiry time of JWT and Refresh Tokens below can be overridden when registering the JwtAuthProvider
:
new JwtAuthProvider {
ExpireTokensIn = TimeSpan.FromDays(14), // JWT Token Expiry
ExpireRefreshTokensIn = TimeSpan.FromDays(365), // Refresh Token Expiry
}
Using JWT and Refresh Tokens
In order to provide the simplest development experience possible you only need to specify the Refresh Token on the Service Client and it will take care of transparently fetching a new JWT Token behind-the-scenes. You don’t even need to configure the client with a JWT Token as it will just fetch a new one on first use, e.g:
var client = new JsonServiceClient(baseUrl) {
RefreshToken = refreshToken,
};
var response = client.Send(new Secured());
Fluent Validation Upgraded
Another major feature added in this release was contributed by the community with Scott Mackay upgrading our internal Fluent Validation implementation to the latest version of FluentValidation. Special care was taken to maintain backwards compatibility where ServiceStack enhancements were retrofitted on top of the new version and existing Error Codes were preserved to ensure minimal disruption with existing code bases.
Async Validators
One of the benefits from using the latest version of Fluent Validation is we now have support for Async Validators! Async validators can be registered using the MustAsync
validator where you could simulate the following built-in Not Empty validation:
public class MyRequestValidator : AbstractValidator<MyRequest>
{
public MyRequestValidator()
{
RuleFor(x => x.Name).NotEmpty();
}
}
And replace it with an Async version that uses the Service Gateway to call a custom Async GetStringLength
Service that returns the same ErrorCode
and Error Message as the Not Empty validator:
public class MyRequestValidator : AbstractValidator<MyRequest>
{
public MyRequestValidator()
{
RuleFor(x => x.Name).MustAsync(async (s, token) =>
(await Gateway.SendAsync(new GetStringLength { Value = s })).Result > 0)
.WithMessage("'Name' should not be empty.")
.WithErrorCode("NotEmpty");
}
}
Async Global Request Filters
To properly implement Async Validators we also needed Async Request Filters which were also added in this release where you can now register non-blocking Request Filters with:
GlobalRequestFiltersAsync.Add(async (req,res,dto) => {
var response = await client.Send(new CheckRateLimit {
Service = dto.GetType().Name,
IpAddress = req.UserHostAddress,
});
if (response.RateLimitExceeded)
{
res.StatusCode = 403;
res.StatusDescription = "RateLimitExceeded";
res.EndRequest();
}
})
Enhanced Compression Options
We’ve followed up Request Compression added in the last release with more compression features in this release including:
[CompressResponse]
Attribute
You can now selectively choose which Services should be compressed with the new [CompressResponse]
attribute to compress responses for clients which support compression, which can be applied to most Response Types, e.g:
[CompressResponse]
public class CompressedServices : Service
{
public object Any(CompressDto request) => new CompressExamplesResponse();
public object Any(CompressString request) => "foo";
public object Any(CompressBytes request) => "foo".ToUtf8Bytes();
public object Any(CompressStream request) => new MemoryStream("foo".ToUtf8Bytes());
public object Any(CompressFile request) => new HttpResult(VirtualFileSources.GetFile("/foo"));
public object Any(CompressAnyHttpResult request)
{
return new HttpResult(new CompressExamplesResponse()); // DTO
return new HttpResult("foo", "text/plain"); // string
return new HttpResult("foo".ToUtf8Bytes(), "text/plain"); // bytes
//etc
}
}
Note using
[CompressResponse]
is unnecessary when returning cached responses as ServiceStack automatically caches and returns the most optimal Response - typically compressed bytes for clients that supports compression
Static File Compression
ServiceStack can also be configured to compress static files with specific file extensions that are larger than specific size with the new opt-in Config options below:
SetConfig(new HostConfig {
CompressFilesWithExtensions = { "js", "css" },
// (optional), only compress .js or .css files > 10k
CompressFilesLargerThanBytes = 10 * 1024
});
When more fine-grained logic is needed you can override ShouldCompressFile()
in your AppHost to choose which static files you want to compress on a per-file basis, e.g:
public override bool ShouldCompressFile(IVirtualFile file)
{
return base.ShouldCompressFile(file) || file.Name == "large.csv";
}
Community
We’ve been lucky to receive a number of quality contributions from the Community in this release starting with the new Serilog Logging provider contributed by Josh Engler:
ServiceStack.Logging.Serilog
To Configure Serilog Logging, first download ServiceStack.Logging.Serilog from NuGet:
PM> Install-Package ServiceStack.Logging.Serilog
Then configure ServiceStack to use SerilogFactory
:
LogManager.LogFactory = new SerilogFactory();
Community Projects
There’s also been a number of quality OSS Community projects that’s surfaced recently that you may find useful to enable in your ServiceStack projects:
ServiceStack.Webhooks
If you need to implement a Webhooks solution for your Services you’ll definitely want to check out ServiceStack.Webhooks by @jezzsantos. ServiceStack.Webhooks is a high-quality, actively developed, well documented solution for raising and managing application-level “events” raised by your services:
ServiceStack.Authentication.Azure
ServiceStack.Authentication.Azure is a fork of ServiceStack.Authentication.Aad developed by Ian Kulmatycki which provides an AzureAuthenticationProvider
for easily authenticating users with Office365 and hybrid Azure Active Directories.
ServiceStack.Authentication.Marten
ServiceStack.Authentication.Marten is a UserAuthRepository
repository developed by Michał Gajek for persisting users in Marten - a Document Database for PostgreSQL.
MultiAppSettingsBuilder
Another feature contributed by Josh Engler is the MultiAppSettingsBuilder
which adds a fluent discoverable API for configuring ServiceStack’s various App Setting Providers, e.g:
AppSettings = new MultiAppSettingsBuilder()
.AddAppSettings()
.AddDictionarySettings(new Dictionary<string,string> { "override" : "setting" })
.AddEnvironmentalVariables()
.AddTextFile("~/path/to/settings.txt".MapProjectPath())
.Build();
CacheClient with Prefix
The CacheClientWithPrefix
class contributed by @joelharkes lets you decorate any ICacheClient
to prefix all cache keys using the .WithPrefix()
extension method. This could be used to easily enable multi-tenant usage of a single redis instance, e.g:
container.Register(c =>
c.Resolve<IRedisClientsManager>().GetCacheClient().WithPrefix("site1"));
OrmLite Soft Deletes
Select Filters were added to OrmLite to let you specify a custom SelectFilter
that lets you modify queries
that use SqlExpression<T>
before they’re executed. This could be used to make working with “Soft Deletes”
Tables easier where it can be made to apply a custom x.IsDeleted != true
condition on every SqlExpression
.
By either using a SelectFilter
on concrete POCO Table Types, e.g:
SqlExpression<Table1>.SelectFilter = q => q.Where(x => x.IsDeleted != true);
SqlExpression<Table2>.SelectFilter = q => q.Where(x => x.IsDeleted != true);
Or alternatively you can configure a global SqlExpressionSelectFilter
with:
OrmLiteConfig.SqlExpressionSelectFilter = q =>
{
if (q.ModelDef.ModelType.HasInterface(typeof(ISoftDelete)))
{
q.Where<ISoftDelete>(x => x.IsDeleted != true);
}
};
Both solutions above will transparently add the x.IsDeleted != true
to all SqlExpression<T>
based queries so it only returns results which aren’t IsDeleted
from any of queries below:
var results = db.Select(db.From<Table>());
var result = db.Single(db.From<Table>().Where(x => x.Name == "foo"));
var result = db.Single(x => x.Name == "foo");
ServiceStack.Text
You can easily escape HTML Entities characters (<
, >
, &
, =
, '
) when serializing JSON strings with:
JsConfig.EscapeHtmlChars = true;
This can also be requested by clients using Customized JSON Responses, e.g:
/my-api?jsconfig=EscapeHtmlChars
Rabbit MQ
A strong-named version of ServiceStack.RabbitMq is now available at:
PM> Install-Package ServiceStack.RabbitMq.Signed
The new CreateQueueFilter
and CreateTopicFilter
filters added to the Rabbit MQ Server will let you customize what options Rabbit MQ Queue’s and topics are created with.
Startup Errors
To better highlight the presence of Startup Errors we’re now adding a red warning banner in /metadata
pages when in DebugMode, e.g:
The number of Startup Errors is also added to the X-Startup-Errors: n
Global HTTP Header so you’ll be
able to notice it when debugging HTTP Traffic.
Forcing a Content Type
Whilst ServiceStack Services are typically available on any endpoint and format, there are times when you only want adhoc Services available in a particular format, for instance you may only want the View Models for your dynamic Web Views available in HTML. This can now be easily enabled with the new [HtmlOnly]
Request Filter Attribute, e.g:
[HtmlOnly]
public class HtmlServices : Service
{
public object Any(MyRequest request) => new MyViewModel { .. };
}
This feature is also available for other built-in Content Types: [JsonOnly]
, [XmlOnly]
, [JsvOnly]
and [CsvOnly]
.
AuthProvider available on IAuthSession
You can now determine what Auth Provider was used to populate a User’s Session with the new IAuthSession.AuthProvider
property. This could be used for instance to detect and ensure highly-sensitive services are only available to users that authenticated using a specific Auth Provider.
All .NET Core Examples upgraded to .NET Core 1.1
All .NET Core Live examples including the redis-geo project used in Deploy .NET Core with Docker to AWS ECS Guide were upgraded to .NET Core 1.1 and has been changed to use the microsoft/dotnet:1.1-sdk-projectjson
Docker Image since microsoft/dotnet:latest
was changed to only support projects with VS 2017 .csproj
msbuild format.
This covers a high-level view of the major features added in this release, please see the
v4.5.8 Release Notes for the full details and other features added in this release.