Nastavení JSON formatteru v API dle klienta
Stalo se vám, že jste potřebovali vracet JSON data v odlišném formátu podle toho, jaký klient se dotazuje na vaše API? Pokud ano, velmi snadno se dá tento "problém" vyřešit pomocí ActionFilter atributu a dodatečné konfigurace controlleru.
Problém
Máme dva klienty, například mobilní aplikaci z roku 2012, která v minulosti konzumovala JSON data ve formátu:
{ "FirstName" : "Miroslav", "LastName" : "Holec" }
Píše se rok 2016 a vzniká nové API, které bude po vzoru JavaScript konvencí vracet lowerCamelCase JSON:
{ "firstName" : "Miroslav", "lastName" : "Holec" }
Problém je, že globální změna v API by znemožnila správnou funkci ve starých klientech z roku 2012, které má plno uživatelů nainstalováno v mobilních zařízeních. Jak nastavit API aby uspokojilo všechny klienty?
Řešení
Předpokladem pro řešení je schopnost rozlišit, jaký klient se dotazuje na data. Obvykle máme podobnou informaci přímo v Requestu. Může to být querystring, hlavička, IP adresa nebo jiný příznak. Pokud tuto informaci nemáme, prohlásíme všechny stávající klienty jako legacy a novým přidáme povinnost uvádět verzi API (například v hlavičce).
Pak už si stačí napsat jednoduchý atribut, který na základě requestu nakonfiguruje vhodný formatter.
[FormatterController] public class MyController : ApiController { } public class FormatterControllerAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { var queryParameters = actionContext.Request.GetQueryNameValuePairs().ToList(); var apiKey = queryParameters.FirstOrDefault(x => x.Key.Equals("API_KEY", StringComparison.InvariantCultureIgnoreCase)); // pokud se nejedna o stara API calls, ktere pouzivaji API_KEY, nastavi se novy formatter if (apiKey.Value != "holec9876api_key_xxxxxxxxxxxxxxxx") { actionContext.ControllerContext.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } base.OnActionExecuting(actionContext); } }
A to je celé řešení. Právě actionContext.ControllerContext.Configuration
nám poskytuje možnost dodatečně nakonfigurovat controller podle potřeby.