Na obsah stránky

Ošetření chybových stavů v ASP.NET MVC

Aleš Roubíček | | # permalink

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. :)

Našli jste v článku chybu? Máte námět na reportáž? Založte mi ticket.