ServiceStack v4.0.21

Authentication

Windows Auth Provider for ASP.NET

An ASP.NET WindowsAuth Provider preview is available. This essentially wraps the existing Windows Auth support baked into ASP.NET and adds an adapter for ServiceStack's Multi-Provider Authentication model.

It can be registered just like any other Auth Provider, i.e. in the AuthFeature plugin:

Plugins.Add(new AuthFeature(
    () => new CustomUserSession(), 
    new IAuthProvider[] {
        new AspNetWindowsAuthProvider(this) { AllowAllWindowsAuthUsers = true }, 
    }
));

By default it only allows access to users in AspNetWindowsAuthProvider.LimitAccessToRoles, but can be overridden with AllowAllWindowsAuthUsers=true to allow access to all Windows Auth users as seen in the example above.

Credentials can be attached to ServiceStack's Service Clients the same way as .NET WebRequest's by assingning the Credentials property, e.g:

var client = new JsonServiceClient(BaseUri) {
    Credentials = CredentialCache.DefaultCredentials,
};

var response = client.Get(new RequiresAuth { Name = "Haz Access!" });

To help with debugging, ?debug=requestinfo has been extended to include the Request's current Logon User info:

WindowsAuth DebugInfo

We're interested in hearing future use-cases this can support, feedback on this and future integration with Windows Auth are welcomed on the Active Directory Integration feature request.

New GitHub and other OAuth Providers available

Thanks to Rouslan Grabar we now have a number of new OAuth providers built into ServiceStack, including authentication with GitHub, Russia's most popular search engine Yandex and Europe's largest Social Networks after Facebook, VK and Odnoklassniki:

Plugins.Add(new AuthFeature(
    () => new CustomUserSession(), 
    new IAuthProvider[] {
        new GithubAuthProvider(appSettings), 
        new YandexAuthProvider(appSettings), 
        new VkAuthProvider(appSettings), 
        new OdnoklassnikiAuthProvider(appSettings), 
    }
));

Extended Auth DTO's

You can now test whether a user is authenticated by calling the Auth Service without any parameters, e.g. /auth which will return summary auth info of the currently authenticated user or a 401 if the user is not authenticated. A DisplayName property was added to AuthenticateResponse to return a friendly name of the currently authenticated user.

Portable ServiceStack

A new ServiceStack.Gap Repository and NuGet package was added to help with creating ServiceStack-powered Desktop applications.

ServiceStack has a number of features that's particularly well-suited for these kind of apps:

  • It allows your services to be self-hosted using .NET's HTTP Listener
  • It supports pre-compiled Razor Views
  • It supports Embedded resources
  • It supports an embedded database in Sqlite and OrmLite
  • It can be ILMerged into a single .exe

Combined together this allows you to encapsulate your ServiceStack application into a single cross-platform .exe that can run on Windows or OSX.

To illustrate the potential of embedded ServiceStack solutions, a portable version httpbenchmarks.servicestack.net was created targetting a number of platforms below:

BenchmarksAnalyzer.zip - Single .exe that opens the BenchmarksAnalyzer app in the users browser

Partial Console Screenshot

BenchmarksAnalyzer.Mac.zip - Self-hosted app running inside a OSX Cocoa App Web Browser

Partial OSX Screenshot

BenchmarksAnalyzer.Windows.zip - Self-hosted app running inside a Native WinForms app inside CEF

Partial Windows Screenshot

Usage

By default BenchmarksAnalyzer.exe will scan the directory where it's run from, it also supports being called with the path to .txt or .zip files to view or even a directory where output files are located. Given this there are a few popular ways to use Benchmarks Analyzer:

  • Drop BenchmarksAnalyzer.exe into a directory of benchmark outputs before running it
  • Drop a .zip or folder onto the BenchmarksAnalyzer.exe to view those results

Note: It can also be specified as a command-line argument, e.g: "BenchmarksAnalyzer.exe path\to\outputs"

Benchmarks Analyzer Usage

ServiceStack.Gap Developer Guide

The guides on how each application was created is on ServiceStack.Gap site, i.e:

Other Framework Features

Filtering support added to Metadata pages

You can now filter services on ServiceStack's /metadata page:

Metadata Filter

Typed Request Filters

A more typed API to register Global Request and Response filters per Request DTO Type are available under the RegisterTyped* API's in AppHost. These can be used to provide more flexibility in multi-tenant solutions by attaching custom data on incoming requests, e.g:

public override void Configure(Container container)
{
    RegisterTypedRequestFilter<Resource>((req, res, dto) =>
    {
        var route = req.GetRoute();
        if (route != null && route.Path == "/tenant/{TenantName}/resource")
        {
            dto.SubResourceName = "CustomResource";
        }
    });
}

Typed Filters can also be used to apply custom behavior on Request DTO's sharing a common interface, e.g:

public override void Configure(Container container)
{
    RegisterTypedRequestFilter<IHasSharedProperty>((req, res, dtoInterface) => {
        dtoInterface.SharedProperty = "Is Shared";    
    });
}

Buffered Stream option has now added to Response

Response streams can be buffered in the same way as you can buffer Request streams by setting UseBufferedStream=true, e.g:

appHost.PreRequestFilters.Add((httpReq, httpRes) => {
    httpReq.UseBufferedStream = true;
    httpRes.UseBufferedStream = true;    
});

AfterInitCallbacks added to AppHost

You can register callbacks to add custom logic straight after the AppHost has finished initializing. E.g. you can find all Roles specified in [RequiredRole] attributes with:

appHost.AfterInitCallbacks.Add(host =>
{
    var allRoleNames = host.Metadata.OperationsMap
        .SelectMany(x => x.Key.AllAttributes<RequiredRoleAttribute>()
            .Concat(x.Value.ServiceType.AllAttributes<RequiredRoleAttribute>()))
        .SelectMany(x => x.RequiredRoles);
});

Request Scopes can be configured to use ThreadStatic

Request Scoped dependencies are stored in HttpRequest.Items for ASP.NET hosts and uses Remoting's CallContext.LogicalData API's in self-hosts. Using the Remoting API's can be problematic in old versions of Mono or when executed in test runners.

If this is an issue the RequestContext can be configured to use ThreadStatic with:

RequestContext.UseThreadStatic = true;

Logging

Updated Logging providers to allow debugEnabled in their LogFactory constructor, e.g:

LogFactory.LogManager = new NullLogFactory(debugEnabled:false);
LogFactory.LogManager = new ConsoleLogFactory(debugEnabled:true);
LogFactory.LogManager = new DebugLogFactory(debugEnabled:true);

Detailed command logging is now enabled in OrmLite and Redis when debugEnabled=true. The external Logging provider NuGet packages have also been updated to use their latest version.

Razor

  • Enabled support for Razor @helpers and @functions in Razor Views
  • Direct access to Razor Views in /Views is now denied by default

Service Clients

  • Change Silverlight to auto emulate HTTP Verbs for non GET or POST requests
  • Shorter aliases added on PostFileWithRequest which uses the Request DTO's auto-generated url
  • The PCL version of ServiceStack.Interfaces now supports a min version of .NET 4.0

OrmLite

Exec and Result Filters

A new CaptureSqlFilter Results Filter has been added which shows some of the power of OrmLite's Result filters by being able to capture SQL Statements without running them, e.g:

public class CaptureSqlFilter : OrmLiteResultsFilter
{
    public CaptureSqlFilter()
    {
        SqlFilter = CaptureSql;
        SqlStatements = new List<string>();
    }

    private void CaptureSql(string sql)
    {
        SqlStatements.Add(sql);
    }

    public List<string> SqlStatements { get; set; }
}

This can then be wrapped around existing database calls to capture and print the generated SQL, e.g:

using (var captured = new CaptureSqlFilter())
using (var db = OpenDbConnection())
{
    db.CreateTable<Person>();
    db.Count<Person>(x => x.Age < 50);
    db.Insert(new Person { Id = 1, FirstName = "Jimi", LastName = "Hendrix" });
    db.Delete<Person>(new { FirstName = "Jimi", Age = 27 });

    var sql = string.Join(";\n", captured.SqlStatements.ToArray());
    sql.Print();
}

Exec filters can be limited to specific Dialect Providers

OrmLiteConfig.DialectProvider.ExecFilter = execFilter;

OrmLite's custom SqlBuilders now implement ISqlExpression

OrmLite provides good support in integrating with external or custom SQL builders that implement OrmLite's simple ISqlExpression interface which can be passed directly to db.Select() API. This has now been added to OrmLite's other built-in SQL Builders, e.g:

Using JoinSqlBuilder

var joinQuery = new JoinSqlBuilder<User, User>()
    .LeftJoin<User, Address>(x => x.Id, x => x.UserId, 
        sourceWhere: x => x.Age > 18, 
        destinationWhere: x => x.Country == "Italy");

var results = db.Select<User>(joinQuery);

Using SqlBuilder

var tmpl = sb.AddTemplate(
    "SELECT * FROM User u INNER JOIN Address a on a.UserId = u.Id /**where**/");
sb.Where("Age > @age", new { age = 18 });
sb.Where("Countryalias = @country", new { country = "Italy" });

var results = db.Select<User>(tmpl, tmpl.Parameters);

Other Changes

Redis

A new TrackingRedisClientsManager client manager has been added by Thomas James to help diagnose Apps that are leaking redis connections.