Over the last month we've started using ServiceStack for a couple of our api endpoints. We're hosting these projects on a debian squeeze vm using nginx and mono. We ran into various problems along the way. Here's a breakdown of what we found and how we solved the issues we ran into. Hopefully you'll find this useful. (We'll cover deployment/infrastructure details in a second post.)

Overriding the defaults

Some of the defaults for ServiceStack are in my opinion not well suited to writing an api. This is probably down to the frameworks desire to be a complete web framework. Here's our current default implementation of AppHost:

 

For me, the biggest annoyance was trying to find the DefaultContentType setting. I found some of the settings unintuitive to find, but it's not like you have to do it very often!

Timing requests with StatsD

As you can see, we've added a StatsD feature which was very easy to add. It basically times how long each request took and logs it to statsD. Here's how we did it:

 

It would have been nicer if we could wrap the request handler but that kind of pipeline is foreign to the framework and as such you need to subscribe to the begin and end messages. There's probably a better way of recording the time spent but hey ho it works for us.

RestServiceBase, Exceptions and Error Responses

One of my biggest bugbears with ServiceStack was the insistence on a separate request and response object, the presence of a special property and that you follow a naming convention all in the name of sending error and validation messages back to the client. It's explained at length on the wiki and Demis was good enough to answer our question.

RestServiceBase

The simple RestServiceBase that comes with provides an easy way of getting started, but there aren't many hooks you can use to manipulate how it works. It would be nice if you could inject your own error response creator. We ended up inheriting from RestServiceBase and overriding how it works:

https://gist.github.com/antonydenyer/3755906#file-gistfile1-cs

public abstract class RestServiceBase<TRequest> : global::ServiceStack.ServiceInterface.RestServiceBase<TRequest>
{
private IErrorResponseFactory _errorResponseFactory;
private IErrorResponseFactory ErrorResponseFactory
{
get { return _errorResponseFactory ?? (_errorResponseFactory = new ErrorResponseFactory()); }
set { _errorResponseFactory = value; }
}
 
private static readonly ILog Log = LogManager.GetLogger(typeof(ServiceBase<>));
 
 
protected override object HandleException(TRequest request, Exception ex)
{
if (ex.InnerException != null && !(ex is IHttpError))
ex = ex.InnerException;
 
var responseStatus = ResponseStatusTranslator.Instance.Parse(ex);
 
if (EndpointHost.UserConfig.DebugMode)
{
responseStatus.StackTrace = GetRequestErrorBody() + ex;
}
 
Log.Error("ServiceBase<TRequest>::Service Exception", ex);
 
var errorResponse = ErrorResponseFactory.CreateErrorResponse(request, ex, responseStatus);
 
AfterEachRequest(request, errorResponse ?? ex);
 
return errorResponse;
}
}

We basically chopped out the bits we're not using and changed the thing that creates the Error Response:

https://gist.github.com/antonydenyer/3755929#file-gistfile1-cs

public interface IErrorResponseFactory
{
object CreateErrorResponse<TRequest>(TRequest request, Exception ex, ResponseStatus responseStatus);
object CreateErrorResponse<TRequest>(TRequest request, ValidationErrorResult validationError);
}
 
 
public class ErrorResponseFactory : IErrorResponseFactory
{
public object CreateErrorResponse<TRequest>(TRequest request, ValidationErrorResult validationError)
{
var responseStatus = ResponseStatusTranslator.Instance.Parse(validationError);
 
var errorResponse = CreateErrorResponse(request, new ValidationError(validationError), responseStatus);
 
return errorResponse;
}
 
public object CreateErrorResponse<TRequest>(TRequest request, Exception ex, ResponseStatus responseStatus)
{
var responseDto = CreateResponseDto(responseStatus);
 
var httpError = ex as IHttpError;
if (httpError != null)
{
if (responseDto != null)
httpError.Response = responseDto;
 
return httpError;
}
 
var errorCode = ex.GetType().Name;
var errorMsg = ex.Message;
if (responseStatus != null)
{
errorCode = responseStatus.ErrorCode ?? errorCode;
errorMsg = responseStatus.Message ?? errorMsg;
}
 
return new HttpError(responseDto, ex.ToStatusCode(), errorCode, errorMsg);
}
 
private static object CreateResponseDto(ResponseStatus responseStatus)
{
return new ErrorResponse { ResponseStatus = responseStatus };
}
}

This allows us to respond with an error no matter what the type is for the request or what response you are going to send back. It provides us with extra flexibility above what is provided out of the box. In a nutshell, if there's an exception in the code we will always get a stack trace in the response if debug is on.

Validation

We had the same issue with the validation feature; if you don't follow the convention you don't get anything in the response body. So we followed the same practice and copied  the ValidationFeature and tweaked it how we wanted it.

https://gist.github.com/antonydenyer/3756013#file-gistfile1-cs

public class ValidationFeature : IPlugin
{
private readonly IErrorResponseFactory _errorResponseFactory;
 
public ValidationFeature(IErrorResponseFactory errorResponseFactory)
{
_errorResponseFactory = errorResponseFactory;
}
 
public static bool Enabled { private set; get; }
 
/// <summary>
/// Activate the validation mechanism, so every request DTO with an existing validator
/// will be validated.
/// </summary>
/// <param name="appHost">The app host</param>
public void Register(IAppHost appHost)
{
Enabled = true;
var filter = new ValidationFilters(_errorResponseFactory);
appHost.RequestFilters.Add(filter.RequestFilter);
}
}
 
 
public class ValidationFilters
{
private readonly IErrorResponseFactory _errorResponseFactory;
 
public ValidationFilters(IErrorResponseFactory errorResponseFactory)
{
_errorResponseFactory = errorResponseFactory;
}
 
public void RequestFilter(IHttpRequest req, IHttpResponse res, object requestDto)
{
var validator = ValidatorCache.GetValidator(req, requestDto.GetType());
if (validator != null)
{
var ruleSet = req.HttpMethod;
var validationResult = validator.Validate(
new ValidationContext(requestDto, null, new MultiRuleSetValidatorSelector(ruleSet)));
 
if (validationResult.IsValid) return;
 
var errorResponse = _errorResponseFactory.CreateErrorResponse(requestDto, validationResult.ToErrorResult());
 
res.WriteToResponse(req, errorResponse);
}
}
}

 

Conclusion

I like ServiceStack; it's really easy to get up and running and whilst it has it's own opinions on how you should work, what framework doesn't?

 

Tag: 
ServiceStack
statsd
anna.siegel@7digital.com
Wednesday, May 11, 2016 - 04:20

Today marks the beginning of the Technical Academy Tour as Academy Coordinator, Miles Pool, VP Technology, Paul Shannon and later, former apprentice, Mia Filisch head out across the UK to talk about our Technical Academy.

 

Continuous learning has always been part of the culture at 7digital and the Technical Academy allowed us to focus those ideas and start hiring apprentices. Changing the team entry requirements and providing a defined period of training allowed us to attract people from more diverse backgrounds and has increased the proportion of female developers in our team; it’s also strengthened the culture of learning and knowledge sharing at every level.

Emma-Ashley Liles
Monday, April 4, 2016 - 13:48

Since I started at 7digital I’ve loved our belief in continuous improvement. Throughout our history as a company we have had a number of influential women working in various parts of organisation yet I knew there was more we could do to improve the diversity of our tech team.

 

Anonymous
Tuesday, February 16, 2016 - 18:30

Here at 7digital, we see the relationship between the customer and the developer as one of the most important aspects of software development. We treat software development as more of a craft than an engineering discipline. Craftsmen back in the day would have constant communication with their customers, receiving regular visits from their customer to discuss progress and alterations as the item takes shape.

 

Over the last twenty years, the agile software movement and extreme programming in particular has championed this with its short iterations, customer showcases and active customer participation in the creation of features.

 

Mia.Filisch
Tuesday, December 1, 2015 - 20:10

7digital software developer Mia Filisch attended the October 28th Velocity conference in Amsterdam. She was kind enough to share her account of the core takeaways here with us. She found that the core recurring theme around security was enough to inspire some internal knowledge sharing sessions she has already started scheming on. The diversity of insights led to a productive and informative conference. See below for her notes.

 

Key takeaways from specific sessions: