ServiceStack Succinctly
ServiceStack Succinctly
Zoran Maksimovic
This book is available for free download from www.syncfusion.com on completion of a registration form.
If you obtained this book from any other source, please register and download a free copy from
www.syncfusion.com.
This book is licensed for reading only if obtained from www.syncfusion.com.
This book is licensed strictly for personal or educational use.
Redistribution in any form is prohibited.
The authors and copyright holders provide absolutely no warranty for any information provided.
The authors and copyright holders shall not be liable for any claim, damages, or any other liability arising
from, out of, or in connection with the information in this book.
Please do not use this book if the listed terms are unacceptable.
Use shall constitute acceptance of the terms listed.
SYNCFUSION, SUCCINCTLY, DELIVER INNOVATION WITH EASE, ESSENTIAL, and .NET ESSENTIALS are the
registered trademarks of Syncfusion, Inc.
Table of Contents
The Story behind the Succinctly Series of Books .................................................................................. 9
About the Author ....................................................................................................................................... 11
Introduction ............................................................................................................................................... 12
Chapter 1 ServiceStack Overview .......................................................................................................... 15
What is ServiceStack? ........................................................................................................................... 15
Why Should You Consider Using ServiceStack? .................................................................................. 15
ServiceStack Components..................................................................................................................... 16
ServiceStack.Text ................................................................................................................................ 16
ServiceStack.Redis .............................................................................................................................. 17
ServiceStack.OrmLite .......................................................................................................................... 18
ServiceStack.Caching .......................................................................................................................... 18
ServiceStack Philosophy ....................................................................................................................... 19
Request and Response Pipeline ........................................................................................................... 20
Hypermedia as the Engine of Application State (HATEOAS) ................................................................ 21
Chapter 2 ServiceStack Basics .............................................................................................................. 22
Application Host ..................................................................................................................................... 22
Service ................................................................................................................................................... 23
Request ................................................................................................................................................ 24
Response ............................................................................................................................................. 24
Web Service Method Return Types ..................................................................................................... 25
REST HTTP Methods .......................................................................................................................... 26
Content Negotiation ............................................................................................................................... 27
Routing ................................................................................................................................................... 28
Whenever platforms or tools are shipping out of Microsoft, which seems to be about
every other week these days, we have to educate ourselves, quickly.
Free forever
Syncfusion will be working to produce books on several topics. The books will always be free.
Any updates we publish will also be free.
10
11
Introduction
Microsofts ASMX, ASP.NET Web API, and WCF are very solid frameworks for building web
services. However, there are a number of open-source alternatives that can be used and
ServiceStack is one of them.
ServiceStack is an open-source1 framework that complements, extends, and substitutes the
aforementioned built-in Microsoft .NET technologies.
Target Audience
This book is intended for software developers with a good understanding of the Microsoft .NET
framework (as all examples are written in C#) and web technologies in general.
A basic understanding of Hypertext Transfer Protocol (HTTP), Simple Object Access Protocol
(SOAP), Representational State Transfer (REST), JavaScript Object Notation (JSON), and
Extensible Markup Language (XML) is required.
HTTP: http://www.ietf.org/rfc/rfc2616.txt
REST: http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
At the time of this writing, as announced by ServiceStack owner Demis Bellot: To ensure its long-term
sustainability, ServiceStack will transition to an annually-supported paid product similar to Xamarin's products.
Starting from the next major v4.0 release of ServiceStack.* on NuGet, which will be the first commercial-only, binary
distribution (with an exception for OSS projects and a "free-tier" for small projects).
1
12
JSON: http://json.org
XML: http://www.w3.org/TR/xml
SOAP: http://www.w3.org/TR/soap12-part1
Source Code
At the time of this writing, ServiceStacks source code is open source and is hosted on GitHub
at https://github.com/ServiceStack/ServiceStack.
https://twitter.com/servicestack
Google Plus
https://plus.google.com/u/0/communities/112445368900682590445
Google
Groups
http://groups.google.com/group/servicestack
JabbR
https://jabbr.net/#/rooms/servicestack
StackOverflow http://stackoverflow.com/questions/tagged/servicestack
Software Requirements
To get the most out of this book and the included examples, you will need to have a version of
the Visual Studio IDE installed on your computer and Microsoft .NET 4. At the time of this
writing, the latest available stable edition of Visual Studio Express is Visual Studio 2013. You
can download Visual Studio Express 2013 for free directly from Microsofts website at
http://www.visualstudio.com/en-us/products/visual-studio-express-vs.aspx.
13
Tip: This will identify tips and tricks throughout the book.
Source Code
All examples in this book are written in C#. The source code will always be shown in the
following format.
[Route("/movies/{Id}", "GET")]
public class GetMovieRequest
{
public string Id { get; set; }
}
Microsoft .NET types or Uniform Resource Identifiers (URIs) that are mentioned in the text will
be shown using a monospace font: JsonSerializer.
Data will be usually displayed in the XML format simply because I believe that XML provides a
very readable format that is easy to understand.
Resources
Code mentioned in this book can be checked out from the Bitbucket repository at
https://bitbucket.org/syncfusiontech/servicestack-succinctly.
ServiceStack Version
All of the examples and explanations apply to version 3.9.59 of ServiceStack, which is the latest
stable version at the time of this writing.
14
And I agree.
In a nutshell, ServiceStack supports the following features:
15
ServiceStack Components
ServiceStack is made up of a number of independent modules (see Figure 1) which can be
used separately in projects without using the ServiceStack framework itself.
ServiceStack.Text
ServiceStack.Redis
ServiceStack.OrmLite
ServiceStack.Caching
ServiceStack Framework
Figure 1: ServiceStack components
ServiceStack.Text
ServiceStack.Text is an independent, dependency-free serialization library that is used by
ServiceStack internally to do any text processing. ServiceStack.Text contains a large number
of features, and some of the most important ones are:
If you were to use ServiceStack.Text in your project and needed the JSON serializer, the
following code example shows how to do so.
public static void JsonSerializationExample()
{
var person = new {LastName = "Doe", Name = "John", Age = 36};
var personJson = JsonSerializer.SerializeToString(person);
Console.WriteLine(personJson);
}
// Output produced (JSON Format):
// {"LastName":"Doe","Name":"John","Age":36}
16
TypeSerializer
TypeSerializer is one of the fastest and most compact text serializers available for .NET. Out
of all the serializers benchmarked, it is the only one to remain competitive with protobuf-net's
very fast implementation of Protocol Buffers, Googles high-speed binary protocol. 2
public static void TypeSerializerExample()
{
var person = new { LastName = "Doe", Name = "John", Age = 36 };
var personJsv = TypeSerializer.SerializeToString(person);
Console.WriteLine(personJsv);
}
// Output produced (JSV Format):
// {LastName:Doe,Name:John,Age:36}
TypeSerializer uses a hybrid CSV and JavaScript-like, text-based format that is optimized for
both size and speed. Authors have called this format JSV, which is a combination of JSON and
CSV.
Source code for TypeSerializer can be downloaded at
https://github.com/ServiceStack/ServiceStack.Text, or the package can be directly installed by
using NuGet with the following command:
PM> Install-Package ServiceStack.Text
ServiceStack.Redis
ServiceStack.Redis is an open-source client for the Redis (http://www.redis.io) in-memory
database. ServiceStack.Redis simplifies interfacing with Redis significantly and, as
mentioned earlier, is built on ServiceStack but can be used separately.
Note: Redis is an open source, Berkeley Software Distribution (BSD) licensed, advanced
key-value store. It is often referred to as a data structure server since keys can contain
strings, hashes, lists, sets, and sorted sets. In order to achieve its outstanding
performance, Redis works with an in-memory data set that can be persisted to disk once
in a while or by appending each command to a log.
TypeSerializer benchmark:
http://mono.servicestack.net/benchmarks/NorthwindDatabaseRowsSerialization.1000000-times.2010-02-06.html
17
ServiceStack.OrmLite
ServiceStack includes its own ORM library, which is a convention-based, configuration-free,
lightweight ORM that uses standard plain-old CLR object (POCO) classes and data annotation
attributes to infer its table schema.
ServiceStack.OrmLite currently supports several relational databases: Microsoft SQL Server,
MySQL, PostgreSQL, Oracle, Firebird, SQLite32, SQLite64, and SQLite.Mono.
ServiceStack.OrmLite is compatible with both Microsoft .NET and Mono.
Source code for ServiceStack.OrmLite can be downloaded from
https://github.com/ServiceStack/ServiceStack.OrmLite. Alternatively, packages can be directly
installed by using NuGet with the following commands.
Microsoft SQL Server provider:
PM> Install-Package ServiceStack.OrmLite.SqlServer
Oracle provider:
PM> Install-Package ServiceStack.OrmLite.Oracle
ServiceStack.Caching
ServiceStack provides ICacheClient, a unified caching interface, for a number of different
cache providers:
18
ServiceStack Philosophy
ServiceStack is influenced by Martin Fowlers Data Transfer Object pattern3 (DTO) and
promotes message based communication. This pattern simplifies manipulation of request and
response data, and enables decoupling of message structures from domain layer entities.
xxxRequestDTO
xxxResponseDTO
Client Application
IIS
APPLICATION
RequestDTO
Web Service
Implementation
ResponseDTO
XXXRequestDTO
XXXResponseDTO
Self Hosted
Application
(Console App,
Windows
Service)
i.e.
OrderService
ProductService
xxxService
Application
Domain Model
Repositories
Application
Business Layer
(Application
Services, Other
things)
Exposing DTOs, and not the application domain model, gives the freedom to refactor internal
implementation of the service without breaking external clients and keeps a clean interface.
By definition, a ServiceStack web service will have, at minimum, the following:
Basic web service components
Class
Description
The input of a service method. It represents the action
to be performed. Usually the name of the class
contains a verb (e.g., GetOrderRequest,
DeleteItem).
For more information about the DTO pattern, please read Martin Fowlers article:
http://martinfowler.com/eaaCatalog/dataTransferObject.html.
3
19
Class
Description
Service
Request DTO
Request Binder
JSON/XML/JSV/
CSV...
Response DTO
Request
Filters
Request DTO
Service
IService<T>
Response
Filters
HttpResult
Response
Response DTO
HttpError
Compressed
Result
20
21
Application Host
The base entry point for ServiceStack is the AppHost (application host). The concept of the
application host is to define a central point for configuring and wiring the request to the service.
There can be only one application host per application.
In general, a ServiceStack service can be hosted on Internet Information Services (IIS) as a
console application, Windows Service, or mixed with an ASP.NET or ASP.NET MVC
application.
Web applications will implement the AppHostBase class while the console and Windows
Service applications will implement AppHostHttpListenerBase.
In a web application, the application host has to be started only once when the application
starts. This can be done in the Global.asax.cs file by calling the Init method in the
Application_Start method. The following code shows the minimum that has to be configured.
public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
new ServiceAppHost().Init();
}
}
public class ServiceAppHost : AppHostBase
{
public ServiceAppHost()
: base("Order Management", typeof (ServiceAppHost).Assembly)
{
}
public override void Configure(Container container)
{
}
}
22
To configure a console application, the style is practically the same. There is no difference apart
from inheriting from a different base class and starting the application in the Main() method
rather than in the Global.asax.cs file.
public class ServiceAppHost : AppHostHttpListenerBase
{
public ServiceAppHost() :
base("Order Management System", typeof (ServiceAppHost).Assembly)
{
}
public override void Configure(Container container)
{
}
}
static void Main()
{
ServiceAppHost appHost = new ServiceAppHost();
appHost.Init();
appHost.Start("http://*:80/");
Console.Read();
}
Service
Historically, there are several ways in which the web service can be created. Interfaces used in
earlier versions of ServiceStack used IRestService and IService<T>. However, currently the
recommended way is to use ServiceStack.ServiceInterface.Service, which is the
approach used in this book.
public class OrderService : ServiceStack.ServiceInterface.Service { }
As shown in the following code, the Service base class implements and exposes several very
useful methods and properties such as Request and Response, which are the two most used
objects in the service and give a great deal of manipulation and inspection possibilities.
public class Service : IService, IRequiresRequestContext, IServiceBase, IResolver,
IDisposable
{
public virtual IResolver GetResolver();
public virtual IAppHost GetAppHost();
public virtual Service SetResolver(IResolver resolver);
public virtual T TryResolve<T>();
public virtual T ResolveService<T>();
protected virtual TUserSession SessionAs<TUserSession>();
public virtual void PublishMessage<T>(T message);
23
Request
Request implements the .NET built-in IHttpRequest interface and therefore exposes all that
we might need to know about the current request. Among the other things to note is the ability to
inspect Headers, QueryString, and the current HttpMethod.
Imagine the following HTTP request.
GET http://<servername>/orders?page=1
Accept: application/json
Response
Response represents the object that the client will eventually receive. Among others, one of the
most useful methods exposed by the Response object is the AddHeader method, which
manipulates the headers returned to the client. Lets look at an example of the AddHeader
usage.
24
Response DTO object serialized to the response type (JSON, XML, PNG).
Any basic .NET value.
HttpResult: Used whenever full control of what client recieves is needed.
HttpError: Used to return the error message to the client.
CompressedResult (IHttpResult) for a customized HTTP response.
25
GET
POST
PUT
Retrieves a resource.
It is safe (guaranteed not to cause side
effects), idempotent,5 and cacheable.
Should never change the state of a resource.
Creates a new resource.
Unsafe; the effect of this verb is not
specifically defined by HTTP.
Not idempotent.
Updates an existing resource.
Can be used to create a new resource when
client knows the URI.
Can be called n-number of times and always
produces the same effect (idempotent).
DELETE
PATCH
The service can have the same HTTP verb implemented multiple times, but the input
parameters should be different as with any other normal method overload.
public class OrderService : ServiceStack.ServiceInterface.Service
{
public object Get
(SomeRequest request) {...}
public object Get
(SomeRequest2 request) {...}
public object Post
public object Post
26
Content Negotiation
If not specified otherwise, ServiceStack offers mainly three ways of negotiating the content type
of the resource:
Use of the HTTP Accept header is considered by many to be a recommended and more
elegant way of negotioating the content type, and it is the HTTP standard. However, its a hot
topic and there is debate about whether to pass the format instructions directly in the URI. I
think that it is good to have them both, as both have their pros and cons.
Since you should already be familiar with how to use the HTTP Accept6 header, we wont go
into details. The following table shows the options available in ServiceStack.
Content negotiation in ServiceStack
Accept Header
/orders?format=json
/orders.json
Accept: application/json
/orders?format=xml
/orders.xml
Accept: application/xml
/orders?format=jsv
/orders.jsv
Accept: application/jsv
/orders?format=csv
/orders.csv
Accept: application/csv
Forcing the content type for every request to JSON by configuring the application host.
27
Specifying the content type at the service methods level, either by using an AddHeader
filter or by specifying the ContentType.
[AddHeader(ContentType = ContentType.Json)]
public object Get(GetOrdersRequest request) { /*..*/}
Or, alternatively:
public object Get(GetOrdersRequest request)
{
base.Response.ContentType = ContentType.Json;
return /*..*/
}
Routing
Routing is the process of selecting paths along which to send a request. To determine which
action to perform for a request, ServiceStack has to keep the list of routes and this has to be
instructed specifically when the application starts. There are several ways in which the route can
be registered:
28
Default Routes
By default, for every Request DTO, ServiceStack will create a default route in the following form:
/api?/[xml|json|html|jsv|csv]/[reply|oneway]/[servicename]
Lets suppose we want to support a custom route, http://<servername>/orders, and we expose a
Request DTO called GetOrders. In this case, without specifying any route, ServiceStack will
create http://<servername>/xml/reply/GetOrders automatically.
Custom Routes
A route can be declared as a class attribute directly at the Request DTO level or in the
AppHostBase by using the Fluent API. The route has an option to define one or more HTTP
verbs.
Route Attribute
By using the RouteAttribute, a route can be declared directly at the Request DTO object.
[Route("/orders", "GET POST", Notes="", Summary="")]
public class GetOrders { }
Fluent API
Instead of using the RouteAttribute, the same can be achieved by defining the route in the
application host declaration.
public class ServiceAppHost : AppHostBase
{
public ServiceAppHost()
: base("Order Management", typeof(ServiceAppHost).Assembly)
{
Routes
.Add<GetOrders>("/orders", "GET")
.Add<CreateOrder>("/orders", "POST")
.Add<GetOrder>("/orders/{Id}", "GET")
.Add<UpdateOrder>("/orders/{Id}", "PUT")
.Add<DeleteOrder>("/orders/{Id}", "DELETE")
}
}
29
Dynamic Paths
ServiceStacks routing mechanism offers a way to dynamically bind the parameters sent in the
URL and to deserialize the object once it reaches the service. In the following code example, we
can see that in the route there is an {Id} declaration. At run time, the value sent as {Id} will be
deserialized as the GetOrder.Id property.
[Route("/orders/{Id}", "GET")]
public class GetOrder
{
public string Id { get; set; }
}
Autoregistered Paths
By using the Routes.AddFromAssembly(typeof(OrderService).Assembly) method, we can
automatically map and define all routes.
using ServiceStack.ServiceInterface;
//Request DTO (notice there is no route defined as the Attribute!)
public class GetOrderRequest
{
public string Id { get; set; }
}
// Service
public class OrderService : ServiceStack.ServiceInterface.Service
{
public List<OrderResponse> Get(GetOrdersRequest request) { }
public object Post(CreateOrder request) { }
}
// Autoregistering the routes in the application host.
public class ServiceAppHost : AppHostBase
{
public ServiceAppHost (): base("Order Management",
typeof(OrderService).Assembly)
{
Routes.AddFromAssembly(typeof (OrderService).Assembly);
}
}
By using reflection, ServiceStack will check the OrderService, determine the requests
parameter type, and autogenerate the route.
For the previous code example, the route generated will be /GetOrders (GET) and
/CreateOrder (PUT).
30
Route Wildcards
There is a way to define a wildcard in the route; this is especially useful when the route
becomes too complex.
The following is an example with a route that uses a wildcard.
Routes.Add("/orders/{CreationDate}//{Others*}", "GET");
IoC Container
ServiceStack has built-in support for dependency injection. To achieve this, it uses a slightly
modified version of the Funq7 framework. Funq is fast and easy to use. ServiceStack has
enhanced the basic version of Funq with lifetime scope of the injected objects and therefore
supports:
31
All of the configuration can be done directly in the application host and declaring the objects is
not very different than in any other framework.
public class ServiceAppHost : AppHostBase
{
public override void Configure(Container container)
{
container.Register<IOrderRepository>(new OrderRepository());
container.Register<IProductMapper>(x => new ProductMapper ())
.ReusedWithin(ReuseScope.Container);
}
}
References will be automatically injected if the service exposes a public property or if it has a
constructor, with the parameters being one of the registered IoC types.
public class OrderService : ServiceStack.ServiceInterface.Service
{
public IOrderRepository OrderRepository { get; set; }
public OrderService(IProductRepository productRepository){ /*..*/}
}
Custom Containers
ServiceStack enables the integration of third-party IoC frameworks through the
IContainerAdapter interface. This only exposes two methods.
public interface IContainerAdapter
{
T Resolve<T>();
T TryResolve<T>();
}
There are several implementations already available for Microsoft Unity8, Ninject9,
StructureMap10, Castle Windsor,11 and Autofac12.
Microsoft Unity Container Adapter installation package:
32
Validation
ServiceStack has a validation framework built around the Fluent Validation library.13
When the validation is enabled, the validation framework will check the predefined rules before
the service method gets invoked. In the case of a validation error, the ErrorResponse object
will be returned with the error details (as shown in Figure 4). Its good to know that the validation
is performed on the server side.
RequestDTO
Validator
Service
GET|POST|PUT|DELETE
ResponseDTO
Validation error
ErrorResponse
Figure 4: Validation
13
33
Create the validator implementation class: In our case, create the GetOrderValidator
class. This class in particular will only be responsible for validating the
GetOrdersRequest Request DTO object. The Validator class has to inherit from the
AbstractValidator class and specify the Request DTO as the generic parameter
type.
Register the ValidationFeature and the validator implementation in the application
host. This will enable the validation framework and register the particular validator
class, in our case GetOrderValidator. Registration is done by using the
Plugins.Add() method.
In the following code example, the validator would only be used in the case of GET or POST
methods, and this is defined with the RuleSet method. RuleFor instead specifies the rule to be
applied for a given DTO property. As you will see, a customized error message can be
specified.
public class GetOrderValidator : AbstractValidator<GetOrdersRequest>
{
public GetOrderValidator()
{
//Validation rules for GET request.
RuleSet(ApplyTo.Get | ApplyTo.Post, () =>
{
RuleFor(x => x.Id)
.GreaterThan(2)
.WithMessage("OrderID has to be greater than 2");
});
}
}
public class ServiceAppHost : AppHostBase
{
public ServiceAppHost()
: base("Order Management", typeof(ServiceAppHost).Assembly)
{
//Enabling the validation.
Plugins.Add(new ValidationFeature());
Container.RegisterValidator(typeof(GetOrderValidator));
}
In case of an invalid request, such as GET/orders/1, the framework will return the following
error (in cases where the format specified is XML).
<ErrorResponse>
<ResponseStatus>
<ErrorCode>GreaterThan</ErrorCode>
<Message>OrderID has to be greater than 2</Message>
<StackTrace i:nil="true" />
<Errors>
<ResponseError>
<ErrorCode>GreaterThan</ErrorCode>
<FieldName>Id</FieldName>
<Message>'Id' must be greater than '2'.</Message>
</ResponseError>
</Errors>
</ResponseStatus>
</ErrorResponse>
34
Client Tools
Since ServiceStack exposes standard RESTful web services which are based on pure HTTP,
any HTTP-capable client is able to access and consume it. It doesnt matter which programming
language or framework is used; the important thing is the ability to enable communication by
using HTTP.
There are several .NET clients currently available: RestSharp,14 which is an open-source
implementation, and several built-in Microsoft .NET ones like HttpClient,15 WebClient,16 and
HttpWebRequest.17
The ServiceStack framework, however, provides its own client implementations that are highly
optimized for ServiceStack (e.g., exception handling and routing). There are different
implementations of the client, which can be a generic C# client, Silverlight client, JavaScript
client, Dart18 client, or MQ client.19
Clients are optimized for the content type. So there is a JsonServiceClient,
XmlServiceClient, JsvServiceClient, and two SOAP clients, Soap12ServiceClient and
Soap11ServiceClient, for the two current SOAP versions. The difference between the clients
is the serializer/deserializer being used. As the following example shows, using a ServiceStack
client is relatively easy.
JsonServiceClient client = new JsonServiceClient("http://localhost:50712/");
OrderResponse order = client.Get<OrderResponse>("/orders/1");
Metadata Page
By default, when accessing the web application, ServiceStack exposes a metadata page. The
metadata page contains information about the operations, available content types, Web
Services Description Language (WSDL), and other objects exposed in the current application
host. It is extremely useful because it acts as documentation for the available operations and
used types (see Figure 5).
For every format (XML, JSON, etc.) there is an example of an input and output object. This
means that we can see what the service accepts as the Request and Response DTOs.
14
RestSharp: http://restsharp.org
HttpClient: http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.aspx
16 WebClient: http://msdn.microsoft.com/en-us/library/system.net.webclient.aspx
17 HttpWebRequest: http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
18 Dart Client: https://github.com/ServiceStack/ServiceStack/wiki/Dart-Client
19 MQ Clients: https://github.com/ServiceStack/ServiceStack/wiki/Messaging
15
35
36
The order management service will then be expanded, and more features and functionalities will
be added, such as authentication, validation, and dependency injection, just to name a few.
37
Product
A Product represents a part of the Order and its a reference class. A Product can be Active
or Inactive, and this is reflected through its Status.
Status
The Status class is a reference data class. In our application, it will have only two possible
statuses: Active and Inactive.
public class Order
{
public int
public bool
public DateTime
public List<OrderItem>
public Status
public int
}
Id
IsTakeAway
CreationDate
Items
Status
Version
{
{
{
{
{
{
{
{
get;
get;
get;
get;
get;
get;
get;
get;
set;
set;
set;
set;
set;
set;
set;
set;
{
{
{
{
{
{
get;
get;
get;
get;
get;
get;
set;
set;
set;
set;
set;
set;
}
}
}
}
}
}
}
}
}
}
}
}
}
}
38
URI
HTTP Verb
Description
/orders
GET
/orders
POST
/orders/1234
GET
/orders/1234
PUT
/orders/1234
DELETE
The web service OrderItemService exposes operations related to the Orders Items
collection as shown in Table 6.
Exposed OrderItemService operations
URI
HTTP Verb
Description
/orders/1234/items
GET
/orders/1234/items/1
GET
URI
39
HTTP Verb
Description
/products
GET
/products
POST
/products/1234
GET
/products/1234
PUT
URI
/products/1234
HTTP Verb
DELETE
Description
Deletes an existing product.
20
The facade layer should be a thin layer without any business logic.
Keep the application implementation separated from the hosting application. This is very
useful in case we want to reuse the application logic and expose it in a different way (e.g.,
as a desktop app, web service, or web application).
Keep the Request and Response DTOs in a separate assembly as this can be shared with
the client (especially useful for .NET clients).
40
Hosting
Client
application
ResponseDTO
RequestDTO
ServiceStack.Succinctly.Host.DLL
- OrderService
- OrderItemService
- Transformation Helpers
OrderManagement.Core.DLL
- Domain Object Model
- Application Services
- Business Rules
OrderManagement.DataAccessLayer.DLL
- OrderRepository
ServiceStack.Succinctly.ServiceInterface.DLL
- RequestDTOs
- ResponseDTOs
ServiceStack.Succinctly.Host
ServiceStack.Succinctly.Host is the entry point of the application and the only component
that communicates with the client. It contains the exposed services and all of the necessary
plumbing, routing, instrumentation, etc. This is exactly where we are going to use the
ServiceStack framework functionalities.
ServiceStack.Succinctly.ServiceInterface
ServiceStack.Succinctly.ServiceInterface contains Request and Response DTOs. This
library can be directly shared with the client application to ease the communication and
transformation of data. This library shouldnt contain any application logic.
OrderManagement.Core
OrderManagement.Core contains the domain model of the application and the required
business logic, application logic, etc.
OrderManagement.DataAccessLayer
OrderManagement.DataAccessLayer contains the logic to access the database and the
related repositories.
41
ServiceStack.Succinctly.Host
ServiceStack.Succinctly.ServiceInterface
OrderManagement.Core
OrderManagement.DataAccessLayer
42
43
ServiceStack
ServiceStack.Common
ServiceStack.Interfaces
ServiceStack.OrmLite
ServiceStack.OrmLite.SqlServer
ServiceStack.Redis
ServiceStack.ServiceInterface
ServiceStack.Text
Tip: If you want to mix ServiceStack with your existing ASP.NET or ASP.NET MVC
applications, there are two packages that were created specifically to help you install
ServiceStack automatically:
PM> Install-Package ServiceStack.Host.Mvc
PM> Install-Package ServiceStack.Host.AspNet
After adding the required binary files to the project, we need to instruct our application to use the
ServiceStack HTTP handler, which will act as an entry point of the application. This is done by
putting the following configuration in the web.config file.
<configuration>
<!-- Required for IIS 6.0 (and above) -->
<system.web>
<httpHandlers>
<add path="*"
type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory,
ServiceStack"
verb="*"/>
</httpHandlers>
<compilation debug="true"/>
</system.web>
<!-- Required for IIS 7.0 -->
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<handlers>
<add path="*"
name="ServiceStack.Factory"
type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory,
ServiceStack"
verb="*"
preCondition="integratedMode"
resourceType="Unspecified"
allowPathInfo="true"/>
</handlers>
</system.webServer>
</configuration>
44
45
Service
OrderService
Description
Contains methods that insert, delete, create, and update orders.
ProductService
/orders
/orders/{id}
/orders
/orders/{id}
/orders/{id}
OrderItemService
GET
GET
POST
PUT
DELETE
GET
GET
POST
PUT
DELETE
/products
/products/{id}
/products
/products/{id}
/products/{id}
GET
GET
/orders/{id}/items
/orders/{id}/items/{id}
46
Additional Information
Link and Status DTOs
All of the Response DTO objects will use the Status and Link classes. Ive included the code
here as a reference.
public class Status
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Link
{
public string
public string
public string
public string
}
The Link class will contain the hypermedia information related to the resource.
Test Project
To test the various services, create a new project called
ServiceStack.Succinctly.Host.IntegrationTest that references the following assemblies:
Removing Namespaces
To remove superfluous namespaces from XML returned objects, add the following code to the
Assembly.cs in the ServiceStack.Succinctly.ServiceInterface project.
[assembly: ContractNamespace("", ClrNamespace="ServiceStack.Succinctly.ServiceInte
rface.OrderModel")]
[assembly: ContractNamespace("", ClrNamespace="ServiceStack.Succinctly.ServiceInte
rface.OrderItemModel")]
[assembly: ContractNamespace("", ClrNamespace="ServiceStack.Succinctly.ServiceInte
rface.ProductModel")]
47
Product Service
In our solution, the Product class can be seen as a reference data class as it is a property of
the OrderItem class. Since one Product can be assigned to several OrderItems, there is a
need to have a separate service that specifically manages a Product itself. ProductService is
a classic create, read, update, and delete (CRUD) service.
Service Model
As a particularity of the service, Ive chosen to create two separate DTOs for managing inserts
and updates. Ive done so to show that there is such a possibility and that we may fine-tune the
actual requests. Indeed, the CreateProduct class specifically doesnt have the Id because the
client shouldnt know about the Id when creating a product. However, you may choose not to
implement it in such a way, but to have a more consistent object instead (as we are going to see
in the OrderService implementation).
The following five classes will be used as Request DTOs of the service. The GetProducts class
intentionally does not have properties; it is mainly used for routing a request to the proper
service method as we will see later.
public class GetProducts { }
public class GetProduct
{
public int Id { get; set; }
}
public class CreateProduct
{
public string Name
{ get; set; }
public Status Status { get; set; }
}
public class UpdateProduct
{
public int
Id
{ get; set; }
public string Name
{ get; set; }
public Status Status { get; set; }
}
public class DeleteProduct
{
public int Id { get; set; }
}
48
Almost all of the Service methods will return the ProductResponse object. ProductResponse is
a mirror of the Product and, as we will see, it can contain more or less information. In our case,
it contains a list of Links and no information about the Product.Version, which is only used
for optimistic concurrency control.
ProductsResponse instead will hold a list of ProductResponse. This is helpful as we can reuse
this object and enrich it with further attributes that can be useful for paging, navigation, etc.
public class ProductResponse
{
public int
Id
public string
Name
public Status
Status
public List<Link> Links
}
{
{
{
{
get;
get;
get;
get;
set;
set;
set;
set;
}
}
}
}
Route Specification
In the application host (Global.asax.cs), we need to register the various routes related to the
Product service. As we have seen in the previous chapters, this is done either in the application
hosts constructor or in the Configure method. Ive chosen to use the constructor because I
want to use the Configure method only for the IoC-related items.
using ServiceStack.Succinctly.Host.Extensions;
public ServiceAppHost():
ceAppHost).Assembly)
{
Routes
.Add<GetProducts> ("/products",
.Add<GetProduct>
("/products/{Id}",
.Add<CreateProduct>("/products",
.Add<UpdateProduct>("/products/{Id}",
.Add<DeleteProduct>("/products/{Id}",
}
"GET",
"GET",
"POST",
"PUT",
"DELETE",
"Returns
"Returns
"Creates
"Updates
"Deletes
Products")
a Product")
a Product")
a Product")
a Product");
The ServiceStack Routes.Add() method currently doesnt expose the method signature that
we have just seen. To achieve this, Ive created an extension method. The Routes property
implements the IServiceRoute interface and, once we know this, its very easy to extend.
public static class RoutesExtensions
{
public static IServiceRoutes Add<T> (this IServiceRoutes routes,
49
The ProductMapper implements the IProductMapper interface, which will make it easier to be
injected to the Service. Lets create the following file in the ServiceStack.Succinctly.Host
project under the ServiceStack.Succinctly.Host.Mappers namespace.
The following code example shows the definition of the IProductMapper interface.
using OrderManagement.Core.Domain;
using ServiceStack.Succinctly.ServiceInterface.ProductModel;
namespace ServiceStack.Succinctly.Host.Mappers
{
public interface IProductMapper
{
Product ToProduct(CreateProduct request);
Product ToProduct(UpdateProduct request);
ProductResponse ToProductResponse(Product product);
21
50
51
{
var productResponseList = new List<SrvObj.ProductResponse>();
products.ForEach(x =>
productResponseList.Add(ToProductResponse(x)));
return productResponseList;
}
}
}
Note: All of our services will have a Mapper class by design because we want to separate
the Request and Response DTOs (service model) from the application domain model.
Validation Implementation
As we saw in the previous chapter, we can create some custom validation logic. We will create
some in this case because we want to make our implementation a bit stronger. For our current
example, we will just make sure that when the Product is created or updated, we check that the
Name property is set and is not longer than 50 characters. We will create the following two
classes in the ServiceStack.Succinctly.Host.Validation namespace, and will register
those two validators in the application host.
public class CreateProductValidator : AbstractValidator<CreateProduct>
{
public CreateProductValidator()
{
string nameNotSpecifiedMsg = "Name has not been specified.";
string maxLenghtMsg = "Name cannot be longer than 50 characters.";
RuleFor(r => r.Name)
.NotEmpty().WithMessage(nameNotSpecifiedMsg)
.NotNull().WithMessage(nameNotSpecifiedMsg)
.Length(1, 50).WithMessage(maxLenghtMsg);
}
}
public class UpdateProductValidator : AbstractValidator<UpdateProduct>
{
public UpdateProductValidator()
{
string nameNotSpecifiedMsg = "Name has not been specified.";
string maxLenghtMsg = "Name cannot be longer than 50 characters.";
RuleFor(r => r.Name)
.NotEmpty().WithMessage(nameNotSpecifiedMsg)
.NotNull().WithMessage(nameNotSpecifiedMsg)
.Length(1, 50).WithMessage(maxLenghtMsg);
}
}
52
Service Implementation
ProductService implements two Get methods, one Post, one Put, and one Delete. Every
ServiceStack service has to inherit from the ServiceStack.ServiceInterface.Service
class.
As shown in the following code example, ProductService has two properties: ProductMapper
and ProductRepository, which will hold the instances that will be injected at run time by the
IoC container.
public class ProductService : ServiceStack.ServiceInterface.Service
{
public IProductMapper ProductMapper { get; set; }
public IProductRepository ProductRepository { get; set; }
public
public
public
public
53
Get Products by Id
When calling GET /product/1, the following method will be called. As you can see, the
implementation is quite simple:
In order to call the GET service method in the previous example, we can use any web client. The
following example shows a unit test (using Microsoft Test Framework) that uses the built-in
ServiceStack JsonServiceClient to perform a GET on the /products/{Id} URI.
[TestMethod]
public void GetProductByProductId_ReturnsValidProduct()
{
//ARRANGE --int PRODUCT_ID = 1;
var client = new JsonServiceClient("http://localhost:50712/");
//ACT -----var product = client.Get<ProductResponse>("/products/" + PRODUCT_ID);
//ASSERT ---Assert.IsTrue(product!=null);
Assert.IsTrue(product.Id == PRODUCT_ID);
}
54
To call the /products in order to get the list of available products, the client would look like the
following.
[TestMethod]
public void GetAllProducts_ReturnsValidProductList()
{
55
56
To insert a new product using the ServiceStack client, we will test that the Response is returning
201 Created HTTP status and that the Location header has been returned with the new URI.
[TestMethod]
public void CreateNewProduct_ReturnsObjectAnd201CreatedStatus()
{
//ARRANGE --WebHeaderCollection headers = null;
HttpStatusCode statusCode = 0;
const string PRODUCT_NAME = "Cappuccino";
const string SITE = "http://localhost:50712";
const string PRODUCTS = "/products";
const string URI = SITE + PRODUCTS;
var client = new JsonServiceClient(SITE)
{
//Grabbing the header once the call is ended.
LocalHttpWebResponseFilter =
httpRes =>
{
headers = httpRes.Headers;
statusCode = httpRes.StatusCode;
}
};
var newProduct = new CreateProduct
{
Name = PRODUCT_NAME,
Status = new Status {Id = 1}
};
//ACT -----var product = client.Post<ProductResponse>(PRODUCTS, newProduct);
57
After the POST method has been called, the following is the response from the service, header,
and body. As you can see, the 201 Created verb and the Location headers are specified and
returned to the client.
HTTP/1.1 201 Created
Cache-Control: private
Content-Type: application/xml
Location: http://localhost:50712/products/10
Server: Microsoft-IIS/8.0
X-Powered-By: ServiceStack/3.956 Win32NT/.NET
Date: Tue, 13 Aug 2013 22:15:42 GMT
Content-Length: 277
<ProductResponse>
<Id>10</Id>
<Links>
<Link>
<Href>products/10</Href>
<Rel>self</Rel>
<Title>self</Title>
<Type i:nil="true" />
</Link>
</Links>
<Name>Cappuccino</Name>
<Status>
<Id>1</Id>
<Name>Active</Name>
</Status>
</ProductResponse>
Updating a Product
To update a product, our service will implement the PUT method which will accept the
UpdateProduct message.
Before doing any work, we check if the resource we are updating is available in the repository. If
it is not, we then force the 404 Not Found status code which indicates that the resource is not
available. In a successful scenario, either the 200 (OK) or 204 (No Content) response codes
should be sent to indicate successful completion of the request. We will return 200 (OK) and
the full body of the message if the call is successful.
58
To test the product update, where only the status of the resource would be updated to
Inactive:
[TestMethod]
public void UpdateProduct_ReturnsUpdatedObject()
{
//ARRANGE --HttpStatusCode statusCode = 0;
const string PRODUCT_NAME = "White Wine";
const string SITE = "http://localhost:50712";
const string PRODUCT_LINK = "/products/2";
var client = new JsonServiceClient(SITE)
{
//Grabbing the header once the call is ended.
LocalHttpWebResponseFilter =
httpRes =>
{
statusCode = httpRes.StatusCode;
}
};
var updateProduct = new UpdateProduct
{
Name = PRODUCT_NAME,
Status = new Status {Id = 2} // Id = 2 means inactive.
};
//ACT -----var product = client.Put<ProductResponse>(PRODUCT_LINK, updateProduct);
//ASSERT ---Assert.IsTrue(statusCode == HttpStatusCode.OK);
59
Assert.IsTrue(product.Name == PRODUCT_NAME);
Assert.IsTrue(product.Status.Id == 2);
}
The difference between the original and the new (updated) resource is only the Status.Id.
Original Resource
Server Response
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/xml
Server: Microsoft-IIS/8.0
Date: Tue, 13 Aug 2013 22:19:55 GMT
Content-Length: 278
<ProductResponse>
<Id>2</Id>
<Links>
<Link>
<Href>products/2</Href>
<Rel>self</Rel>
<Title>self</Title>
<Type i:nil="true"/>
</Link>
</Links>
<Name>White Wine</Name>
<Status>
<Id>1</Id>
<Name>Active</Name>
</Status>
</ProductResponse>
<ProductResponse>
<Id>2</Id>
<Links>
<Link>
<Href>products/2</Href>
<Rel>self</Rel>
<Title>self</Title>
<Type i:nil="true"/>
</Link>
</Links>
<Name>White Wine</Name>
<Status>
<Id>2</Id>
<Name>Inactive</Name>
</Status>
</ProductResponse>
Deleting a Product
In case the resource is not found, we can return the 404 Not Found status code. But you might
choose not to return an error and instead return 200 OK. In this example, Ive chosen to inform
the client about the nonexistence of the resource. When returning 201 NoContent, we wont
return any body.
//Deletes a product.
public HttpResult Delete(DeleteProduct request)
{
var domainObject = ProductRepository.GetById(request.Id);
if (domainObject == null)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
else
{
60
ProductRepository.Delete(request.Id);
Response.StatusCode = (int)HttpStatusCode.NoContent;
}
return null;
}
The following code tests the Delete method implemented in the service.
[TestMethod]
public void DeleteProduct_ReturnsNoContent()
{
//ARRANGE --HttpStatusCode statusCode = 0;
const string SITE = "http://localhost:50712";
var client = new JsonServiceClient(SITE)
{
//Grabbing the header once the call is ended.
LocalHttpWebResponseFilter =
httpRes =>
{
statusCode = httpRes.StatusCode;
}
};
//ACT -----client.Delete<HttpResult>("/products/5");
//ASSERT ---Assert.IsTrue(statusCode == HttpStatusCode.NoContent);
}
Order Service
OrderService will implement the following methods:
GET
GET
POST
PUT
DELETE
/orders
/orders/{id}
/orders
/orders/{id}
/orders/{id}
As we did in the ProductService, we will divide the implementation the same way.
61
Service Model
OrderServices service model is a bit more complex since we have a graph of objects that
should mimic in some ways the domain object model. In this example, Ive chosen to use the
Order name for the DTO class when creating and updating an Order rather than the
CreateOrder and UpdateOrder as I did in the previous chapter. This is mainly for brevity. Keep
in mind that you should take care of the naming convention and follow it through all the services
as much as possible (as it would be much easier for clients to understand your services object
model).
The following classes will be used as Request DTOs of the service.
public class GetOrders { }
public class GetOrder
{
public int Id { get; set; }
}
public class DeleteOrder
{
public int Id { get; set; }
}
//Class used for Create and Update.
public class Order
{
public int
Id
public bool
IsTakeAway
public DateTime
CreationDate
public Status
Status
public List<OrderItem> Items
}
{
{
{
{
{
get;
get;
get;
get;
get;
set;
set;
set;
set;
set;
}
}
}
}
}
Almost all of the Service methods will return the OrderResponse object. OrderResponse is a
representation of the Order which is a class within the domain object. But, as we will see, it can
contain some more information. In our case, it contains a list of Links.
62
Route Specification
In the application host, we need to register the various Request DTOs to their relative URLs.
Routes
.Add<GetOrder>
("/orders/{Id}",
.Add<GetOrders> ("/orders",
.Add<Order>
("/orders",
.Add<Order>
("/orders/{Id}",
.Add<DeleteOrder>("/orders/{Id}",
"GET",
"GET",
"POST",
"PUT",
"DELETE",
"Returns
"Returns
"Creates
"Updates
"Deletes
an Order")
Orders" )
an Order")
an Order")
an Order");
63
The OrderMapper implements the IOrderMapper interface which will make it easier to be
injected to the service.
using SrvObj = ServiceStack.Succinctly.ServiceInterface.OrderModel;
using Domain = OrderManagement.Core.Domain;
public interface IOrderMapper
{
Domain.Order ToOrder(SrvObj.Order request);
SrvObj.OrderResponse ToOrderResponse(Domain.Order order);
List<SrvObj.OrderResponse> ToOrderResponseList(List<Domain.Order> orders);
SrvObj.OrderItemResponse ToOrderItemResponse
(int orderId, Domain.OrderItem orderItem);
List<SrvObj.OrderItemResponse> ToOrderItemResponseList
(int orderId, List<Domain.OrderItem> items);
}
64
x.Product.Links.Add(SelfLink(productLInk));
x.Links = new List<Link>();
x.Links.Add(SelfLink(itemsLink));
});
return orderResponse;
}
private Link SelfLink(string uri)
{
return new Link
{
Title = "self",
Rel = "self",
Href = uri
};
}
private Link ParentLink(string uri)
{
return new Link
{
Title = "parent",
Rel = "parent",
Href = uri
};
}
public List<SrvObj.OrderResponse> ToOrderResponseList(List<Domain.Order> orders
)
{
var orderResponseList = new List<SrvObj.OrderResponse>();
orders.ForEach(x => orderResponseList.Add(ToOrderResponse(x)));
return orderResponseList;
}
public SrvObj.OrderItemResponse ToOrderItemResponse(int orderId,
Domain.OrderItem item)
{
var orderItemReponse = Mapper.Map<SrvObj.OrderItemResponse>(item);
var
var
var
var
productId =
orderLink =
itemsLink =
productLink
orderItemReponse.Product.Id;
"orders/{0}".Fmt(orderId);
"/items/{0}".Fmt(item.Id);
= "products/{0}".Fmt(productId);
orderItemReponse.Links.Add(SelfLink(orderLink + itemsLink));
orderItemReponse.Links.Add(ParentLink(orderLink));
orderItemReponse.Product.Links.Add(SelfLink(productLink));
return orderItemReponse;
}
public List<SrvObj.OrderItemResponse>
65
ToOrderItemResponseList(int orderId,
List<Domain.OrderItem> items)
{
var orderItemResponseList = new List<SrvObj.OrderItemResponse>();
items.ForEach(x => orderItemResponseList
.Add(ToOrderItemResponse(orderId, x)));
return orderItemResponseList;
}
}
Validation Implementation
The order validator is simple. It only checks if the order contains a not-empty OrderItems
collection and it will simultaneously check that the CreationDate is not in the future.
public class OrderValidator : AbstractValidator<Order>
{
public OrderValidator()
{
RuleFor(r => r.CreationDate)
.LessThan(DateTime.Now.AddSeconds(10))
.WithMessage("Creation Data shouldn't be in the future");
RuleFor(r => r.Items)
.NotEmpty()
.WithMessage("Order Items should be specified");
}
}
66
//Orders
.Add<GetOrder>("/orders/{Id}", "GET", "Returns an Order")
.Add<GetOrders>("/orders", "GET", "Returns Orders")
.Add<Order>("/orders", "POST", "Creates an Order")
.Add<Order>("/orders/{Id}", "PUT", "Updates an Order")
.Add<DeleteOrder>("/orders/{Id}", "DELETE", "Deletes an Order");
Plugins.Add(new ValidationFeature());
}
public override void Configure(Container container)
{
//Product dependencies
container.Register<IProductRepository>(new ProductRepository());
container.Register<IProductMapper>(new ProductMapper());
//Orders dependencies
container.Register<IOrderRepository>(new OrderRepository());
container.Register<IOrderMapper>(new OrderMapper());
container.Register<IStatusRepository>(new StatusRepository());
//Validators
container.RegisterValidator(typeof (CreateProductValidator));
container.RegisterValidator(typeof (UpdateProductValidator));
container.RegisterValidator(typeof (OrderValidator));
}
}
The highlighted code is what is needed for the OrderService. As we may see, it is just a
natural progression by following the same style of coding and service creation.
Service Implementation
OrderService implements two Get methods, one Post, one Put, and one Delete.
As shown in the following code example, OrderService has four public properties, namely
OrderMapper, OrderRepository, ProductRepository, and StatusRepository, which will
hold the instances that will be injected at run time by the IoC container.
public class OrderService : ServiceStack.ServiceInterface.Service
{
public IOrderRepository
OrderRepository
{ get; set; }
public IProductRepository ProductRepository { get; set; }
public IStatusRepository StatusRepository { get; set; }
public IOrderMapper
OrderMapper
{ get; set; }
//Returns all the orders.
public OrdersResponse Get(GetOrders request) { }
//Returns a single order.
67
In order to call the GET service method used in the previous example, we will use the
XmlServiceClient which, by default, would communicate with the Content-Type:
application/xml. The following example shows a test that uses the built-in ServiceStack client
to perform a GET on the /orders/{Id} URI.
[TestMethod]
public void GetOrdersByOrderId()
{
//ARRANGE ---
68
To call GET /orders (to get the list of available orders), the client code would look like the
following example.
[TestMethod]
public void GetAllOrders()
{
//ARRANGE --var client = new XmlServiceClient("http://localhost:50712/");
//ACT -----var orders = client.Get<OrdersResponse>("/orders");
//ASSERT ---Assert.IsTrue(orders != null);
Assert.IsTrue(orders.Orders.Count > 0);
}
69
To insert a new Order using the ServiceStack client, we will test that the response is returning
the 201 Created HTTP status and that the Location header has been filled.
[TestMethod]
public void CreateNewOrder()
{
//ARRANGE --WebHeaderCollection headers = null;
HttpStatusCode statusCode = 0;
const string SITE = "http://localhost:50712";
70
After the Order is created, the following XML will be generated and headers will be returned to
the client.
HTTP/1.1 201 Created
Cache-Control: private
Content-Type: application/xml
Location: http://localhost:50712/orders/5
Server: Microsoft-IIS/8.0
X-Powered-By: ServiceStack/3.956 Win32NT/.NET
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 14 Aug 2013 22:13:31 GMT
Content-Length: 722
<OrderResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CreationDate>2013-08-13T00:00:00</CreationDate>
71
<Id>5</Id>
<IsTakeAway>false</IsTakeAway>
<Items>
<OrderItemResponse>
<Id>8</Id>
<Links>
<Link>
<Href>orders/5/items/8</Href>
<Rel>self</Rel>
<Title>self</Title>
<Type i:nil="true" />
</Link>
</Links>
<Product>
<Id>1</Id>
<Links>
<Link>
<Href>products/1</Href>
<Rel>self</Rel>
<Title>self</Title>
<Type i:nil="true" />
</Link>
</Links>
<Name>Pizza</Name>
<Status>
<Id>1</Id>
<Name>Active</Name>
</Status>
</Product>
<Quantity>10</Quantity>
</OrderItemResponse>
</Items>
<Links>
<Link>
<Href>orders/5</Href>
<Rel>self</Rel>
<Title>self</Title>
<Type i:nil="true" />
</Link>
</Links>
<Status>
<Id>1</Id>
<Name>Active</Name>
</Status>
</OrderResponse>
Updating an Order
To update an Order, our service will implement the PUT method, which will accept the Order
message.
72
If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes
should be sent to indicate successful completion of the request. In our case, if everything goes
well, we dont specify anything because 200 OK is the default response. If the Order doesnt
exist, Ive chosen to return a Not Found message.
//Updates an existing order /orders/{id}.
public OrderResponse Put(Order request)
{
var domainObject = OrderRepository.GetById(request.Id);
if (domainObject == null)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return null;
}
var updatedOrder = OrderMapper.ToOrder(request);
updatedOrder.Status = StatusRepository.GetById(request.Status.Id);
updatedOrder.Items.ForEach(x =>
{
x.Product = ProductRepository.GetById(x.Product.Id);
});
//Store data to database.
var order = OrderRepository.Update(updatedOrder);
//Transform to OrderResponse and return.
return OrderMapper.ToOrderResponse(order);
}
To test the Order update, we will change a couple of fields: CreationDate, IsTakeAway,
Quantity, and the Product associated with the OrderItem with an Id of 6.
In the assert section, we will check that the newly created object has the values assigned to the
updated Order.
[TestMethod]
public void UpdateOrder()
{
//ARRANGE --HttpStatusCode statusCode = 0;
const string SITE = "http://localhost:50712";
const string ORDERS_LINK = "/orders/1";
const string URI = SITE + ORDERS_LINK;
const int NEW_PRODUCT_ID = 5;
DateTime NEW_CREATION_DATE = new DateTime(2013, 08, 08);
var client = new XmlServiceClient(SITE)
{
//Grabbing the header once the call is ended.
LocalHttpWebResponseFilter =
httpRes =>
73
{
statusCode = httpRes.StatusCode;
}
};
var updateOrder = new Order
{
CreationDate = NEW_CREATION_DATE,
IsTakeAway = false,
Items = new List<OrderItem>()
{
new OrderItem
{
Id = 6,
//Setting a different product!
Product = new Product {Id = NEW_PRODUCT_ID},
//Setting a different quantity.
Quantity = 100
}
},
Status = new Status {Id = 1}
};
//ACT -----var orderResponse = client.Put<OrderResponse>(ORDERS_LINK, updateOrder);
//ASSERT ---Assert.IsTrue(statusCode == HttpStatusCode.OK);
Assert.IsTrue(orderResponse.CreationDate == NEW_CREATION_DATE);
Assert.IsTrue(orderResponse.IsTakeAway == false);
Assert.IsTrue(orderResponse.Items.Count == 1);
Assert.IsTrue(orderResponse.Items[0].Product.Id == NEW_PRODUCT_ID);
}
When testing the data in the browser by accessing GET /Orders/6, we can see that the Order
has been updated.
Deleting an Order
When deleting an Order, the important thing is to return the No content status after the
deletion has taken place. When returning the No content status, the message body shouldnt
be returned back to the client.
public HttpResult Delete(DeleteOrder request)
{
var domainObject = OrderRepository.GetById(request.Id);
if (domainObject == null)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
}
74
else
{
//Delete order in the database.
OrderRepository.Delete(request.Id);
Response.StatusCode = (int)HttpStatusCode.NoContent;
}
//Not returning any body!
return null;
}
The following code tests the Delete method implemented in the service.
[TestMethod]
public void DeleteOrder()
{
//ARRANGE --HttpStatusCode statusCode = 0;
const string SITE = "http://localhost:50712";
var client = new XmlServiceClient(SITE)
{
//Grabbing the header once the call is ended.
LocalHttpWebResponseFilter =
httpRes =>
{
statusCode = httpRes.StatusCode;
}
};
//ACT -----client.Delete<HttpResult>("/orders/2");
//ASSERT ---Assert.IsTrue(statusCode == HttpStatusCode.NoContent);
}
OrderItem Service
OrderItemService will implement the following methods:
GET
GET
/orders/1234/items
/orders/1234/items/{id}
With this example, I want to show how to create a service that will enable navigating complex
properties of a parent resource. While we can implement the creation or updating of an existing
OrderItem, I think that is not as important since we have already seen how to create or update
a resource in the previous two services. So, Ive intentionally omitted the POST, PUT, and
DELETE verbs for the OrderItem.
75
Service Model
OrderItemServices service model is simple. The following classes will be used as the
Request DTOs of the service.
public class GetOrderItem
{
public int OrderId { get; set; }
public int ItemId { get; set; }
}
public class GetOrderItems
{
public int OrderId { get; set; }
}
We will reuse the OrderItemResponse that we have previously created for the OrderService
because it perfectly suits the need, and create a new class, OrderItemsResponse, that will
carry on a list of OrderItem responses.
public class OrderItemsResponse
{
public List<OrderItemResponse> Items { get; set; }
}
Route Specification
In the application host, we need to register the various Request DTOs to their relative URIs.
Routes
.Add<GetOrderItem>("/orders/{OrderId}/items/{ItemId}","GET"," Get an OrderItem")
.Add<GetOrderItems>("/orders/{OrderId}/items", "GET", "Get a list of OrderItems");
Mapper Implementation
We will reuse the OrderMapper because it implements all of the necessary mappings.
Service Implementation
The following code example shows the public members of the OrderItemService. As in
previous examples, public members will be injected by the IoC.
public interface IOrderItemService
76
{
public
public
public
public
77
Assert.IsNotNull(items != null);
Assert.IsNotNull(items.Items);
Assert.IsTrue(items.Items.Count == 2);
}
And it contains the test code that fully tests the URI GET /orders/1/items/2.
[TestMethod]
public void GetOrderItemByOrderId()
{
//ARRANGE --var ITEM_ID = "2";
var client = new XmlServiceClient("http://localhost:50712/");
string ITEM_LINK = "/orders/1/items/2";
//ACT -----var item = client.Get<OrderItemResponse>(ITEM_LINK);
//ASSERT ---Assert.IsNotNull(item != null);
Assert.IsTrue(item.Id.ToString() == ITEM_ID);
}
78
Conclusion
In this chapter, we have seen the implementation of all the verbs for the three web services as
well as how to wire the requests to the right call.
We were able to test the implementation by using the unit tests, and we have seen how to use
the JsonServiceClient and the XmlServiceClient to call the newly implemented services.
79
Chapter 6 Pagination
It's almost always a bad idea to return every available resource to the client at once. A
technique that is usually referred to as pagination (or sometimes as paging mechanism,
especially on webpages) is used for displaying a limited number of results where a returned
data set is too big to be displayed in one place or at once.
Usually whenever pagination is implemented, this is followed by the information on how to get
the next result set or how to get to the beginning of the list. RESTful services are not much
different in this sense. It is possible to create a representation of resources by using pagination.
Pagination as a Representation
When building REST web services, we usually think about resources. The first question to pose
is if the page or pagination is a resource on its own. In my opinion it is not, and, instead, the
information about the pagination is just an integrated part of the resource itself. In other words,
the information about how to page through, lets say a collection of Products, will be embedded
in the Product collection itself.
There are mainly three locations to place the information about pagination:
As previously mentioned, the first option is not the best idea because pagination is not a
resource (this option, through the URI, looks like a page is a resource). So, I would generally
skip this way of representing the pagination.
The second option represents a standard way of solving this problem as it enables the encoding
of paging application directly in the query string. In our examples, we are going to use this
approach.
http://ordermanagement.com/
products
?page=1&size=100&format=xml
resource
Query String
22
80
In this chapter, we are going to see how to implement pagination for the already existing
Products that we have implemented in the ProductService. The pagination makes sense only
when returning a list of objects and, therefore, the perfect candidate for us would be the GET
/products method which returns the list of available products.
Query String
There are several ways and styles of expressing the paging components when it comes to the
naming convention for the query string parameters. In some examples, you will see offset and
limit being mentioned. In other examples, you will see skip and take. But I will use a more
intuitive and simple way, which is page and size:
Discoverability
In order to inform the client application that there is a way to navigate through pages, we will
use the Links section. Discoverability is part of the aforementioned HATEOAS constraint. The
basic idea is the service exposes links to resources available. In our case, they are next,
previous, first, and last so that the client is able to discover the URI associated with the resource
and to further navigate to the resource. It is a bit similar to navigating a webpage through a
browser. The links are the shown to us and, by following the links, we can discover new
resources (in this case, pages).
Actually, the logic is straightforward; the client will know which URI is available through the
Link.Rel attribute.
Pagination links
Link.Rel
next
Description
Goes to the next page.
81
first
last
Service Model
You might remember the GetProducts object that was used to retrieve a list of Products. We
will expand this object to contain the two aforementioned query string parameters, and we will
add a List<Link> property to the ProductsResponse.
public class GetProducts
{
public int? Page { get; set; }
public int? Size { get; set; }
}
public class ProductsResponse
{
public ProductsResponse()
{
Links = new List<Link>();
}
public List<ProductResponse> Products { get; set; }
public List<Link> Links { get; set; }
}
public class ProductResponse
{
public ProductResponse()
{
Links = new List<Link>();
}
public
public
public
public
23
82
{
public bool HasNext { get; set; }
public bool HasPrevious { get; set; }
public int Page { get; set; }
public int Size { get; set; }
public long LastPage()
{
long lastPage = (Count/Size);
if ((lastPage*Size) < Count) { lastPage++;}
return lastPage;
}
public long Count { get; set; }
public IList<TEntity> Entities { get; set; }
}
83
};
}
Product Mapper
As shown previously when we looked at the ProductService, we will extend the
ProductMapper class with a new method called ToProductsResponse() to which we will pass
the PagedListResult<Product> structure. I have omitted the actual Links generation logic to
keep the code succinct; I show only the NextLink() method. But the important thing to note
here is that we will generate extra links as explained in the Discoverability section.
public ProductsResponse ToProductsResponse(PagedListResult<Product> products)
{
var productList = ToProductResponseList(products.Entities.ToList());
var productResponse = new ProductsResponse();
productResponse.Products = productList;
SelfLink(products, productResponse);
NextLink(products, productResponse);
PreviousLink(products, productResponse);
FirstLink(products, productResponse);
LastLink(products, productResponse);
return productResponse;
}
private void NextLink(PagedListResult<Product> products,
ProductsResponse productResponse)
{
if (products.HasNext)
{
productResponse
.Links.Add(NewLink("next", "products?page={0}&size={1}"
.Fmt(products.Page + 1, products.Size)));
}
}
Service Implementation
Getting the list of products now requires two scenarios. In the first scenario, we want the paging,
and in the second scenario, all the data is returned. This is not necessarily a real-world
scenario, but I think that it is important to show this kind of implementation as well. If the page
and size parameters are not supplied, the Get method will return the full list of products.
Otherwise, the paging will kick in.
84
And finally, when retrieving data, we can see that the links get updated every time there are
available products. The response of the web service would look like the following if we were
requesting GET /products?page=1&size=100.
<ProductsResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Links>
<Link>
<Href>products?page=1&size=100</Href>
<Rel>self</Rel>
<Title>self</Title>
<Type i:nil="true"/>
</Link>
<Link>
<Href>products?page=2&size=100</Href>
<Rel>next</Rel>
<Title>next</Title>
<Type i:nil="true"/>
</Link>
<Link>
<Href>products?page=1&size=100</Href>
<Rel>first</Rel>
<Title>first</Title>
<Type i:nil="true"/>
</Link>
<Link>
<Href>products?page=10&size=100</Href>
<Rel>last</Rel>
85
<Title>last</Title>
<Type i:nil="true"/>
</Link>
</Links>
<Products><!--omitted for brevity--></Products>
</ProductsResponse>
86
Chapter 7 Authentication
We have seen how to create the service, and now we want to control access to it. In this
chapter, we will look at some options for securing a web application from unauthorized users.
Note: Authentication is all about knowing the identity of the user.
Authorization is deciding whether or not a user is allowed to perform an action.
Authentication providers
Authentication session cache clients
User authentication repository
Authentication services
Domain object model: AuthUserSession, UserAuth, and UserOAuthProvider
24
25
87
Request
Authentication Provider
(IAuthProvider)
Session Cache
(ICacheClient)
Has Session?
Authentication Repository
(IUserAuthRepository)
OAuth/OpenID
Where do we start? As we have already seen several times, everything starts in the application
host configuration where we need to enable the authentication feature. Before going into details
and explaining all of the aforementioned components, lets take a look at the minimum
configuration needed in order to run the Basic Authentication with the OrmLite
Repository.
public override void Configure(Container container)
{
//1. Registering the authorization provider.
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[]
{
new BasicAuthProvider()
}));
//2. Enabling the /register service.
Plugins.Add(new RegistrationFeature());
//3. Configuring the repository that uses a SQL Server back-end.
var connString = "Data Source=;Initial Catalog=;User ID=;password=";
var factory = new OrmLiteConnectionFactory(
connString,
SqlServerOrmLiteDialectProvider.Instance);
var ormLiteRepository = new OrmLiteAuthRepository(factory);
//Registering the repository.
container.Register<IUserAuthRepository>(ormLiteRepository);
//Should be run only once as this creates the necessary tables in the db.
//There is a possibility to DropAndReCreate the entire table.
ormLiteRepository.CreateMissingTables();
//ormLiteRepository.DropAndReCreateTables();
88
Authenticate Attribute
For ServiceStack to know which service requires authentication, it has to be marked with the
Authenticate attribute.
[Authenticate (ApplyTo.Get | ApplyTo.Post]
public class ProductService : ServiceStack.ServiceInterface.Service
{
//..
}
By default, all of the verbs of the service marked with the Authenticate attribute require
authentication. However, it is possible to refine the control of what verb needs authentication by
specifying the list of verbs directly in the attribute declaration (e.g., ApplyTo.Get).
89
OrmLiteRepository
OrmLiteRepository is the built-in authentication repository that uses the database as the
storage for the authentication. The repository needs the OrmLiteConnectionFactory object
which simply defines the connection string and the actual type of the database being used.
If instructed to do so, the repository will create all of the necessary tables in the database when
calling the CreateMissingTables method. It is also possible to use DropAndReCreateTables.
Both options are very useful when doing unit testing or on the applications first run, but I would
suggest you avoid using this in a production system.
In order for the example to be realistic, we create a simple user so that when the application
starts, we will have at least one user in the system that will be used to test the authentication.
Authentication Providers
Authentication providers constitute the heart of the authentication functionality; they define the
actual type of the authentication to be used. The authentication provider communicates with the
authentication repository in order to retrieve the information about the user or it communicates
directly with the cache in order to retrieve information about the cached session.
As shown in the following class diagram, the main interface from which every provider inherits is
the IAuthProvider, and the abstract base class that implements this interface is
AuthProvider.
90
Provider
Description
Basic authentication
Digest authentication
Credentials authentication
OAuth26 providers
OpenID27 providers
26
27
91
Extensibility
By inheriting from the CredentialsAuthProvider class, ServiceStack offers a great
extensibility point for creating a customized authentication provider based on the username and
password. This is as easy as overriding the TryAuthenticate method.
public class MyCustomAuthorizationProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService,
string userName, string password)
{
//Your implementation here.
}
}
The following table details a few other implementations that can be downloaded separately and
are available on NuGet.
92
Repository (Database)
NuGet package
MongoDB
RavenDB
NHibernate
93
Services
There are mainly four services exposed that are related to the authentication, and as shown in
the following figure, all inherit from the Service class which makes those very ordinary
ServiceStack services.
The following table contains the list of available services with their description and URI.
Authentication services explained
Service
AuthService
Exposed Path
/auth/{provider}
Description
Executes the authentication and returns the currently
logged user. The {Provider} parameter defines the
name of the provider, and it can be one of the following.
URI
/auth/credentials
/auth/basic
/auth/twitter
/auth/facebook
/auth/yammer
/auth/googleopenid
/auth/yahooopenid
/auth/myopenid
/auth/openid
Provider Class
CredentialsAuthProvider
BasicAuthProvider
TwitterAuthProvider
FacebookAuthProvider
YammerAuthProvider
GoogleOpenIdOAuthProvider
YahooOpenIdOAuthProvider
MyOpenIdOAuthProvider
OpenIdOAuthProvider or any
custom OpenID provider
AssignRolesService
/assignroles
UnAssignRolesService
/unassignroles
RegistrationService
/register
94
The registration service can be enabled or disabled through the application host by configuring
the Registration feature.
Plugins.Add(new RegistrationFeature());
The following test method is doing exactly the same thing but using the ServiceStack client.
[TestMethod]
public void GetProductAndAuthenticateByUsingServiceStackClient()
{
//ARRANGE --var client = new JsonServiceClient("http://localhost:50712/" );
client.UserName = "johnd";
client.Password = "mypassword";
//ACT -----var product = client.Get<ProductResponse>("/products/1");
//ASSERT ---Assert.IsTrue(product != null);
}
95
For the full implementation, check the ProductData.html file provided in the sample project,
available at https://bitbucket.org/syncfusiontech/servicestack-succinctly.
<script type="text/javascript">
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
function authenticate() {
//Getting the username and password.
var username = $("input#username").val();
var password = $("input#password").val();
$.ajax({
type: "GET",
url: "/products/1",
async: false,
dataType: 'json',
beforeSend: function (xhr) {
xhr.setRequestHeader('Authorization', make_base_auth(username,
password));
},
success: function (result) { //Displaying the values.
$('#product_id').val(result.Id);
$('#product_name').val(result.Name);
},
});
}
</script>
96
UserName = "JaneR",
FirstName = "Jane",
LastName = "Roe",
DisplayName = "Jane Roe",
Email = "[email protected]",
Password = "somepassword",
AutoLogin = true
});
var response = client.Post<UserResponse>(request);
Assert.IsTrue(response != null);
Assert.IsTrue(response.Data.UserId != null);
}
public class UserResponse
{
public string UserId { get; set; }
public string SessionId { get; set; }
public string UserName { get; set; }
}
97
Note: As unassigning roles follow the same structure, the actual code example is omitted.
Authorization
Together with authentication, ServiceStack also offers an authorization mechanism. There is a
way to control which Role or Permission is allowed to execute a certain service or method.
We have seen that it is possible to register a user and, consequently, to assign roles and
permissions. Therefore, whenever the user authenticates, all of this information will be available
in the session.
[Authenticate]
public class ProductService : ServiceStack.ServiceInterface.Service
{
[RequiredRole(RoleNames.Admin)]
[RequiredPermission("some_permission")]
public ProductResponse Get(GetProduct request) { }
//To execute this method a user has to be authenticated.
public List<ProductResponse> Get(GetProducts request) { }
}
//This is the equivalent declaration.
[Authenticate]
[RequiredRole(RoleNames.Admin)]
[RequiredPermission("some_permission")]
public class GetProduct { }
98
[Authenticate]
public class GetProducts { }
In case the user doesnt have the correct Role or Permission assigned, the 403 Invalid
Role or 403 Invalid Permission statuses will be returned.
99
Chapter 8 Caching
In Chapter 1, we briefly mentioned that ServiceStack contains built-in support for server-side
caching.
Client
Application
Database
Client
Application
Service
GET|POST|PUT|DELETE
Cache:
In Memory
Redis
Memcached...
Client
Application
ServiceStack provides ICacheClient, a clean caching interface, for a number of different cache
providers:
Which cache provider you use depends mainly on the needs of your application.
MemCached, Azure, or AwsDynamoDb providers need to be installed separately through
NuGet.
28
29
100
Configuring Caching
The ICacheClient interface is the starting point for all of the supported caching options as it
defines the basic interface that every cache provider has to implement.
To start using the caching mechanism, it is as simple as registering the provider in the
application host. Once registered, an instance of the ICacheClient will be injected to the
service at run time, and will be available as a Cache property, which is part of the ServiceBase
class.
The following example shows how to configure the various providers in the application host.
public override void Configure(Container container)
{
//MemoryCacheClient
container.Register<ICacheClient>(new MemoryCacheClient());
//Redis
container.Register<IRedisClientsManager>
(c => new PooledRedisClientManager("<redis_address_here>"));
container.Register
(c => c.Resolve<IRedisClientsManager>().GetCacheClient())
.ReusedWithin(ReuseScope.None);
//MemCached
container.Register<ICacheClient>
(new MemcachedClientCache(new[] {"<memcached_host>"}));
//Azure
container.Register<ICacheClient>(new AzureCacheClient("CacheName"));
//AWS DynamoDB
container.Register<ICacheClient>(new DynamoDbCacheClient(...))
}
Response Caching
To demonstrate how to use caching, we will reuse our ProductService. One important thing to
note here is that the cache will contain the full response rather than the Response DTO itself.
In order to support this scenario, we have to change the output of the method to object instead
of the existing ProductResponse. We should know two things about caching:
101
Cache is basically a dictionary and it needs a unique key to identify a cached object.
The key is a string and it can be generated by the UrnId helper class.
RequestContext.ToOptimizedResultUsingCache is the method responsible for
returning data from the cache.
The following code example contains the modified Get() method. The highlighted code will be
executed in case nothing is found in the cache with the given key (this always happens the first
time a particular item is requested from the cache) and, for all the subsequent calls, it will be
ignored.
public object Get(GetProduct request)
{
var key = UrnId.Create<GetProduct>(request.Id.ToString());
var cachedProduct =
RequestContext.ToOptimizedResultUsingCache(this.Cache, key, () =>
{
var product = ProductRepository.GetById(request.Id);
if (product == null)
{
Response.StatusCode = (int) HttpStatusCode.NotFound;
return default(ProductResponse);
}
return ProductMapper.ToProductResponse(product);
});
return cachedProduct;
}
102
103
Chapter 9 Logging
ServiceStack provides a logging framework that exposes the ILog interface and adapters for
the major Microsoft .NET logging providers (log4net, NLog, etc.).
By depending on the ILog interface, the web service and the application logic are free from any
direct dependency to any external logging library, which then can be injected at run time.
The following NuGet packages are available for download:
ServiceStack.Logging.NLog
ServiceStack.Logging.Elmah
ServiceStack.Logging.Log4Net
ServiceStack.Logging.Log4Netv129
ServiceStack.Logging.EventLog
ServiceStack, however, contains some basic logger implementation as shown in the following
figure.
104
log4net Integration
log4net is probably the most often used logging library in the Microsoft .NET world. Many
developers are familiar with its configuration and usage. In this chapter, we will see how to
configure and start using the logging feature as well as how to use the log4net provider adapter.
To start using the log4net, the first thing to do is to install a package from NuGet:
ServiceStack.Logging.Log4Net.
In the application host configuration, we need to register the fact that we want to use the log4net
provider. This is done by creating a new instance of the Log4NetFactory and assigning the
object to the LogManager.LogFactory.
public override void Configure(Container container)
{
LogManager.LogFactory = new Log4NetFactory(true);
}
To get an instance of the logger that we just registered in the application host configuration, we
will use the LogManager.GetLogger() method.
public class ProductService : ServiceStack.ServiceInterface.Service
{
private ILog Logger { get; set; }
public ProductService()
{
Logger = LogManager.GetLogger(GetType());
}
public ProductResponse Get(GetProduct request)
{
Logger.Debug("Getting the product");
return new ProductResponse();
}
}
105
By changing the ILog (logger) property to become public, the IoC container will inject the
instance at service creation.
public class ProductService : ServiceStack.ServiceInterface.Service
{
public ILog Logger { get; set; }
public ProductResponse Get(GetProduct request)
{
Logger.Debug("Getting the product");
return new ProductResponse();
}
}
RequestLogsFeature Plug-in
In addition to the aforementioned possibility of enabling the logging by exposing the ILog
interface, ServiceStack contains a built-in plug-in that enables configurable, in-memory tracking
of recent requests and error responses.
To enable the plug-in, we have to register the RequestLogsFeature by adding it to the plug-ins
list in the application host.
public override void Configure(Container container)
{
Plugins.Add(new RequestLogsFeature()
{
RequiredRoles = new string[]{}
});
}
106
Property
AtRestPath
Default value: /requestlogs
EnableSessionTracking
Default value: False
EnableRequestBodyTracking
Description
The location at which the logging output can be
queried. Being a configurable option, one can set
a new location (e.g., /lastrequests).
Tracks the information about the current user
session. The log will contain the information
exposed by the implementor of the
IAuthSession mentioned in Chapter 7.
Enables the tracking of the raw body of the
request (as in the request header).
RequiredRoles
Default value: Admin
RequestLogger
Default value: Instance of
InMemoryRollingRequestLogger
107
Property
ExcludeRequestDtoTypes
Description
Contains a list of DTOs for which we dont want to
track the logging.
Default value:
typeof (Auth), typeof (Registration)
InMemoryRollingRequestLogger
InMemoryRollingRequestLogger is a built-in class that implements the IRequestLogger
interface (IRequestLogger interface is only used in the RequestLogs plug-in). As its name
suggests, the logger implements the tracking and stores the values in memory as
RequestLogEntry entries.
The list of RequestLogEntry values is returned in the /requestlog URI. The
RequestLogEntry class is defined as follows.
public class RequestLogEntry
{
public long Id { get; set; }
public DateTime DateTime { get; set; }
public string HttpMethod { get; set; }
public string AbsoluteUri { get; set; }
public string PathInfo { get; set; }
public string RequestBody { get; set; }
public object RequestDto { get; set; }
public string UserAuthId { get; set; }
public string SessionId { get; set; }
public string IpAddress { get; set; }
public string ForwardedFor { get; set; }
public string Referer { get; set; }
public Dictionary<string, string> Headers { get; set; }
public Dictionary<string, string> FormData { get; set; }
public Dictionary<string, object> Items { get; set; }
public object Session { get; set; }
public object ResponseDto { get; set; }
public object ErrorResponse { get; set; }
public TimeSpan RequestDuration { get; set; }
}
And, when displayed in the browser, it looks like the following figure.
108
109
Chapter 10 Profiling
Another built-in feature of ServiceStack is a profiler. The most common use of profiling
information is to aid in finding bottlenecks and, obviously, in helping with
application optimization. With a profiler, we are able to find out the time needed to perform a
specific action or a sequence of actions.
Internally, ServiceStack uses a slightly changed version of the MVC MiniProfiler,30 which is
adapted for ServiceStacks needs. The MiniProfiler on its own provides support for OrmLite,
which enables you to see the timing for actual database queries. We will see this later.
30
110
When running the application in the browser, we can directly see (and expand in the top right
corner) the statistics related to the current request.
Very interesting statistics can be seen regarding the serialization, deserialization, and the actual
execution time of the Service method itself.
111
When executing the query, we can see in the browser something similar to the following figure,
where we can see the timing for each of the SQL queries executed.
In the output, we can see that Returning the product response is shown with the execution
time.
112
113
Filters
Filters are a simple concept; they allow us to execute some code before or after a service
executes. Filters can be seen as a cross-cutting concern and allow a great deal of reusability of
code. For instance, monitoring, authorization, authentication, logging, transaction processing,
and validation can be implemented by filters. This is a great way of keeping the service code as
clean as possible without implementing these same concepts over and over again.
There are two types of filters: Request filters and Response filters. We have seen this when we
mentioned the Request and Response pipeline. A particularity of a filter is that it can have a
priority, which means that we can control the order in which filters are going to be executed.
Response DTO
Request Filters
Request DTO
Request
Filter
0
Request
Filter
1
Response Filters
Request
Filter
2
Service
Response
Filter
0
Request
Filter
1
Request
Filter
2
Response DTO
Request DTO
Filters are applied as attributes to a Request and Response DTO or, alternatively, to the
service itself. There are some predefined ServiceStack filters that are executed before the userapplied ones. This default behavior can be changed if the priority of a user filter is set to a
negative number, in which case it gets executed beforehand.
In the example, we will see how to create a Request and a Response filter and how to apply it to
the service. The Request filter will count how many times the GET method has been called, and
the Response filter will record how many successful requests have been processed. This
recorded information will be exposed through a service called StatsCounterService.
114
Request Filters
The Request filters are executed before the service gets called. There are two ways of declaring
the new Request filter, either by implementing the IHasRequestFilter interface or by using a
RequestFilterAttribute base class. Using the RequestFilterAttribute is simpler as it
already implements the basic functionalities; we only need to override the Execute() method.
In the following example, we will create the ServiceUsageStatsRequestFilter class that
counts the number of times the Get() method is called. To store the data, we will use the
MemoryCacheClient (ICacheClient) that will be injected at run time, hence the property called
Cache. The filter also exposes the ServiceName property that should be specified every time
the attribute is assigned to a service.
public class ServiceUsageStatsRequestFilter : RequestFilterAttribute
{
public string ServiceName { get; set; }
//This property will be resolved by the IoC container.
public ICacheClient Cache { get; set; }
public override void Execute(IHttpRequest req, IHttpResponse res,
object requestDto)
{
string getKey = ServiceName + "_GET";
if (req.HttpMethod == "GET")
{
var item = Cache.Get<long>(getKey);
Cache.Set(getKey, (item + 1));
}
}
}
Response Filters
The Response filters are executed after the service has been executed. As for the Request
filter, there are two ways of declaring the new Response filter: either by implementing the
IHasResponseFilter interface or by using a ResponseFilterAttribute-derived attribute.
In the following example, we will create the ServiceUsageStatsRequestFilter class that
counts the number of times a response status has been in the range of 200which would
generally mean that the call has been successful.
As for the Request filter, the same logic applies when it comes to the Cache and ServiceName
declaration.
public class ServiceUsageStatsResponseFilter: ResponseFilterAttribute
{
115
Applying Filters
In order to enable the filters, we have to add the attributes to the service itself by applying the
attributes as follows.
[ServiceUsageStatsRequestFilter(ServiceName = "ProductService")]
[ServiceUsageStatsResponseFilter(ServiceName = "ProductService")]
public class ProductService : ServiceStack.ServiceInterface.Service
{
In this case, by default, filters will be called for every method call (GET, POST, PUT, etc.).
If we would like to limit the filter execution to only a certain method, we can use the ApplyTo
switch.
[ServiceUsageStatsRequestFilter(ApplyTo = ApplyTo.Get | ApplyTo.Put,
ServiceName = "ProductService", Priority = 1)]
[ServiceUsageStatsResponseFilter(ServiceName = "ProductService")]
public class ProductService : ServiceStack.ServiceInterface.Service
{
Another way of enabling a filter is by attaching it directly to the Request and Response DTO.
[ServiceUsageStatsRequestFilter(ServiceName = "ProductService")]
public class GetProduct
{
116
117
[
{
{
"ServiceName":"ProductService_GET", "Value": 1 },
"ServiceName":"ProductService_SUCCESSFUL", "Value": 1 }
Plug-ins
As filters, plug-ins are used to extend ServiceStack. Unlike filters, they are meant to live within
the request and response pipeline. Plug-ins can enhance the functionality of ServiceStack, for
example, by enabling new content type handlers, adding new services, and automatically
registering certain filters to be executed for each request.
The following is a list of predefined plug-ins already available in ServiceStack that can be
enabled or disabled:
Plug-in Creation
In order to create a plug-in, ServiceStack exposes an IPlugin interface that a plug-in has to
implement. The IPlugin interface is defined as follows.
public interface IPlugin
{
void Register(IAppHost appHost);
}
118
As you can see, the implementation is simple. In the GET method, we iterate through the list of
the currently registered plug-ins and return it as List<RegisteredPlugin>.
119
Plug-in Registration
In order to be available to the system, the plug-in has to be registered. The registration is done
in the application host (as we have seen in previous chapters) by using the Plugins collection.
public override void Configure(Container container)
{
Plugins.Add(new RegisteredPluginsFeature());
}
Plug-in Usage
When running the application and pointing to the predefined plug-in URI /ListPlugins (in our
case, http://localhost:50712/ListPlugins?format=xml), we can see the list of plug-ins
registered in the system.
<ArrayOfRegisteredPlugin xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/ServiceStack.Succinctly.Host.Plugin
s">
<RegisteredPlugin>
<Name>HtmlFormat</Name>
</RegisteredPlugin>
<RegisteredPlugin>
<Name>CsvFormat</Name>
</RegisteredPlugin>
<RegisteredPlugin>
<Name>MarkdownFormat</Name>
</RegisteredPlugin>
<RegisteredPlugin>
<Name>PredefinedRoutesFeature</Name>
</RegisteredPlugin>
<RegisteredPlugin>
<Name>MetadataFeature</Name>
</RegisteredPlugin>
<RegisteredPlugin>
<Name>RequestInfoFeature</Name>
</RegisteredPlugin>
<RegisteredPlugin>
<Name>RegisteredPluginsFeature</Name>
</RegisteredPlugin>
</ArrayOfRegisteredPlugin>
As you can see, the new URI can be queried as any other service we have seen in the previous
examples, and it can return a result in XML, JSON, or any other format supported by the
framework.
120
Metadata page
Swagger integration32
Metadata Page
We have already mentioned that there is a metadata page that is automatically generated by
the framework; it includes the main information about the operations and types included in the
service.
In order for the metadata page to display the information required, the main source of
information is the RouteAttribute. The same applies to the Route definition in the application
host as the two offer the same functionality.
RouteAttribute provides five properties that can be set. The following is the public API of the
RouteAttribute definition.
public class RouteAttribute : Attribute
{
public object TypeId { get; set; }
public string Path { get; set; }
public string Verbs { get; set; }
public string Notes { get; set; }
public string Summary { get; set; }
}
However, there is support for the new attributes Api, ApiResponse, ApiMember, and
ApiAllowableValues which are now available to further enrich the objects. The new attributes
will be mainly used when integrated with Swagger but, as we will see, some of them are
available in the metadata page too.
31
32
121
WADL: http://en.wikipedia.org/wiki/Web_Application_Description_Language
Swaggers official webpage: https://developers.helloreverb.com/swagger/
Once we run the application in the metadata page, we can see all of the information in the
previous code example. As shown in the following figure, all of the information is available.
At the time of writing, ApiResponse and ApiAllowableValues are not displayed in the
metadata page.
ApiResponse allows you to specify the different error response statuses that the service can
return. It can be declared several times, once for each error.
The ApiAllowableValues attribute allows you to specify an allowable minimum and maximum
numeric range, a list of named values, an Enum of named values, or a custom factory that
returns a list of names.
122
Swagger Integration
The following description of Swagger is taken directly from the company webpage:
Swagger is a specification and complete framework implementation for describing, producing,
consuming, and visualizing RESTful web services.
ServiceStack has an API that enables integration with the Swagger framework. You can
integrate Swagger in a matter of minutes.
1. Install the ServiceStack.Api.Swagger NuGet package. This will create a swagger-ui
folder where the Swagger JavaScript and HTML page is stored. Additionally, this will
create two new services: /resources and /resource/name*. These two services are
the data source for the Swagger UI.
2. Configure the application host by adding the SwaggerFeature.
Plugins.Add(new SwaggerFeature());
123
We can see that all of the information provided through the Api attributes is visible.
124