Jak vracet správně chybové stavy z REST API
Tento článek byl napsán v roce 2019. Vývojářské technologie se neustále inovují a článek již nemusí popisovat aktuální stav technologie, ideální řešení a můj současný pohled na dané téma.
Otázku, jak správně reagovat na chyby v REST API, si kladlo v minulosti mnoho vývojářů a každý to dělal víceméně po svém. Naštěstí dnes už existují určitá doporučení, kterých se lze držet a které obecně budou fungovat i ve vašem řešení. V článku se podíváme na základní pravidla, kterých stojí za to se držet.
Vracejte správně Status Code
Existuje celá série stavových kódů, které může vývojář použít pro vyjádření chyby. V ASP.NET Core se stavový kód umí nastavit automaticky (například když neklapne validace) nebo jej obvykle nastavíte manuálně skrze volání určité metody. Například:
[HttpGet("api/articles/{id:int}")] public IActionResult GetArticle(int id) { var result = repository.GetArticle(id); if(result == null) { return NotFound(); } return Ok(result); }
V případě globálního zpracování chyb budete obvykle nastavovat stavový kód manuálně. Zde je důležité co nejlépe propojit stavový kód se skutečným stavem. Pásmo kódů 4xx použít výhradně v případě, kdy došlo k chybě na základě vstupu klienta a generický stav 5xx pro případy, kde jste za chybu zodpovědní vy na straně API. Čím lépe stavový kód nastavíte, tím snáze se bude vaše API konzumovat. Řada klientských knihoven totiž na základě stavového kódu zpřístupňuje různé metody (např.: IsSuccessResponse() apod).
Některé stavové kódy jsou spojeny i s HTTP hlavičkami. Pokud vracíte například stavový kód 301, kterým vyjadřujete, že daný resource se přesunul jinam, nezapomeňte uvést HTTP hlavičku Location. Pro throttling a stavový kód 429 se hodí vrátit i sérii hlaviček popisujících kdy může klient požádat znovu o data nebo alespoň hlavičku Retry-After.
Dodržujte stejnou strukturu chyb
V případě různých chyb by z API měla nejlépe přicházet vždy stejná struktura. Aby mohl klient vaše API snadno konzumovat, potřebuje všechny chybné výsledky zpracování deserializovat proti stejné třídě. Vrátíte-li jednou strukturu { "message" = "xxx" }, podruhé { "result" = ""} a potřetí třeba array validačních chyb, je pro klienta API velmi těžko zpracovatelné.
Od verze ASP.NET Core 2.1 jsou k dispozici třídy ProblemDetails a ValidationProblemDetails, které umožňují vracet generické chybové struktury dle RFC 7807. Použijete-li tuto strukturu, přiblížíte se k pravděpodobnosti, že konzument už s takovou strukturou v minulosti pracoval a usnadníte mu tím výrazně život. Problem Details jsou navíc rozšiřitelné, takže kromě společných informací můžete vrátit i specifické.
Zapomenou byste neměli na Content-Negotiation. Pokud chtěl klient data vrátit v XML formátu a dojde v API k chybě, i chybová struktura by měla být stále v XML. I když nemůžete nebo nechcete zajistit podporu negociace, nezapomeňte vždy vrátit HTTP hlavičku Content-Type. A chcete-li podporovat pouze chyby ve formátu JSON, pak to jasně uveďte v dokumentaci a toto pravidlo dodržujte v celém API.
HTTP Response Headers
V případě chyby je nutné vrátit všechny očekávané HTTP hlavičky. Především pokud reflektujete RFC 7807, měli byste vracet například Content-Type: application/problem+json. Máte-li zapnutý CORS middleware, neměly by se související response hlavičky ztratit. I v případě vzniku chyby potřebuje prohlížeč totiž vědět, nakolik může datům z API důvěřovat. Opatrní buďte i v případě Cache hlaviček, abyste například nevraceli instrukce pro cachování dat, která se v případě chyby ani nevrátí.
Call Stack + TraceID
V development prostředí si vraťte do chybové struktury i stack trace pro snazší diagnostiku chyby. Na ostatních produkčních prostředích tyto informace samozřejmě nereflektujte. Bez ohledu na prostředí doporučuji vytvořit pro každý request speciální TraceID (dělá to i ASP.NET Core nebo např.: Application Insights) a toto TraceID reflektovat do HTTP hlavičky. Konzument tak bude moci zapisovat všechny TraceID a v případě potíží se na vás s tímto TraceID obrátit. Nezapomeňte toto TraceID minimálně v případě chybových stavů tedy logovat.
Čas od času není na škodu logy kontrolovat a nejčastější příčiny chyb diagnostikovat. Seskupovat chyby se stejnou signaturou umí výborně například služba Application Insights. I když velmi často chyby vzniknou logicky na straně klienta, který na něco zapomene, může vás to upozornit i na chybu v návrhu vašeho API.