Skip to content

3. Controllers, acties, routing

Laten we eens kijken naar de architectuur van een ASP.NET MVC-toepassing:

In dit hoofdstuk bekijken we het proces dat ervoor zorgt dat het verzoek [1] terechtkomt bij de controller en de actie [2a] die het zullen verwerken, een mechanisme dat ‘routing’ wordt genoemd. Daarnaast bespreken we de verschillende reacties [3] die een actie aan de browser kan geven. Dit kan iets anders zijn dan een V-weergave [4b].

3.1. De structuur van een project ASP.NET MVC

Laten we een eerste project ASP.NET MVC opzetten met Visual Studio Express 2012. We voegen dit [1] toe aan de oplossing die in het vorige hoofdstuk is gebruikt:

  • in [2], de naam van het nieuwe project;
  • [3, 4], waarbij we een basisproject kiezen: ASP.NET en MVC. Dit sjabloon biedt ons een lege webapplicatie met echter alle benodigde bronnen (DLL, JavaScript-bibliotheken, ...) om mee te werken.

Het resulterende project wordt weergegeven in [5]. We maken hiervan [6] het startproject van de oplossing:

In [5] worden de volgende punten genoteerd:

  • de architectuur van het project weerspiegelt het model MVC:
  • de C-controllers worden in de map [Controllers] geplaatst,
  • de M-datamodellen worden in de map [Models] geplaatst,
  • de V-weergaven worden in de map [Views] geplaatst,
  • in [1] is het bestand [Site.css] het standaardbestand van de applicatie;
  • in [2] staan een aantal JavaScript-bibliotheken tot onze beschikking;
  • in [3] zijn drie specifieke weergaven te vinden: _ViewStart, _Layout en Error.

Het bestand [_ViewStart] ziet er als volgt uit:


@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
  • regel 1: het teken @ duidt een C#-reeks in de weergave aan. Het is namelijk mogelijk om C#-code in een weergave op te nemen;
  • regel 2: definieert een variabele Layout die de bovenliggende weergave van alle weergaven definieert. Deze komt overeen met de masterpagina van het klassieke ASP.NET-framework.

Het bestand [_Layout] ziet er als volgt uit:


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    @RenderBody()

    @Scripts.Render("~/bundles/jquery")
    @RenderSection("scripts", required: false)
</body>
</html>

Wanneer een weergave uit de map [Views] wordt weergegeven, wordt de hoofdtekst ervan weergegeven door regel 11 hierboven. Dit betekent dat de weergave de tags <html>, <head> en <body> niet hoeft te bevatten. Deze worden geleverd door het bovenstaande bestand. Dit bestand is op dit moment nogal ondoorzichtig. Laten we het vereenvoudigen:


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Tutoriel ASP.NET MVC</title>
</head>
<body>
  <h2>Tutoriel ASP.NET MVC</h2>
  @RenderBody()
</body>
</html>
  • regel 6: de titel die voor alle weergaven geldt;
  • regel 9: de koptekst die voor alle weergaven geldt;
  • regel 10: de inhoud die specifiek is voor de weergegeven weergave.
  
  • [Web.config] is het configuratiebestand van de webapplicatie. Het is complex. We zullen het moeten aanpassen wanneer we het framework [Spring.net] in een meerlaagse architectuur gebruiken.
  • [Global.asax] bevat de code die bij het opstarten van de applicatie wordt uitgevoerd. Over het algemeen maakt deze code gebruik van de verschillende configuratiebestanden van de applicatie, waaronder [Web.config].

3.2. De standaardroutering van URL

De code van [Global.asax] is momenteel als volgt:


using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Exemple_01
{
 
  public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
   ...
    }
  }
}
  • regel 6, de namespace van de klasse. Deze is rechtstreeks afgeleid van de projectnaam en staat vermeld in de projecteigenschappen:

Image

We stellen [1] in als standaardnaamruimte. Deze wordt dan standaard gebruikt voor alle klassen die in het project worden aangemaakt.

Laten we teruggaan naar de code van [Global.asax]:


using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Exemple_01
{
 
  public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
   ...
    }
  }
}
  • regel 9: de klasse [MvcApplication] is afgeleid van de klasse [HttpApplication]. De naam [MvcApplication] kan worden gewijzigd;
  • regel 11: de methode [Application_Start] is de methode die wordt uitgevoerd bij het opstarten van de webapplicatie. Deze wordt slechts één keer uitgevoerd. Hier wordt de applicatie geïnitialiseerd.

De code van [Application_Start] is momenteel als volgt:


    protected void Application_Start()
    {
      AreaRegistration.RegisterAllAreas();

      WebApiConfig.Register(GlobalConfiguration.Configuration);
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Voorlopig hoeven we niet de volledige code te begrijpen. De regels 5-8 definiëren de routes die door de webapplicatie worden geaccepteerd. Laten we teruggaan naar de architectuur van een ASP.NET MVC-applicatie:

We hebben uitgelegd dat de [Front Controller] een URL moet doorsturen naar de actie die verantwoordelijk is voor de verwerking ervan. Een route dient om een koppeling te leggen tussen een URL-sjabloon en een actie. Deze routes worden in de map [App_Start] van het project gedefinieerd door de klassen [WebApiConfig, FilterConfig, RouteConfig, BundleConfig]:

 

Op dit moment is alleen de klasse [RouteConfig] voor ons van belang:


using System.Web.Mvc;
using System.Web.Routing;

namespace Exemple_01
{
  public class RouteConfig
  {
    public static void RegisterRoutes(RouteCollection routes)
    {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
      );
    }
  }
}

De regels 12-16 definiëren de vorm van de URL die door de applicatie worden geaccepteerd. Dit wordt een route genoemd. Er kunnen meerdere mogelijke routes zijn (= meerdere mogelijke URL-sjablonen). Ze onderscheiden zich van elkaar door hun naam (regel 13). De vorm van de URL van de route wordt gedefinieerd in regel 14. Hier zal de URL drie componenten hebben:

  • {controller}: de naam van een klasse die is afgeleid van [Controller]. Deze wordt gezocht in de map [Controllers] van het project. Volgens afspraak geldt dat, als de URL /X/Y/Z is, de controller die verantwoordelijk is voor de verwerking van deze URL de klasse XController is. Het achtervoegsel Controller wordt toegevoegd aan de naam van de controller in de URL;
  • {action}: de naam van een methode in de hierboven genoemde controller. Deze methode ontvangt de parameters die bij de URL horen en verwerkt deze. Deze methode kan verschillende resultaten opleveren:
    • void: de actie stelt zelf het antwoord aan de clientbrowser samen
    • String: de actie retourneert een tekenreeks naar de client;
    • ViewResult: stuurt een weergave naar de client;
    • PartialViewResult: stuurt een gedeeltelijke weergave terug;
    • EmptyResult: er wordt een leeg antwoord naar de client verzonden;
    • RedirectResult: vraagt de client om door te verwijzen naar een URL
    • RedirectToRouteResult: idem, maar de URL wordt samengesteld op basis van routes in de applicatie;
    • JsonResult: stuurt een JSON-antwoord
    • JavaScriptResult: stuurt een JavaScript-code terug naar de client;
    • ContentResult: stuurt een feed HTML terug naar de client zonder via een weergave te gaan;
    • FileContentResult: stuurt een bestand terug naar de client;
    • FileStreamResult: idem, maar via een andere route;
    • FilePathResult: ...
  • {id}: een parameter die aan de actie wordt doorgegeven. Hiervoor moet de actie een parameter met de naam id hebben.

Regel 15 definieert standaardwaarden wanneer de URL niet de verwachte vorm /{controller}/{action}/{id} heeft. Deze regel geeft ook aan dat de parameter {id} in de URL optioneel is. Hieronder volgt een lijst met onvolledige URL-formaten en de URL aangevuld met de standaardwaarden:

Oorspronkelijke URL
Aangevuld URL
/
/Home/Index
/Do
/Do/Index
/Do/Something
/Do/Something
/Do/Something/4
/Doe/Iets/4
/Doe/Iets/x/y/z
URL niet gerouteerd

3.3. Een controller en een eerste actie aanmaken

Laten we een eerste controller maken:

 
  • in [1] voer de naam van de controller in met het achtervoegsel [Controller];
  • in [2], maak een lege controller MVC aan;
  • in [3] is deze aangemaakt.

De code van [FirstController] is als volgt:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
    public class FirstController : Controller
    {
        //
        // GET: /First/

        public ActionResult Index()
        {
            return View();
        }

    }
}
  • regel 7: er is een standaard namespace gegenereerd;
  • regel 9: de klasse [FirstController] is afgeleid van de klasse [System.Web.Mvc.Controller];
  • regels 14-17: er is standaard een actie [Index] gegenereerd. Deze is openbaar. Dit is belangrijk, anders wordt ze niet gevonden. Ze retourneert een type [ActionResult], een abstracte klasse waarvan de meeste resultaten afleiden die een actie gewoonlijk retourneert. Het is een „verzameltype“ dat kan worden gespecificeerd door het te vervangen door de werkelijke naam van het geretourneerde type;
  • regel 16: de methode doet niets. Ze geeft alleen een weergave terug, dus een type [ViewResult]. De naam van de weergave is niet gespecificeerd. In dit geval zoekt het framework in de map [Views / First] naar een weergave met de naam van de actie, dus in dit geval: [Index.cshtml].

Laten we de weergave [Index.cshtml] aanmaken:

  • in [1] klikken we met de rechtermuisknop in de actiecode en kiezen we de optie [Ajouter une vue];
  • in [2] stelt de wizard een weergave voor met de naam van de actie. Dat is wat we hier willen;
  • in [3] wordt standaard het gebruik van de basispagina [_Layout.cshtml] voorgesteld;
  • in [4] maakt de wizard, zodra deze is bevestigd, de weergave aan in een submap van de map [Views] met de naam van de controller (First).

De gegenereerde code voor de weergave [Index] is als volgt:


@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
  • regels 1-3: C#-code die een variabele definieert;
  • regel 5: een tag HTML.

We vervangen de volledige voorgaande code door deze:


<strong>Vue [Index]...</strong>

Laten we het even samenvatten:

  • we hebben een C-controller met de naam [First];
  • we hebben een actie met de naam [Index] die vraagt om de weergave van een weergave met de naam [Index];
  • we hebben de weergave V [Index].

We kunnen de actie [Index] op twee manieren aanroepen:

  • /First/Index;
  • /First, omdat [Index] ook de standaardactie is in de routes.

Laten we de applicatie uitvoeren (CTRL-F5). We krijgen de volgende pagina te zien:

1

Image

In [1] was de aangevraagde URL http://localhost:49302. Er is geen pad. We weten dat onze router URL verwacht in de vorm /{controller}/{action}/{id}. Aangezien deze elementen ontbreken, worden de standaardwaarden gebruikt. De URL wordt http://localhost:49302/Home/Index. De controller [Home] bestaat niet. Daarom wordt de URL afgewezen.

Laten we nu de URL http://localhost:49302/First/Index proberen door deze rechtstreeks in de browser in te voeren:

De bovenstaande pagina is gegenereerd door de actie [Index] van de controller [First]. De door deze actie gegenereerde pagina is de weergave [Index], waarvan de code als volgt was:


<strong>Vue [Index]...</strong>

Deze genereert het onderdeel [1]. Het onderdeel [2] is afkomstig van de masterpagina [_Layout] die we eerder hebben gedefinieerd:


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Tutoriel ASP.NET MVC</title>
</head>
<body>
  <h2>Tutoriel ASP.NET MVC</h2>
  @RenderBody()
</body>
</html>

Regel 9 heeft het deel [2] van de pagina gegenereerd. De weergave [Index] komt pas in regel 10 voor.

Als we de broncode van de ontvangen pagina bekijken, zien we inderdaad dat de pagina [Index] is opgenomen (regel 10 hieronder) in de pagina [Layout]:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Tutoriel ASP.NET MVC</title>
</head>
<body>
  <h2>Tutoriel ASP.NET MVC</h2>
  <strong>Vue [Index]...</strong>
</body>
</html>

Laten we nu eens kijken naar URL en [/First]:

  

De URL [/First] was onvolledig. Deze is aangevuld met de standaardwaarden van de route en is nu [/First/Index] geworden. We krijgen dus hetzelfde resultaat als eerder.

3.4. Actie met een resultaat van het type [ContentResult] - 1

Laten we een nieuwe actie aanmaken in de controller [First]:


using System.Text;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
  public class FirstController : Controller
  {
    // Index
    public ViewResult Index()
    {
      return View();
    }
    // Actie01
    public ContentResult Action01()
    {
      return Content("<h1>Action [Action01]</h1>", "text/plain", Encoding.UTF8);
    }
  }
}

De nieuwe actie wordt gedefinieerd in de regels 14-18. Deze geeft simpelweg een tekenreeks terug met behulp van de methode [Content] (regel 16) van de klasse [Controller] (regel 6). De parameters van de methode zijn:

  1. de tekenreeks van het antwoord;
  2. een indicator voor het type van de verzonden tekst: "text/plain", "text/html", "text/xml", ... Deze indicator wordt het type MIME (http://fr.wikipedia.org/wiki/Type_MIME) genoemd;
  3. met de derde parameter kan het coderingstype worden gespecificeerd dat voor de tekst wordt gebruikt.

In plaats van het abstracte type [ActionResult] te gebruiken, specificeren onze methoden het daadwerkelijk weergegeven type (regels 9 en 14).

Laten we URL [/First/Action01] opvragen. We krijgen dan de volgende pagina:

  

Laten we de broncode van de ontvangen pagina bekijken:

<h1>Action [Action01]</h1>

De browser heeft alleen de door de actie verzonden tekst ontvangen en verder niets. Deze modus is interessant wanneer men de webserver om pure gegevens wil vragen zonder de HTML-opmaak eromheen. We merken hierboven op dat de browser de tag HTML <h1> niet heeft geïnterpreteerd. Om te begrijpen waarom, kijken we in Chrome naar de uitwisselingen HTTP:

De browser heeft de volgende headers verzonden:

1
2
3
4
5
6
7
8
GET /First/Action01 HTTP/1.1
Host: localhost:49302
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0,8
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

De server heeft hierop gereageerd met de volgende headers:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wMQ==?=
X-Powered-By: ASP.NET
Date: Mon, 23 Sep 2013 15:22:33 GMT
Content-Length: 141
  • regel 3: bepaalt de aard van het document. We zien hier de attributen die zijn ingesteld in de methode [Action01]. Omdat de browser te horen kreeg dat het document van het type "text/plain" was en niet "text/html", heeft hij de tag <h1> in het ontvangen document niet geïnterpreteerd.

3.5. Actie met een resultaat van het type [ContentResult] - 2

Laten we de volgende derde actie eens bekijken:


using System.Text;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
  public class FirstController : Controller
  {
   ...
    // Actie02
    public ContentResult Action02()
    {
      string data = "<action><name>Action02</name><description>renvoie un texte XML</description></action>";
      return Content(data, "text/xml", Encoding.UTF8);
    }
  }
}
  • regel 12: we definiëren een tekst XML;
  • regel 13: deze wordt naar de browser verzonden met de specificatie dat het XML betreft, met het type MIME "text/xml".

In de browser verschijnt de volgende pagina:

 

Laten we in Chrome eens kijken naar het antwoord HTTP van de server:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/xml; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wMg==?=
X-Powered-By: ASP.NET
Date: Mon, 23 Sep 2013 15:29:34 GMT
Content-Length: 176
  • regel 3: bepaalt de aard van het document. We zien hier de attributen die zijn ingesteld in de methode [Action02];
  • regel 12: de grootte in bytes van het door de server verzonden document.

Het door de server verzonden document is dit (kopie uit Chrome):

 

3.6. Actie met een resultaat van het type [JsonResult]

Laten we de volgende actie toevoegen aan de controller [First]:


    // Actie03
    public JsonResult Action03()
    {
      dynamic personne = new ExpandoObject();
      personne.nom = "someone";
      personne.age = 20;
      return Json(personne,JsonRequestBehavior.AllowGet);
}
  • regel 4: een variabele van het type dynamic. Tijdens de uitvoering kunnen we vrijelijk eigenschappen aan een dergelijke variabele toevoegen. De eigenschap wordt aangemaakt op het moment dat deze wordt geïnitialiseerd;
  • regels 5-6: er worden twee eigenschappen geïnitialiseerd: nom en age;
  • regel 7: we retourneren de weergave JSON (JavaScript Object Notation) van het object. Met JSON kan een object worden geserialiseerd tot een tekenreeks en omgekeerd kan een tekenreeks worden gedeserialiseerd tot een object. Dit is een alternatief voor de serialisatie/deserialisatie XML;
  • regel 2: de actie retourneert een type [JsonResult]. Dit type kan alleen worden geretourneerd bij een aanroep van POST. Als men dit type wil retourneren voor een methode GET, moet men aan de tweede parameter van de constructor van de klasse Json (regel 7) de waarde JsonRequestBehavior.AllowGet toekennen.

Wanneer men de URL [/First/Action03] opvraagt, geeft de browser het volgende weer:

 

Het antwoord HTTP van de server is dan als volgt:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wMw==?=
X-Powered-By: ASP.NET
Date: Mon, 23 Sep 2013 15:48:53 GMT
Content-Length: 58
  • regel 3: geeft aan dat het verzonden document JSON is;
  • regel 10: het verzonden document is 58 bytes groot. Het is het volgende:
[{"Key":"nom","Value":"someone"},{"Key":"age","Value":20}]

Het dynamische element [personne] wordt door JSON gezien als een array van woordenboeken, waarbij elk woordenboek:

  • overeenkomt met een veld van de variabele [personne];
  • twee sleutels heeft: „Key” en „Value”. Aan „Key” is de naam van het veld gekoppeld en aan „Value” de waarde van het veld.

3.7. Actie met een resultaat van het type [string]

Laten we de volgende actie toevoegen aan de controller [First]:


    // Actie04
    public string Action04()
    {
      return "<h3>Contrôleur=First, Action=Action04</h3>";
}

Wanneer we URL [/First/Action04] opvragen met Chrome, krijgen we het volgende antwoord:

 

We zien dat de tag <h3> is geïnterpreteerd. Laten we eens kijken naar het antwoord HTTP van de server:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wNA==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 07:49:00 GMT
Content-Length: 156

en het volgende document:

<h3>Contrôleur=First, Action=Action04</h3>

Op regel 3 zien we dat de server heeft aangegeven tekst in het formaat HTML te verzenden. Daarom heeft de browser de tag <h3> geïnterpreteerd. Wanneer we platte tekst willen verzenden, is het dus beter om een [ContentResult] te retourneren in plaats van een [string]. Met [ContentResult] kunnen we namelijk het type MIME "text/plain" specificeren om aan te geven dat we onopgemaakte tekst verzenden, die dus niet door de browser kan worden geïnterpreteerd.

3.8. Actie met een resultaat van het type [EmptyResult]

Neem de volgende nieuwe actie:


    // Actie05
    public EmptyResult Action05()
    {
      return new EmptyResult();
}

De actie geeft alleen een type [EmptyResult] terug. In dit geval stuurt de server een leeg antwoord naar de client, zoals blijkt uit het antwoord HTTP:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Cache-Control: private
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wNQ==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 08:11:12 GMT
Content-Length: 0
  • regel 9: de server geeft aan de client door dat hij een leeg document verstuurt.

3.9. Actie met een resultaat van het type [RedirectResult] - 1

Neem de volgende nieuwe actie:


    // Actie06
    public RedirectResult Action06()
    {
      return new RedirectResult("/First/Action05");
}

De actie retourneert een type [RedirectResult]. Met dit type kan een omleidingsopdracht naar de URL-parameter van de constructor (regel 4) naar de client worden verzonden. De client zal vervolgens een nieuw verzoek GET naar [/First/Action05] verzenden. De client doet dus in totaal twee verzoeken.

De browser geeft het resultaat van het tweede verzoek weer:

 

Laten we het antwoord HTTP van de server in Chrome eens bekijken:

 

Hierboven zien we de twee verzoeken van de browser. Laten we het eerste verzoek [Action06] eens bekijken. Het antwoord HTTP van de server is als volgt:

HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /First/Action05
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wNg==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 08:16:59 GMT
Content-Length: 132
  • regel 1: de server heeft geantwoord met een 302 Found-code. Tot nu toe was dat 200 OK, wat betekent dat het opgevraagde document is gevonden. De 302-code geeft aan dat er een omleiding wordt gevraagd. Het omleidingsadres wordt gegeven in regel 4. Daar vinden we het omleidingsadres terug dat we in de actiecode hadden opgegeven;
  • regel 11: de server geeft aan dat hij met zijn antwoord HTTP een document van het type text/html (regel 3) van 132 bytes (regel 11) verstuurt. Wanneer we in Chrome het antwoord op het verzoek [Action06] bekijken, is dit leeg, zoals te verwachten was. Er is waarschijnlijk een verklaring voor, maar die ken ik niet.

Vanwege de omleiding doet de browser een nieuw verzoek GET naar het in regel 4 hierboven genoemde URL, zoals te zien is in Chrome op regel 1 hieronder:

1
2
3
4
5
6
7
GET /First/Action05 HTTP/1.1
Host: localhost:49302
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0,8
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

3.10. Actie met een resultaat van het type [RedirectResult] - 2

Ofwel de volgende nieuwe actie:


    // Actie07
    public RedirectResult Action07()
    {
      return new RedirectResult("/First/Action05",true);
}

In regel 4 is een tweede parameter toegevoegd aan de constructor van het type [RedirectResult]. Dit is een booleaanse waarde die standaard is ingesteld op false. Wanneer deze wordt ingesteld op true, wordt het antwoord HTTP dat naar de client wordt verzonden, gewijzigd. Het wordt dan:

HTTP/1.1 301 Moved Permanently

De responscode die naar de klant wordt verzonden, is nu dus 301 Moved Permanently. De omleiding verloopt zoals voorheen, maar er wordt aangegeven dat deze omleiding permanent is. Hierdoor kunnen zoekmachines in hun resultaten de oude code URL vervangen door de nieuwe.

3.11. Actie met een resultaat van het type [RedirectToRouteResult]

Neem de volgende nieuwe actie:


    // Actie08
    public RedirectToRouteResult Action08()
    {
      return new RedirectToRouteResult("Default",new RouteValueDictionary(new {controller="First",action="Action05"}));
}
  • regel 2: de actie retourneert een type [RedirectToRouteResult]. Met dit type kan een client worden omgeleid naar een URL die niet wordt gespecificeerd door een tekenreeks zoals eerder, maar door een route.

De routes worden gedefinieerd in [App_Start/RouteConfig]. Er is er momenteel slechts één:


      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
  • In regel 4 wordt de client gevraagd om door te sturen naar de route met de naam [Default] met de variabele controller=First en de variabele action=Action05. Het routeringssysteem genereert vervolgens de omleidingsroute URL naar /First/Action05. Dit blijkt uit het antwoord HTTP van de server:
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /First/Action05
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wOA==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 08:58:19 GMT
Content-Length: 132
  • regel 1: de omleiding;
  • regel 4: het door het routingsysteem van URL gegenereerde omleidingsadres.

3.12. Actie met een resultaat van het type [void]

Neem de volgende nieuwe actie:


    // Actie09
    public void Action09()
    {
      string nom = Request.QueryString["nom"] ?? "inconnu";
      Response.AddHeader("Content-Type", "text/plain");
      Response.Write(string.Format("<h3>Action09</h3>nom={0}", nom));
}
  • regel 2: de actie levert geen resultaat op. Ze schrijft zelf in de responsstroom die naar de klant wordt verzonden;
  • regel 4: er wordt een eventuele parameter met de naam [nom] uit het verzoek opgehaald. Deze is toegankelijk via de eigenschap [Request] van de controller [Controller], waarvan de controller [First] erft. De parameter [nom], doorgegeven in de vorm [/First/Action09?nom=quelquechose], is beschikbaar in Request.QueryString["nom"]. De syntaxis van regel 4 komt overeen met:
string nom=Request.QueryString["nom"];
if(nom==null){
    nom="inconnu";
}
  • regel 5: het naar de client verzonden antwoord is toegankelijk via de eigenschap [Response] van de controller [Controller], waarvan de controller [First] erft;
  • regel 5: de header HTTP [Content-Type] wordt ingesteld, die de aard aangeeft van het document dat de server op het punt staat naar de client te verzenden. Hier geeft "text/plain" aan dat het document platte tekst is die niet door de browser mag worden geïnterpreteerd;
  • regel 6: er wordt een tekenreeks in de responsstroom geschreven. Hierin zijn HTML-tags opgenomen die niet door de browser mogen worden geïnterpreteerd, aangezien deze eerder de header HTTP [Content-Type : text/plain"] heeft ontvangen. Dat is wat we willen controleren.

Laten we het project compileren en eerst de URL [/First/Action09?nom=someone ][1] opvragen en vervolgens deURL [/First/Action09 ] [2]:

Laten we nu in Chrome eens kijken naar het antwoord HTTP van de server:

1
2
3
4
5
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
...
Content-Length: 144
  • regel 3: we zien hier de header HTTP terug die we zelf in de code van de actie hadden ingesteld.

3.13. Een tweede controller

Laten we in het project een tweede controller aanmaken. We volgen de methode die wordt beschreven in paragraaf 3.1, pagina 40. We noemen deze [Second].

  

De gegenereerde code is als volgt:


namespace Exemple_01.Controllers
{
  public class SecondController : Controller
  {
    //
    // GET: /Second/

    public ActionResult Index()
    {
      return View();
    }

  }
}

Laten we deze als volgt aanpassen:


using System.Text;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
  public class SecondController : Controller
  {
    // /Second/Action01
    public ContentResult Action01()
    {
      return Content("Contrôleur=Second, Action=Action01", "text/plain", Encoding.UTF8);
    }

  }
}

Vervolgens roepen we de URL [/Second/Action01] op met een browser. We krijgen het volgende antwoord:

 

Deze URL is opgevraagd met een commando HTTP GET, zoals blijkt uit de logbestanden HTTP van de aanvraag in Chrome:

GET /Second/Action01 HTTP/1.1

URL kan ook worden aangevraagd met de opdrachten HTTP en POST. Om dit te laten zien, gebruiken we opnieuw de applicatie [Advanced Rest Client]:

  • in [1] starten we de applicatie (in het tabblad [Applications] van een nieuw Chrome-tabblad);
  • in [2] selecteren we de optie [Request];
  • in [3], geef je de gevraagde URL op;
  • in [4] geef je aan dat de URL moet worden aangevraagd met een POST;

We schakelen de logbestanden van Chrome in via (CTRL-I) om het antwoord HTTP van de server te ontvangen. Wanneer [Send] wordt uitgevoerd, zijn de uitwisselingen HTTP als volgt:

De browser verstuurt het volgende verzoek:

POST /Second/Action01 HTTP/1.1
Host: localhost:49302
Connection: keep-alive
Content-Length: 0
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
  • regel 1: de URL wordt inderdaad aangevraagd met een POST;
  • regel 4: de grootte in bytes van de verzonden elementen. Die zijn hier niet aanwezig.

Het antwoord HTTP van de server is als volgt:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxTZWNvbmRcQWN0aW9uMDE=?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 10:47:59 GMT
Content-Length: 148
  • regel 3: de server verstuurt een onopgemaakt (plain) tekstdocument;
  • regel 12: van 148 tekens.

Het verzonden document is als volgt:

 

We krijgen hetzelfde document als bij de GET.

3.14. Actie gefilterd op een attribuut

Laten we de volgende nieuwe actie aanmaken:


    // /Second/Action02
    [HttpPost]
    public ContentResult Action02()
    {
      return Content("Contrôleur=Second, Action=Action02", "text/plain", Encoding.UTF8);
}

De actie [Action02] is vergelijkbaar met de actie [Action01], maar er wordt aangegeven dat deze alleen toegankelijk is via de opdracht HTTP POST (regel 2). Er kunnen nog andere attributen worden gebruikt:

HttpGet
is alleen van toepassing op de opdracht GET
HttpHead
is alleen van toepassing op het commando HEAD
HttpOptions
geldt alleen voor de bestelling OPTIONS
HttpPut
geldt alleen voor bestelling PUT
HttpDelete
is alleen van toepassing op de opdracht DELETE

Laten we URL [/Second/Action02] rechtstreeks in de browser opvragen. Deze wordt dan opgevraagd door een GET. De browser geeft vervolgens het volgende antwoord weer:

 

Het antwoord HTTP van de server was als volgt:

1
2
3
HTTP/1.1 404 Not Found
...
Content-Length: 3807
  • regel 1: de code HTTP 404 Not Found geeft aan dat de server het gevraagde document niet heeft gevonden. In dit geval kon de actie [Action02] het verzoek GET niet verwerken, omdat deze actie alleen de opdrachten POST verwerkt;
  • regel 3: de grootte van het teruggestuurde document. Dit is de pagina die door de browser is weergegeven:
 

3.15. Elementen van een route ophalen

In de twee eerder beschreven acties schreven we iets in de trant van:


public ContentResult Action02()
    {
      return Content("Contrôleur=Second, Action=Action02", "text/plain", Encoding.UTF8);
}

De namen van de controller en de actie waren hard gecodeerd in de code. Als we deze namen wijzigen, klopt de code niet meer. We kunnen op de volgende manier toegang krijgen tot de controller en de actie:


    // /Tweede/Actie03
    public ContentResult Action03()
    {
      string texte = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return Content(texte, "text/plain", Encoding.UTF8);
}

De route die is gedefinieerd in [App_Start/RouteConfig] is als volgt:


      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Regel 3: de drie elementen van de route kunnen worden verkregen via RouteData.Values["élément"] met het element in [controller, action, id].

Laten we URL en [http://localhost:49302/Second/Action03] opvragen:

 

We hebben zowel de naam van de controller als de naam van de actie correct opgehaald.