ViewData, ViewBag, TempData i Action Filteri

Modul 3 - Lekcija 2

Razmjena podataka između Controller-a i View-a, te upotreba Action Filter-a

⏱️ Trajanje: ~3 časa | 📚 Nivo: Srednji | 🎯 Praktični primjeri: 5

📖 Razmjena Podataka

Osim prosljeđivanja snažno tipizovanog Modela (Strongly Typed Model) metodi View(model), MVC nudi dodatna tri načina za prosljeđivanje privremenih podataka iz Controller-a u View:

  • ViewData
  • ViewBag
  • TempData

Ovi rječnici koriste se za prenošenje pomoćnih informacija poput poruka o greškama, statusa operacija, title taga na stranici i padajućih listi (Dropdown objekata).

📦 ViewData Rječnik

ViewData je dictionary objekat (Dictionary<string, object>) koji omogućava prosljeđivanje podataka iz Controller-a u View. Podaci su dostupni samo tokom trenutnog HTTP zahtjeva.

// --- CONTROLLER ---
public ActionResult Index()
{
    ViewData["MinistryName"] = "Ministarstvo Finansija";
    ViewData["EmployeeCount"] = 350;
    return View();
}

// --- VIEW (Index.cshtml) ---
<h1>@ViewData["MinistryName"]</h1>
<p>Broj zaposlenih: @ViewData["EmployeeCount"]</p>

💡 ViewData Ograničenja

Zahtijeva eksplicitni type casting ako koristite metode nad objektom i nema IntelliSense podršku.

🎒 ViewBag Dinamički Objekt

ViewBag je dinamički wrapper oko ViewData. Omogućava dinamičko dodavanje svojstava putem dot notacije (npr. ViewBag.Key) bez type casting-a na View strani.

// --- CONTROLLER ---
public ActionResult Index()
{
    ViewBag.MinistryCode = "MF";
    ViewBag.Departments = db.Departments.ToList(); // Nema potrebe za TypeCast
    return View();
}

// --- VIEW (Index.cshtml) ---
<h1>Ministarstvo: @ViewBag.MinistryCode</h1>
@foreach (var dept in ViewBag.Departments) // C# zna koji je objekat
{
    <p>@dept.DepartmentName - @dept.Budget KM</p>
}

🔑 Međusobna povezanost ViewBag i ViewData

Oni koriste isti kontejner – promjena ViewBag.Title će automatski promijeniti i ViewData["Title"]. Zbog preglednosti koristite isključivo ViewBag dizajn.

💾 TempData - Perzistencija između Zahtjeva

TempData čuva podatke između dva uzastopna zahtjeva. Često se koristi za prebacivanje Success poruka na Index prikaz nakon uspješnoj POST snimanja.

Kako TempData radi u pozadini?

Za razliku od ViewBag(za isti HTTP poziv), TempData preživljava HTTP Redirect jer u pozadini koristi HTTP Session. Kada se u narednom MVC zahtjevu vrijednost jednom pročita iz TempData rječnika, ona se briše i oslobađa poslužiteljsku memoriju.

[HttpPost]
public ActionResult Create(Department dept)
{
    db.Departments.Add(dept);
    db.SaveChanges();
    TempData["SuccessMessage"] = "Odjel uspješno kreiran!"; // PISAO
    return RedirectToAction("Index"); // 302 REDIRECT REQUEST
}

public ActionResult Index() // <- DRUGI NOVI ZAHTJEV
{
    ViewBag.Message = TempData["SuccessMessage"]; // ČITAO, BRISANJE!
    return View();
}

🔍 Action Filteri i Životni Ciklus

Action Filteri su atributske klase koje dodaju se na Action Metode radi ubacivanja C# koda "oko" vašeg stvarnog operativnog Controller metoda koda.

U poslovnim (enterprise) aplikacijama ovi presretači se masovno koriste za rješavanje nevidljivih tehničkih zahtjeva ("Cross-Cutting Concerns"):

  • Autorizacija i Autentifikacija
  • Globalni Audit Logging
  • Keširanje Odgovora i Error Handling

Time održavamo Controller kod fokusiranim isključivo na logiku radnje!

MVC Action Filter Pipeline

⚠️ Ugrađeni Filter: HandleError

Presretač grešaka. Kada kod "pukne", umjesto žute programske stranice, HandleError nam vraća čistu HTML stranicu "Doslo je do greske" bez otkrivanja C# tajni.

// Globalno postavljen za cijelu aplikaciju (Global.asax -> FilterConfig.cs)
filters.Add(new HandleErrorAttribute());

// Ili lokalno postavljen samo na neku metodu kojom upravljamo:
[HandleError]
public ActionResult CalculateTaxes()
{
    throw new Exception("Neobuhvaćena sistemska greška");
}

// Specijaliziran sa custom View-om kad "pukne" specifičan kontroler
[HandleError(View = "MaintenanceErrorPage")]
public ActionResult DatabaseHeavyTask()
{
    // C# kod
}

🔐 Ugrađeni Filter: Authorize

Filter bez kojeg nijedna korporativna i sigurnosna .NET MVC aplikacija ne može funkcionisati. Usmjerava "anonymous" korisnike na Login ekran.

// Zahtijeva da korisnik makar bude Logovan (bilo kakav account)
[Authorize]
public ActionResult Dashboard()
{
    return View();
}

// Zahtijeva specifičnu poslovnu grupu/rolu zaduženu za odobravanje
[Authorize(Roles = "SuperAdmin, FinancialOffice")]
public ActionResult DeleteResource(int id)
{
    // Samo oni logovani a koji pripadaju Role grupama pokreću kod
}

// Override ponašanje klase, recimo ako je na klasi [Authorize], 
// ovu specifičnu metodu ne filtrira:
[AllowAnonymous]
public ActionResult PublicTermsAndConditions()
{
    return View();
}

⚙️ Custom Action Filter (Vlastiti)

Ponekad želimo napisati sopstveni Filter. Nasljeđivanjem klase ActionFilterAttribute možemo ga zalijepiti iznad svake metode!

public class LoggovanjeAktivnostiUpraveAttribute : ActionFilterAttribute
{
    // Metoda se okida ODMAH PRIJE nego Controller metod počne:
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string ipAdresa = filterContext.HttpContext.Request.UserHostAddress;
        string userName = filterContext.HttpContext.User.Identity.Name;
        string actionName = filterContext.ActionDescriptor.ActionName;

        // Snimimo u Logging bazu zapisa!
        DbLogger.Snimi(ipAdresa, userName, actionName, DateTime.Now);
    }
}

// KORIŠTENJE FILTERA:
[LoggovanjeAktivnostiUprave]
public ActionResult PregledUgovora() { return View(); }

🎯 Praktična Vježba

Zadatak: Implementirajte AuditLog Filter

Kreirajte AuditLogAttribute koji bilježi IP adresu korisnika i naziv akcije u tekstualni fajl svake akcije koja je pod autorizacijom (simulacija audit loga).

public class AuditLogAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var action = filterContext.ActionDescriptor.ActionName;
        var controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
        var log = $"Akcija {controller}/{action} pozvana u {DateTime.Now}";
        
        // Jednostavan fajl log upis (potreban IO .txt fajl path)
        System.IO.File.AppendAllText("C:\\Logs\\AuditLog.txt", log + Environment.NewLine);
    }
}

✅ Zaključak

  • ViewBag koristimo češće za male vrijednosti proslijeđene ka View (SelectListItem i Title parametri), jer je čišći od sintakse ViewData rječnika.
  • TempData preživljava isključivo preusmjeravanje sa HTTP statusom 302, oslanja se na serveru memoriju, na Session i čim se vrijednost pročita gasi joj se podrška i briše iz memorije. Idealno za poruke!
  • Action Filtere možete vezati na metode izvana kao Dekorator šablone ponašanja bez da zagadite biznis metodu Controllera. Najpoznatiji filter su naravno [Authorize] i [HttpPost].