Vad har ActionResult för funktion i ASP.NET MVC?

När vi skapar ett projekt i ASP.NET MVC så får vi med två standard-controllers i projektet (AccountController och HomeController). I dessa finns det sedan metoder som används för att ta emot de requests som görs. Metoderna som tar hand om dessa kallas ofta ”action-metoder” och är av typen ActionResult som standard. Men vad är egentligen ActionResult?

ActionResult-klassen har en metod vid namn ExecuteResult(ControllerContext context), vilken exekveras när vi kör den.

För att testa funktionaliteten så skapar vi en nya controller med namnet ”CustomerController”. Vi får nu med en action-metod kallad Index. Den körs som standard då det är bestämt så i route-inställningarna i global.asax.

Vår kod har nu det här utseendet:

using System.Web.Mvc;
 
namespace MvcActionResult.Controllers
{
    public class CustomerController : Controller
    {
        //
        // GET: /Customer/
 
        public ActionResult Index()
        {
            return View();
        }
 
    }
}

Surfar vi till /Customer/ så får vi det här felet på sidan:

The view 'Index' or its master was not found. The following locations were searched:
~/Views/Customer/Index.aspx
~/Views/Customer/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx

Det metoden gör i det här fallet är att den letar reda på Index.aspx eller Index.ascx i antingen den delade Shared-mappen (vilken alla controllers kommer åt), eller i Customer-mappen, vilken innehåller aspx-filer för just CustomerController. Då vi inte har skapat någon vy för vår action-metod (i det här fallet en aspx-fil) så får vi felet.

Om vi vill returnera en vanlig aspx-fil passar det utmärkt med en ActionResult då det är just det den gör. Om man kikar i dokumentationen för ActionResult så kan vi se att det dessutom finns en del olika klasser som ärver denna. Det finns även klasser som ärver dessa i sin tur.

De klasser som ärver ActionResult är:

ContentResult
ContentResult är lämplig när man vill returnera till exempel en text, XML eller annat. Man kan ange content som en string, och sedan även ange encoding och content type.

EmptyResult
Namnet säger allt. Ett EmptyResult gör ingenting och returnerar ingenting.

FileResult
Kan användas när man vill returnera en fil. Finns även klasser som ärver denna och är anpassade för t.ex. FileStream.

HttpUnauthorizedResult
Om en användare inte har rätt att se innehållet kan denna returneras.

JavaScriptResult
Då JavaScript används allt mer ökar även kravet på dynamiskt skapad sådan. Med JavaScriptResult kan returnera JavaScript direkt från sin action-metod.

JsonResult
JsonResult är en av mina favoriter, och jag kommer snart att visa hur man kan använda den. Det den gör är att den serialiserar modellen och returnerar som ett Json-objekt. En utmärkt lösning när man arbetar med Ajax.

RedirectResult
Om du vill skicka iväg användare till en annan Url bör du använda RedirectResult.

RedirectToRouteResult
Använd denna om du vill använda route-värden för att skicka en användare till rätt Url. Bör användas istället för RedirectResult om du skall skicka användaren till en sida i samma ASP.NET MVC-projekt.

ViewResultBase
Det här är egentligen ingen action-metod, men ärver ActionResult. Det är här ASP.NET MVC avgör vilken vy som skall returneras till klienten. Det är alltså en väldigt viktig del.

När vi vill använda någon av dessa så kan vi returnera dem rakt av. För att testa det så kan vi ändra i Index-metoden vi har i CustomerController till det här:

public ActionResult Index()
{
    return new ContentResult() {
        Content = "Testar lite!"
    };
}

Om vi besöker sidan nu så kan vi se att vi inte får felmeddelandet om aspx-filerna längre, utan nu får vi istället texten ”Testar lite!” utskriven på skärmen. Anledningen till det är att ContentResult bara returnerar det som vi har sagt åt det att returnera.

För att göra ett mer avancerat exempel så kan vi använda JsonResult. Som jag nämnde tidigare så fungerar det utmärkt med Ajax, så för att testa det kommer jag att använda jQuery för att hämta Json-strängen asynkront.

För att returnera något så behöver vi en modell. I det här fallet så kommer jag inte att skapa en ny typ att returnera, utan kommer istället att skapa en anonym typ för att spara ned på antalet rader kod. Det vi vill returnera är en person med dess namn och hemort.

public ActionResult Customer()
{
    var data = new {
        Name = "Mikael",
        City = "Stockholm"
    };
 
    return Json(data, JsonRequestBehavior.AllowGet);
}

Json returnerar ett JsonResult, och har ett antal överlagrade konstruktorer. I det här fallet anger jag dels modellen (data) samt ett JsonRequestBehavior då jag vill kunna anropa metoden vid GET och inte bara POST.

Surfar vi till Url:en får vi upp:

{"Name":"Mikael","City":"Stockholm"}

Nu väljer jag att skapa en vy för Index-metoden som bara returnerar View(). Här lägger jag in en knapp samt en referens till jQuery.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Testar JsonResult</title>
    <script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(function () {
            $('input').click(function () {
                $.getJSON('/Customer/Customer', function (customer) {
                    alert(customer.Name);
                });
            });
        });
    </script>
</head>
<body>
    <div>
        <input type="button" value="Klicka för namnet!" />
    </div>
</body>
</html>

Om vi nu klickar på knappen så kommer en alert-ruta med texten ”Mikael” att dyka upp. Det är alltså väldigt enkelt att med hjälp av JsonResult returnera modellerna som ren Json utan särskilt mycket arbete.

Men om vi nu vill skapa egna typer av Action-metoder? Det är absolut möjligt att göra, och kan enkelt göras genom att man antingen ärver ActionResult, eller någon av de klasser som redan ärver ActionResult.

Säg att vi vill ha en ImageResult som returnerar bilder direkt i webbläsaren. Det vi behöver göra för att få det är att först ärva ActionResult, och sedan i ExecuteResult välja att skriva ut något direkt till klienten. Denna ImageResult kommer vi sedan att kunna använda i vilken controller vi vill, samt även i andra projekt då den blir väldigt generisk.

För enkelhetens skull har jag valt att ärva direkt i ActionResult nu, men jag skulle även kunna ärva från någon av de klasser som har med binära resultat att göra.

Den färdiga koden ser ut på det här viset:

using System;
using System.IO;
using System.Web.Mvc;
 
namespace MvcActionResult.Code
{
    public class ImageResult : ActionResult
    {
        private string _path;
 
        public ImageResult(string imagePath)
        {
            if (String.IsNullOrWhiteSpace(imagePath))
                throw new ArgumentException("Image path can not be null, empty or whitespace.");
 
            _path = imagePath;
        }
 
        public override void ExecuteResult(ControllerContext context)
        {
            byte[] imageData = File.ReadAllBytes(_path);
            context.HttpContext.Response.ContentType = "image/png";
            context.HttpContext.Response.BinaryWrite(imageData);
        }
    }
}

Jag har först en konstruktor som tar emot en fysisk sökväg till en fil. I ExecuteResult läser jag sedan av filen och returnerar hos klienten.

För att använda action-metoden så skapar jag en ny metod i controllern:

public ActionResult Image()
{
    return new ImageResult(Server.MapPath("~/Content/logo.png"));
}

Om jag nu surfar till /Customer/Image så kan jag se att bilden visas direkt i sidan. Tack vare att det är en action-metod kan jag nu använda vanliga helpers för att generera URL:en. Det är även enkelt att programmatiskt påverka vilken bild som skall visas. Just den här metoden är väldigt enkelt och klarar enbart png-bilder, men den är å andra sidan enkelt att bygga ut.

Då det är så enkelt att skapa egna implementationer av ActionResult så finns det en mängd olika varianter att tillgå på internet för att returnera t.ex. RSS, vCard-filer och annat.

Hör gärna av dig om du har funderingar, eller om du har skrivit någon egen typ av ActionResult!

No Comments