:::: MENU ::::

Wednesday, January 31, 2018

If you follow my blog, you may also know that I’ve blogged a bit about Hypermedia.  Because I’m a fan, I’ve decided to start creating an custom ASP.NET Core Output Formatter to handle the Siren hypermedia specification(application/vnd.siren+json).
All the code samples in this blog post are pulled from a sample application of the game Hangman.   I’m creating it in ASP.NET Core.  I will eventually pull out the Output Formatter and create a repo on GitHub and a Nuget.

Output Formatter

Since I’m handling Text, I’m going to be deriving from the TextOutputFormatter.  This requires you to implement two methods:
bool CanWriteType(Type type)
This method determines if your output formatter can handle the type returned from your controller action.
Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
Handle how yo want to transform the object returned from your controller action.
Lastly, you also need to define which media types your output formatter will handle.  In other words, when the client sends an Accept: header, which values apply to your output formatter.
In my case, Siren uses application/vnd.siren+json
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace Hangman.Api.Siren.Formatters
{
public class SirenOutputFormatter : TextOutputFormatter
{
private static readonly JsonSerializerSettings DefaultJsonSerializerSettings
= new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
NullValueHandling = NullValueHandling.Ignore,
};
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
private readonly Dictionary<Type, object> _templates;
private readonly JsonSerializerSettings _jsonSerializerSettings;
public SirenOutputFormatter(Dictionary templates)
: this(templates, DefaultJsonSerializerSettings, DefaultEncoding)
{
}
public SirenOutputFormatter(Dictionary templates, JsonSerializerSettings settings)
: this(templates, settings, DefaultEncoding)
{
}
public SirenOutputFormatter(Dictionary templates, Encoding encoding)
: this(templates, DefaultJsonSerializerSettings, encoding)
{
}
public SirenOutputFormatter(Dictionary templates, JsonSerializerSettings settings, Encoding encoding)
{
_templates = templates;
_jsonSerializerSettings = settings;
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/vnd.siren+json"));
SupportedEncodings.Add(encoding);
}
protected override bool CanWriteType(Type type)
{
return _templates.ContainsKey(type);
}
public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var template = _templates[context.ObjectType];
var builder = template.GetType()
.GetMethod("ToRepresentation")
.Invoke(template, new [] {context.Object});
var entity = builder.GetType().GetMethod("Build").Invoke(builder, new object[0]);
return context.HttpContext.Response.WriteAsync(
JsonConvert.SerializeObject(entity, _jsonSerializerSettings),
selectedEncoding,
context.HttpContext.RequestAborted);
}
}
}
view rawformatter.cs hosted with ❤ by GitHub

MVC Options

Finally, in our startup, we need to add our new Output Formatter in the AddMVC extension method.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddMvc(options =>
{
var templates = new Dictionaryobject
> { { typeof(GameResponse), new GameSirenResponse() } }; options.OutputFormatters.Add(new SirenOutputFormatter(templates)); }); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new Info { Title = "Hangman Api v1.0", Version = "v1" }); c.IncludeXmlComments("wwwroot/Hangman.Api.xml"); }); }
view rawstartup.cs hosted with ❤ by GitHub

Results

Here are two different outputs.  The first is with an Accept: application/json and the second is requestion Siren with Accept: application/vnd.siren+json
Stay Tuned
If you’re interested in supporting Siren in your ASP.NET Core MVC applications, I plan on publishing my small library soon to GitHub as well as Nuget.
Have you created a custom output formatter? I’d love to hear what your creating!  Let me know on Twitter or in the comments.