Ošetření chybových stavů v ASP.NET MVC
|
Každá aplikace se může dostat do stavu, kdy není vše, jak má být, a nastane chyba. Tyto stavy bychom neměli ignorovat, ale pečlivě ošetřovat. Jak na to se podíváme v tomto spotu.
ASP.NET obsahuje poměrně sofistikovaný mechanismus na zpracování chyb. Ukazuje celkem podrobné chybové výpisy, které jsou nám vývojářům velice užitečné. Nicméně nejsou užitečné naším uživatelům a ještě mohou leccos prozradit našim útočníkům. Proto by v produkci měly být tyto hlášky zakázané.
Konfigurace
Každá aplikace v produkci by měla mít nastavené pěkné chybové stránky, bez podrobností, co se stalo, ale s informacemi, kam dál uživatel může pokračovat. Popřípadě mu pomoci nalézt to, co hledal. K tomu slouží konfigurační element customErrors
, kde můžeme nastavit režim zobrazování a jednotlivé stránky, které se mají zobrazit na určitý chybový kód.
Bohužel, systém je navržen zcela kokotsky a pokud chcete v konfiguraci nastavit pro 404 a 500 jiné stránky, jsou vraceny s kódem 200. Proto využijeme jen následující konfiguraci. Zbylá je celkem k ničemu.
<customErrors mode="RemoteOnly" />
Tím docílíme toho, že všechny requesty, které nejsou z tohoto serveru obdrží pěkné chybové stránky. Dotaz z daného serveru dostane podrobný chybový výpis, což se může hodit.
Zpracování chyb
ASP.NET MVC má v sobě základní mechanismus pro ošetřování chyb. Je jím ActionFilter HandleError
, který renderuje pohled Error.aspx
, pokud dojde k probublání neošetřené výjimky až do akce řadiče.
[HandleError]
public class MyController : Controller {
}
Procento chyb, takto zachytitelných, jistě není malé, ovšem není 100%. Nemusíme chodit pro příklad daleko a stačí udělat chybku v šabloně pohledu. Hned tu máme nepěknou chybovou stránku.
Takovouto chybu odchytíme až v Global.asax:
protected void Application_Error() {
#if DEBUG
return;
#endif
Exception exception = Server.GetLastError();
Response.Clear();
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (IsPageNotFoundException(exception as HttpException)) {
routeData.Values.Add("action", "NotFound");
}
else {
routeData.Values.Add("action", "Error");
}
Server.ClearError();
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
Pokud máme aplikaci v debug modu (typicky u vývojáře) je vhodné nechat chyby probublat až k němu. Pokud jsme ale v releasu musíme chybu zpracovat. Na výpis chybové stránky nám poslouží ErrorController
, který má dvě akce: NotFound
pro 404 a Error
pro 500.
public class ErrorController : Controller {
[StatusCode(HttpStatusCode.NotFound)]
public ActionResult NotFound() {
return View();
}
[StatusCode(HttpStatusCode.InternalServerError)]
public ActionResult Error() {
return View();
}
}
Díky způsobu hledání šablon v ASPX View enginu, můžeme na Error
využít stejnou Error.aspx
ve složce Shared
jako pro chyby zachycené už na řadiči.
Neplatná referenční identita
Teď se ještě dostaneme k tomu, proč tu máme zpracování HttpException
s kódem 404. Kde se vezme?
Typicky, když máme nějakou detail page, snažíme se pomocí nějakého jedinečného identifikátoru natáhnout zobrazovanou entitu. Pokud žádnou nenajdeme je vhodné vyhodit výjimku a protože jsme na úrovni webové aplikace, měla by to být HttpException
s kódem 404
, která říká, že hledaná stránka nebyla nalezena.
Její zpracování už máme výše vyřešeno. A to je snad pro dnešek vše. :)