An often overlooked feature in ServiceStack is its ability to easily support custom media types and formats.
It's not given as much prominence as ServiceStack's other formats simply because supporting specific media types require more custom development effort so are unlike other built-in ServiceStack generic data formats (JSON, XML, CSV, JSV, HTML, SOAP) that are automatically available to all your services without any extra effort.
At times when your clients have built-in support for a specific media-type it can allow richer functionality and a deeper integration into users applications than what would otherwise be possible. An example of this is allowing users to import contact information directly into Outlook by providing contact information in the VCard Format, which you can make available for instance on the results page on an online contact directory search.
So when it's beneficial to, adding Custom Media Types in Service Stack is easy where in just 1 Line of Code you can register your format and have it immediately availble in ServiceStack's REST pipeline and auto-generated /metadata pages.
Below is a live example of what is possible when we add the VCard format to the Northwinds Customer web services. It allows us to provide the unusual relationship giving our MONO web server deep access into your Windows Outlook client :)
Like the HTML and CSV formats before it, the best way to add an additional Media Type in ServiceStack is to encapsulate it in a single class keeping it loosely-coupled from the rest of your system thus making it an easy drop-in or removal whenever you need it. The entire support for the format is contained in VCardFormat.cs and is explained below:
In the Register() method we simply tell ServiceStack that we have a new ContentType available and supply the Stream serialisers that it should use whenever processing that ContentType.
With just this 1 line of config:
appHost.ContentTypeFilters.Register(VCardContentType, SerializeToStream, DeserializeFromStream);
We now have your custom format registered into ServiceStack who will now use the supplied Content serializers
whenever this Content-Type is requested by the client in any of the following ways:
Another benefit ServiceStack gives you is that this format is automatically provided on the auto-generated
/metadata page.
This is what the Customers X-VCARD metadata page looks like: /x-vcard/metadata?op=Customers
The ResponseFilter is added to intercept the response and a Content-Disposition HTTP header added to signal to the browser that this resource is to be treated as an attachment using the prescribed filename.
private const string VCardContentType = "text/x-vcard";
public static void Register(IAppHost appHost)
{
appHost.ContentTypeFilters.Register(VCardContentType, SerializeToStream, DeserializeFromStream);
appHost.ResponseFilters.Add((req, res, dto) =>
{
if (req.ResponseContentType == VCardContentType)
{
res.AddHeader(HttpHeaders.ContentDisposition,
string.Format("attachment;filename={0}.vcf", req.OperationName));
}
});
}
With the x-vcard format now registered in ServiceStack, the only thing left to to is implement the Content-Type SerializeToStream routine. Unlike the data formats it is not as simple as just serioulizing the Respone DTO. In this case we need to detect if the response DTO contains a customer and if it does return its data in the VCard format.
public static void SerializeToStream(IRequestContext requestContext, object response, Stream stream)
{
var customerDetailsResponse = response as CustomerDetailsResponse;
using (var sw = new StreamWriter(stream))
{
if (customerDetailsResponse != null)
{
WriteCustomer(sw, customerDetailsResponse.Customer);
}
var customers = response as CustomersResponse;
if (customers != null)
{
customers.Customers.ForEach(x => WriteCustomer(sw, x));
}
}
}
public static void WriteCustomer(StreamWriter sw, Customer customer)
{
sw.WriteLine("BEGIN:VCARD");
sw.WriteLine("VERSION:2.1");
sw.WriteLine("FN:" + customer.ContactName);
sw.WriteLine("ORG:" + customer.CompanyName);
sw.WriteLine("TITLE:" + customer.ContactTitle);
sw.WriteLine("EMAIL;TYPE=PREF,INTERNET:" + customer.Email);
sw.WriteLine("TEL;HOME;VOICE:" + customer.Phone);
sw.WriteLine("TEL;WORK;FAX:" + customer.Fax);
sw.WriteLine("ADR;TYPE=HOME;"
+ new[] { customer.Address, customer.City, customer.PostalCode }.Join(";"));
sw.WriteLine("END:VCARD");
}
And that's the entire VCard format in a single loosely-coupled class. No other concepts / artefacts are required to make this work, you can just simply plug it into ServiceStack the the following 1 Line of Code:
public override void Configure(Container container)
{
VCardFormat.Register(this);
}