MODUL 3 - LEKCIJA 2

ViewData, ViewBag, TempData i Action Filteri

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

MVC nudi tri načina za prosljeđivanje podataka iz Controller-a u View: ViewData, ViewBag, i TempData. Svaki ima svoju svrhu i ograničenja.

📦 ViewData

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

🛠️ ViewData Primjer
// Controller
public ActionResult Index()
{
    ViewData["MinistryName"] = "Ministarstvo Finansija";
    ViewData["EmployeeCount"] = 350;
    return View();
}

// View

@ViewData["MinistryName"]

Broj zaposlenih: @ViewData["EmployeeCount"]

💡 ViewData Ograničenja

ViewData zahtijeva type casting i nema IntelliSense podršku. Koristite ga za jednostavne podatke.

🎒 ViewBag

ViewBag je dinamički wrapper oko ViewData. Omogućava dinamičko dodavanje svojstava bez type casting-a.

🛠️ ViewBag Primjer
// Controller
public ActionResult Index()
{
    ViewBag.MinistryCode = "MF";
    ViewBag.BudgetYear = 2024;
    ViewBag.Departments = db.Departments.ToList();
    return View();
}

// View

Ministarstvo: @ViewBag.MinistryCode

Budžetska godina: @ViewBag.BudgetYear

@foreach (var dept in ViewBag.Departments) {

@dept.DepartmentName - @dept.Budget KM

}

🔑 ViewBag vs ViewData

ViewBag i ViewData koriste isti storage - promjena u jednom se reflektuje u drugom. ViewBag je samo sintaksni šećer.

💾 TempData i Background Mehanizam

TempData čuva podatke između dva uzastopna zahtjeva. Koristi se pretežno za preusmjeravanja (redirects) nakon uspješnog snimanja ili zbog prikaza poruke o grešci.

Kako TempData radi u pozadini?

Za razliku od ViewData i ViewBag koji žive samo tokom trenutnog HTTP Request-a, TempData podrazumijevano u pozadini koristi sistemski Session State. To znači da se podaci čuvaju u memoriji servera (ili u Redis-u/SQL-u ako je konfigurisan Scale-Out za državne sisteme). Kada se vrijednost iz TempData rječnika jednom pročita (bilo u Action metodi ili na View-u), ona se markira za brisanje i nestaje iz Session-a na kraju tog Request-a, čime se automatski oslobađa dragocjena poslužiteljska memorija.

🛠️ TempData Primjer
// Controller - Create action
[HttpPost]
public ActionResult Create(Department dept)
{
    if (ModelState.IsValid)
    {
        db.Departments.Add(dept);
        db.SaveChanges();
        TempData["SuccessMessage"] = "Odjel uspješno kreiran!";
        return RedirectToAction("Index");
    }
    return View(dept);
}

// Controller - Index action
public ActionResult Index()
{
    // TempData je dostupan ovdje nakon redirect-a
    ViewBag.Message = TempData["SuccessMessage"];
    return View();
}

// View
@if (ViewBag.Message != null)
{
    
@ViewBag.Message
}

💡 TempData Keep()

Koristite TempData.Keep("key") da zadržite podatke za sljedeći zahtjev.

🔍 Action Filteri i Životni Ciklus

Action Filteri omogućavaju izvršavanje prilagođenog koda prije ili nakon izvršavanja Action metode. U velikim enterprise aplikacijama, kao što su portali za priznavanje diploma ili registar javnih nabavki, oni se masovno koriste za rješavanje tehničkih zahtjeva ("Cross-Cutting Concerns") poput logginga svake akcije u bazu, provjere kompleksne RBAC autorizacije, i cachinga, ostavljajući takav kod fizički izvan Controller klasa što čini kod mnogo čitljivijim.

MVC Action Filter Pipeline

Slika: Životni ciklus i redoslijed izvršavanja Action Filtera

1. HandleError Filter

🛠️ HandleError
// Globalni filter (FilterConfig.cs)
filters.Add(new HandleErrorAttribute());

// Ili na action metodi
[HandleError]
public ActionResult Index()
{
    throw new Exception("Test greška");
}

// Custom error view
[HandleError(View = "CustomError")]
public ActionResult Index()
{
    // ...
}

2. Authorize Filter

🛠️ Authorize
// Zahtijeva autentifikaciju
[Authorize]
public ActionResult Admin()
{
    return View();
}

// Zahtijeva određenu ulogu
[Authorize(Roles = "Admin")]
public ActionResult Delete(int id)
{
    // ...
}

// Dozvoljava anonimnim korisnicima
[AllowAnonymous]
public ActionResult Public()
{
    return View();
}

3. Custom Action Filter

🛠️ Kreiranje Custom Filtera
public class AuditLogAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Logujemo ko, šta i kada pristupa - obavezno za javnu upravu
        string user = filterContext.HttpContext.User.Identity.Name ?? "Anonymous";
        string action = filterContext.ActionDescriptor.ActionName;
        string controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
        string ip = filterContext.HttpContext.Request.UserHostAddress;

        System.Diagnostics.Debug.WriteLine($"[AUDIT] User: {user} | Action: {controller}/{action} | IP: {ip}");
    }
}

// Korištenje
[AuditLog]
[Authorize] // Obično ide uz autorizaciju
public ActionResult Index()
{
    return View();
}

🎯 Praktična Vježba

Zadatak: Implementirajte AuditLog Filter

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

✅ Zaključak

Naučili ste: