Skip to content

4. Het model van een actie

Laten we teruggaan naar de architectuur van een applicatie ASP.NET MVC:

In het vorige hoofdstuk hebben we gekeken naar het proces dat het verzoek [1] naar de controller en de actie [2a] leidt die het zullen verwerken, een mechanisme dat we 'routing' noemen. Daarnaast hebben we de verschillende reacties besproken die een actie naar de browser kan sturen. Tot nu toe hebben we acties besproken die geen gebruik maakten van het verzoek dat aan hen werd voorgelegd. Een verzoek [1] bevat diverse gegevens die ASP.NET en MVC in de vorm van een model aan de actie [2a] presenteren. Deze term mag niet worden verward met het M-sjabloon van een V-weergave [2c], dat wordt gegenereerd door de actie:

  • de aanvraag HTTP van de klant komt binnen als [1];
  • in [2] wordt de informatie uit het verzoek omgezet in het actiemodel [3], vaak maar niet noodzakelijkerwijs een klasse, dat als invoer dient voor de actie [4];
  • in [4] genereert de actie op basis van dit model een antwoord. Dit antwoord bestaat uit twee onderdelen: een weergave V [6] en het model M van deze weergave [5];
  • de weergave V [6] gebruikt haar model M [5] om de reactie HTTP voor de klant te genereren.

In het model MVC maakt de actie [4] deel uit van de C (controller), is het model van de weergave [5] de M en is de weergave [6] de V.

In dit hoofdstuk worden de koppelingsmechanismen besproken tussen de informatie die door de aanvraag wordt overgedragen – die van nature uit tekenreeksen bestaat – en het model van de actie, dat een klasse kan zijn met eigenschappen van verschillende typen.

4.1. Initialisatie van de actieparameters

We voegen aan de bestaande oplossing een nieuw basisproject ASP.NET MVC toe:

  • in [2], de naam van het nieuwe project;
  • in [3, 4] kiezen we een basisproject ASP.NET MVC;
  • in [5], het nieuwe project.

We maken van het nieuwe project het startproject van de oplossing.

Net zoals in paragraaf 3.1 maken we een controller aan met de naam [First] [1]:

  

In deze controller maken we de volgende actie [Action01] aan:


using System.Web.Mvc;

namespace Exemple_02.Controllers
{
  public class FirstController : Controller
  {
    // Actie01
    public ContentResult Action01(string nom)
    {
      return Content(string.Format("Contrôleur=First, Action=Action01, nom={0}", nom));
    }

  }
}

Het nieuwe zit in regel 8: de methode [Action01] heeft een parameter. In dit hoofdstuk kijken we naar de verschillende manieren om de parameters van een actie te initialiseren. De bovenstaande parameter [nom] wordt achtereenvolgens geïnitialiseerd met de volgende waarden:

Request.Form["nom"]
een parameter met de naam [nom], verzonden door een commando POST
RouteData.Values["nom"]
een element van URL met de naam [nom]
Request.QueryString["nom"]
een parameter met de naam [nom], verzonden door een commando GET
Request.Files["nom"]
een geüpload bestand met de naam [nom]

Laten we deze verschillende gevallen eens bekijken. Laten we URL en [/First/Action01?nom=someone] rechtstreeks in de browser opvragen. We krijgen het volgende antwoord:

 

De aanvraag HTTP vanuit de browser was als volgt:

1
2
3
GET /First/Action01?nom=someone HTTP/1.1
Host: localhost:55483
...
  • regel 1: het verzoek is een GET. Het aangevraagde URL bevat de parameter [nom]. Aan de serverzijde komt het verzoek terecht bij de actie [Action01], die de volgende handtekening heeft:

public ContentResult Action01(string nom)

Om de parameter ‘nom’ een waarde te geven, probeert ASP.NET MVC achtereenvolgens en in de aangegeven volgorde de waarden Request.Form["nom"], RouteData.Values["nom"], Request.QueryString["nom"], Request.Files["nom"]. Het stopt zodra het een waarde heeft gevonden. De parameter [nom], die is ingebed in de URL van de GET, is door het framework geplaatst in Request.QueryString["nom"]. Met deze waarde [someone] wordt de parameter [nom] van [Action01] geïnitialiseerd. Vervolgens wordt de code van [Action01] uitgevoerd:


return Content(string.Format("Contrôleur=First, Action=Action01, nom={0}", nom), "text/plain", Encoding.UTF8);

Deze code geeft het antwoord weer dat naar de klant wordt verzonden:

 

Opmerking: bij het koppelen van parameters wordt geen onderscheid gemaakt tussen hoofdletters en kleine letters. Als onze actie dus is gedefinieerd als:


public ContentResult Action01(string NOM)

en de doorgegeven parameter [?NoM=zébulon] is, zal de koppeling wel degelijk plaatsvinden. De parameter [NOM] van [Action01] krijgt dan de waarde [zébulon].

Laten we nu dezelfde URL opvragen met een POST. Hiervoor gebruiken we de applicatie [Advanced Rest Client]:

  • in [1], de aangevraagde URL;
  • in [2] wordt de opdracht POST gebruikt;
  • in [3], de parameters van POST.

Laten we dit verzoek verzenden en de logbestanden van HTTP bekijken. Het verzoek HTTP is als volgt:

  • in [1], de POST;
  • in [2], de parameters van POST. Technisch gezien zijn deze verzonden achter de headers van HTTP, na de lege regel die het einde van deze headers aangeeft;
  • in [3], het verkregen antwoord. De parameter [nom] van POST wordt inderdaad teruggevonden. Van de geteste waarden voor de parameternaam Request.Form["nom"], RouteData.Values["nom"], Request.QueryString["nom"], Request.Files["nom"], werkte de eerste.

Laten we nu de standaardroute in [App_Start/RouteConfig] aanpassen. Momenteel is deze route als volgt:


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

Laten we deze wijzigen in:


      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{nom}",
          defaults: new { controller = "Home", action = "Index", nom = UrlParameter.Optional }
);
  • op regel 3 hebben we het derde element van een route [nom] genoemd;
  • regel 4: dit element is als optioneel gedefinieerd.

Laten we de applicatie nu opnieuw compileren en URL [/First/Action01/zébulon] rechtstreeks in de browser opvragen. We krijgen het volgende antwoord:

 

Bij de geteste waarden voor de parameter ‘naam’ – Request.Form["nom"], RouteData.Values["nom"], Request.QueryString["nom"], Request.Files["nom"], werkte de tweede.

Laten we dezelfde zoekopdracht uitvoeren met een POST en [Advanced Rest Client]:

  • in [1] hebben we een waarde toegekend aan het element {naam} van de route;
  • in [2] voegen we een parameter [nom] toe aan de verzonden verzoek;
  • het verkregen antwoord is in [3].

Bij de geteste waarden voor de parameter [nom], Request.Form["nom"], RouteData.Values["nom"], Request.QueryString["nom"], Request.Files["nom"], waren er twee geschikt, namelijk de eerste twee. De eerste is gebruikt.

4.2. Controleer de geldigheid van de parameters van de actie

Als een actie een parameter heeft met de naam [p], dan zullen ASP.NET en MVC proberen deze een van de volgende waarden toe te wijzen: Request.Form["p"], RouteData.Values["p"], Request.QueryString["p"], Request.Files["p"] toe te wijzen. De eerste drie waarden zijn tekenreeksen. Als de parameter [p] niet van het type [string] is, kunnen er problemen optreden.

Laten we de volgende nieuwe actie aanmaken:


    // Actie02
    public ContentResult Action02(int age)
    {
      string texte = string.Format("Contrôleur={0}, Action={1}, âge={2}", RouteData.Values["controller"], RouteData.Values["action"],age);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 2: de actie [Action02] accepteert een parameter met de naam [age] van het type int. De opgehaalde tekenreeks moet dus omzetbaar zijn naar int.

Laten we URL [http://localhost:55483/First/Action02?age=21] opvragen. We krijgen de volgende pagina te zien:

 

Laten we URL en [http://localhost:55483/First/Action02?age=21x] opvragen. We krijgen de volgende pagina te zien:

 

Deze keer hebben we een foutpagina ontvangen. Het is interessant om de headers van HTTP te bekijken die de server in dit geval heeft verzonden:

1
2
3
4
5
HTTP/1.1 500 Internal Server Error
...
Content-Type: text/html; charset=utf-8
...
Content-Length: 12438
  • regel 1: de server heeft gereageerd met een code [500 Internal Server Error] en heeft een pagina HTML (regel 3) van 12438 bytes (regel 5) verzonden om de mogelijke oorzaken van deze fout uit te leggen.

Laten we nu de volgende actie [Action03] aanmaken:


    // Actie03
    public ContentResult Action03(int? age)
    {
      ...
}

[Action03] is identiek aan [Action02], behalve dat het type van de parameter [age] is gewijzigd in int?, wat staat voor geheel getal of null.

Laten we URL en [http://localhost:55483/First/Action03?age=21x] opvragen. We krijgen de volgende pagina:

 

ASP.NET MVC is er niet in geslaagd om [21x] naar het type int te converteren. Daarom heeft het de waarde null toegewezen aan de parameter [age], zoals het type int? toestaat. Het is echter mogelijk om te achterhalen of de parameter al dan niet een waarde uit de query heeft ontvangen.

We stellen de volgende nieuwe actie [Action04] samen:


    // Actie04
    public ContentResult Action04(int? age)
    {
      bool valide = ModelState.IsValid;
      string texte = string.Format("Contrôleur={0}, Action={1}, âge={2}, valide={3}", RouteData.Values["controller"], RouteData.Values["action"], age, valide);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 2: het type [int?] is behouden. Dit zorgt er met name voor dat de aanvraag de parameter [age] niet hoeft te verstrekken, die dan de waarde null krijgt;
  • regel 4: er wordt gecontroleerd of het actiemodel geldig is. Het actiemodel bestaat uit alle parameters, in dit geval [age]. Het model is geldig als alle parameters een waarde uit de aanvraag hebben kunnen verkrijgen of de waarde null, indien het type van de parameter dit toestaat;
  • regel 5: de waarde van de variabele [valide] wordt toegevoegd aan de tekst die naar de klant wordt verzonden.

Laten we URL en [http://localhost:55483/First/Action04?age=21x] opvragen. We krijgen de volgende pagina te zien:

 

ASP.NET MVC is er niet in geslaagd om [21x] naar het type int te converteren. Daarom heeft het de waarde null toegewezen aan de parameter [age], zoals het type int toestaat. Maar er zijn conversiefouten opgetreden, zoals blijkt uit de waarde van [valide].

Het is mogelijk dat er een foutmelding verschijnt bij een mislukte conversie. Laten we de volgende nieuwe actie eens bekijken:


    // Actie05
    public ContentResult Action05(int? age)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("Contrôleur={0}, Action={1}, âge={2}, valide={3}, erreurs={4}", RouteData.Values["controller"], RouteData.Values["action"], age, ModelState.IsValid, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

De nieuwigheid zit in regel 4. Daar wordt een privémethode [getErrorMessagesFor] aangeroepen, waaraan de status van het actiemodel wordt doorgegeven. Deze methode retourneert een tekenreeks die de meldingen van alle opgetreden fouten samenvat. Deze methode is als volgt:


private string getErrorMessagesFor(ModelStateDictionary état)
    {
      List<String> erreurs = new List<String>();
      string messages = string.Empty;
      if (!état.IsValid)
      {
        foreach (ModelState modelState in état.Values)
        {
          foreach (ModelError error in modelState.Errors)
          {
            erreurs.Add(getErrorMessageFor(error));
          }
        }
        foreach (string message in erreurs)
        {
          messages += string.Format("[{0}]", message);
        }
      }
      return messages;
    }
  • regel 1: de daadwerkelijke parameter [ModelState] die aan de methode wordt doorgegeven, is van het type [ModelStateDictionary];
  • regel 3: een lijst met foutmeldingen, die aanvankelijk leeg is;
  • regel 5: er wordt getest of de als parameter doorgegeven status geldig is of niet. Zo niet, dan worden alle foutmeldingen samengevoegd tot één enkele tekenreeks;
  • regel 7: het type [ModelStateDictionary] heeft een eigenschap [Values] die een verzameling is van typen [ModelState]. Er is één [ModelState] per element van het model. Bijvoorbeeld:
    • ModelState["age"]: de status van het actiemodel voor de parameter [age],
    • ModelState["age"].Errors: de verzameling fouten voor deze parameter. De fouten zijn van het type [ModelError],
    • ModelState["age"].Errors[i].ErrorMessage: de eventuele foutmelding nr. i voor de parameter [age] van het model
    • ModelState["age"].Errors[i].Uitzondering: de uitzondering van fout nr. i uit de foutverzameling voor de parameter [age],
    • ModelState["age"].Errors[i].Exception.InnerException: de oorzaak van deze uitzondering,
    • ModelState["age"].Errors[i].Exception.InnerException.Message: de melding van de oorzaak van de uitzondering;
  • regel 9: we doorlopen de verzameling [Errors] van een specifieke [ModelState];
  • regel 11: de foutmelding van een specifieke [ModelError] wordt opgehaald en toegevoegd aan de lijst met foutmeldingen van regel 3;
  • regels 14-17: de elementen van de lijst met foutmeldingen worden samengevoegd tot één enkele tekenreeks.

De methode [getErrorMessageFor] uit regel 11 is als volgt:


    private string getErrorMessageFor(ModelError error)
    {
      if (error.ErrorMessage != null && error.ErrorMessage.Trim() != string.Empty)
      {
        return error.ErrorMessage;
      }
      if (error.Exception != null && error.Exception.InnerException == null && error.Exception.Message != string.Empty)
      {
        return error.Exception.Message;
      }
      if (error.Exception != null && error.Exception.InnerException != null && error.Exception.InnerException.Message != string.Empty)
      {
        return error.Exception.InnerException.Message;
      }
      return string.Empty;
}
  • regel 1: er wordt een type [ModelError] ontvangen dat een fout in een van de elementen van het actiemodel bevat. De foutmelding wordt op drie verschillende plaatsen opgehaald:
    • in [ModelError].ErrorMessage, regels 3-6;
    • in [ModelError].Exception.Message, regels 7-10;
    • in [ModelError].Exception.InnerException.Message, regels 11-14;

Tijdens het testen valt op dat de foutmelding op deze drie plaatsen wordt aangetroffen, afhankelijk van de aard van het modelelement. Er moet een regel zijn waarmee je gegarandeerd de foutmelding kunt vinden die bij een modelelement hoort, maar die ken ik niet. Ik zoek er dus naar op de verschillende plaatsen waar ik die kan vinden, en wel in een bepaalde volgorde. Zodra er een niet-lege melding is gevonden, wordt deze geretourneerd.

Laten we eens URL [http://localhost:55483/First/Action05?age=21x] opvragen. We krijgen de volgende pagina te zien:

 

4.3. Een actie met meerdere parameters

Laten we de volgende nieuwe actie eens bekijken:


    // Actie06
    public ContentResult Action06(double? poids, int? age)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, valide={4}, erreurs={5}", RouteData.Values["controller"], RouteData.Values["action"], poids, age, ModelState.IsValid, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 2: we hebben twee parameters, [poids] en [age].

De eerder beschreven regels zijn nu van toepassing op beide parameters. Hier volgen enkele voorbeelden van uitvoering:

 
 

4.4. Een klasse gebruiken als sjabloon voor een actie

Laten we een klasse definiëren die als sjabloon voor een actie zal dienen. We plaatsen deze in de map [Models] [1].

De code ziet er als volgt uit:


namespace Exemple_02.Models
{
  public class ActionModel01
  {
    public double? Poids { get; set; }
    public int? Age { get; set; }
  }
}

Onze klasse heeft als automatische eigenschappen de twee eerder besproken parameters [Poids] en [Age]. Deze klasse zal de invoerparameter zijn voor de actie [Action07]:


    // Actie07
    public ContentResult Action07(ActionModel01 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, valide={4}, erreurs={5}", RouteData.Values["controller"], RouteData.Values["action"], modèle.Poids, modèle.Age, ModelState.IsValid, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 2: het model van de actie is een instantie van het type [ActionModel01].

Laten we dezelfde twee voorbeelden als eerder nemen:

 
 

Merk op dat de koppeling van de parameters niet hoofdlettergevoelig is. De parameters van de query waren [age] en [poids]. Deze hebben de eigenschappen [Age] en [Poids] van de klasse [ModelAction01] gevuld.

Bovendien hebben we tot nu toe de query's HTTP en [GET] gebruikt. Laten we aantonen dat de query's [POST] hetzelfde gedrag vertonen. Hiervoor gebruiken we opnieuw de applicatie [Advanced Rest Client]:

  • in [1], de gevraagde URL;
  • in [2] zal dit gebeuren via een opdracht POST;
  • in [3], de instellingen van POST.

We krijgen hetzelfde antwoord als bij de GET:

Image

4.5. Actiemodel met geldigheidsvoorwaarden - 1

Met het vorige model:


namespace Exemple_02.Models
{
  public class ActionModel01
  {
    public double? Poids { get; set; }
    public int? Age { get; set; }
  }
}

mogen de parameters [poids] en [age] in de aanvraag ontbreken. In dat geval krijgen de eigenschappen [Poids] en [Age] de waarde [null] en wordt er geen fout gemeld. Men zou het model als volgt kunnen aanpassen:


namespace Exemple_02.Models
{
  public class ActionModel01
  {
    public double Poids { get; set; }
    public int Age { get; set; }
  }
}

In de regels 5 en 6 kunnen de eigenschappen [Poids] en [Age] niet langer de waarde [null] hebben. Laten we eens kijken wat er met dit nieuwe model gebeurt wanneer de parameters [poids] en [age] ontbreken in de query.

 

Er zijn geen fouten opgetreden en de eigenschappen [Poids] en [Age] hebben hun initialisatiewaarde behouden: 0. ASP.NET MVC:

  • heeft een instantie van het model aangemaakt met een `new ActionModel01`. Op dat moment hebben de eigenschappen [Poids] en [Age] de waarde 0 gekregen;
  • heeft aan deze twee eigenschappen geen waarde toegekend omdat er geen parameter was met die naam.

Met het eerste model kunnen we controleren of een parameter ontbreekt: de bijbehorende eigenschap krijgt dan de waarde [null]. Met het tweede model is dit niet mogelijk. Het is mogelijk om andere validatiebeperkingen toe te voegen dan alleen het type van de parameters. We zullen deze nu bespreken.

Laten we het volgende nieuwe actiemodel eens bekijken:

  

using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
  public class ActionModel02
  {
    [Required]
    [Range(1, 200)]
    public double? Poids { get; set; }
    [Required]
    [Range(1, 150)]
    public int? Age { get; set; }
  }
}
  • regel 6: geeft aan dat het veld [Poids] verplicht is;
  • regel 7: geeft aan dat het veld [Poids] binnen het bereik [1,200] moet liggen;
  • regel 9: geeft aan dat het veld [Age] verplicht is;
  • regel 7: geeft aan dat het veld [Age] binnen het bereik [1,150] moet vallen;

De actie die dit sjabloon gebruikt, is de volgende actie [Action08]:


    // Actie08
    public ContentResult Action08(ActionModel02 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, valide={4}, erreurs={5}", RouteData.Values["controller"], RouteData.Values["action"], modèle.Poids, modèle.Age, ModelState.IsValid, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 2: de actie ontvangt een instantie van het sjabloon [ActionModel02];

Laten we een paar tests uitvoeren:

 
 
 
 

De fouten worden correct gedetecteerd. Laten we het model nu als volgt aanpassen:


using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
  public class ActionModel02
  {
    [Required]
    [Range(1, 200)]
    public double Poids { get; set; }
    [Required]
    [Range(1, 150)]
    public int Age { get; set; }
  }
}

In de regels 8 en 11 mogen de eigenschappen niet langer de waarde [null] hebben. Laten we het model compileren en de test opnieuw uitvoeren zonder parameters:

 

Doordat er geen parameters zijn, hebben de eigenschappen [Poids] en [Age] hun waarde behouden die ze bij het instantiëren van het model hadden gekregen: 0. Vervolgens vindt de validatie plaats. Aan het attribuut [Required] wordt dan voldaan. We zien dat de bovenstaande foutmelding betrekking heeft op het attribuut [Range]. Om dus te controleren of er een parameter aanwezig is, moet de bijbehorende eigenschap nullable zijn, d.w.z. de waarde null kunnen aannemen.

Laten we teruggaan naar het oorspronkelijke model [ActionModel02] en een actie bekijken waarvan het model bestaat uit een instantie [ActionModel02] en een type [DateTime] nullable:


    // Actie09
    public ContentResult Action09(ActionModel02 modèle, DateTime? date)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, date={4}, valide={5}, erreurs={6}", RouteData.Values["controller"], RouteData.Values["action"], modèle.Poids, modèle.Age, date, ModelState.IsValid, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

Laten we een paar tests uitvoeren:

 

Er zijn geen parameters doorgegeven aan de actie. De attributen [Required] van de eigenschappen [Poids] en [Age] hebben hun rol vervuld. De datum kreeg de waarde null en er werd geen fout gemeld.

We geven nu ongeldige parameters door:

 

We geven nu geldige waarden door:

 

Laten we nog enkele andere geldigheidsvoorwaarden bekijken. Het nieuwe actiemodel is als volgt:

  

using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
  public class ActionModel03
  {
    [Required(ErrorMessage = "Le paramètre email est requis")]
    [EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
    public string Email { get; set; }

    [Required(ErrorMessage = "Le paramètre jour est requis")]
    [RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
    public string Jour { get; set; }

    [Required(ErrorMessage = "Le paramètre info1 est requis")]
    [MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
    public string Info1 { get; set; }

    [Required(ErrorMessage = "Le paramètre info2 est requis")]
    [MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
    public string Info2 { get; set; }

    [Required(ErrorMessage = "Le paramètre info3 est requis")]
    [MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    [MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
    public string Info3 { get; set; }
  }
}
  • regel 6: het attribuut [Required], ditmaal met een foutmelding die we zelf instellen;
  • regel 7: het attribuut [EMailAddress] vereist dat het veld [Email] een e-mailadres in een geldig formaat bevat;
  • regel 11: het attribuut [RegularExpression] vereist dat het veld [Jour] een tekenreeks van één of twee cijfers bevat. De eerste parameter is de reguliere uitdrukking waaraan het veld moet voldoen;
  • regel 15: het attribuut [MaxLength] vereist dat het veld [Info1] maximaal 4 tekens bevat;
  • regel 19: het attribuut [MinLength] schrijft voor dat het veld [Info2] ten minste 2 tekens moet bevatten;
  • regels 23-24: de attributen [MaxLength] en [MinLength] vereisen samen dat het veld [Info3] precies 4 tekens bevat;

De actie [Action10] gebruikt dit sjabloon:


// Actie10
    public ContentResult Action10(ActionModel03 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("email={0}, jour={1}, info1={2}, info2={3}, info3={4}, erreurs={5}",
        modèle.Email, modèle.Jour, modèle.Info1, modèle.Info2, modèle.Info3, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
    }

Laten we een paar tests uitvoeren met deze actie.

Eerst zonder parameters:

 

Vervolgens met ongeldige parameters:

 

Vervolgens met geldige parameters:

 

4.6. Actiemodel met geldigheidsbeperkingen - 2

We introduceren nog enkele integriteitsbeperkingen. Het nieuwe actie-model wordt de volgende klasse [ActionModel04]:


using System.ComponentModel.DataAnnotations;

namespace Exemple_02.Models
{
  public class ActionModel04
  {
    [Required(ErrorMessage="Le paramètre url est requis")]
    [Url(ErrorMessage="URL invalide")]
    public string Url { get; set; }
    [Required(ErrorMessage = "Le paramètre info1 est requis")]
    public string Info1 { get; set; }
    [Required(ErrorMessage = "Le paramètre info2 est requis")]
    [Compare("Info1",ErrorMessage="Les paramètres info1 et info2 doivent être identiques")]
    public string Info2 { get; set; }
    [Required(ErrorMessage = "Le paramètre cc est requis")]
    [CreditCard(ErrorMessage = "Le paramètre cc n'est pas un n° de carte de crédit valide")]
    public string Cc { get; set; }
  }
}
  • regel 8: vereist dat het geannoteerde veld een geldige URL is;
  • regel 13: vereist dat de eigenschappen [Info1] en [Info2] dezelfde waarde hebben;
  • regel 16: vereist dat het geannoteerde veld een geldig creditcardnummer is.

De actie die dit sjabloon gebruikt, is als volgt:


    // Actie11
    public ContentResult Action11(ActionModel04 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("URL={0}, Info1={1}, Info2={2}, CC={3},erreurs={4}",
        modèle.Url, modèle.Info1, modèle.Info2, modèle.Cc, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

Om de actie [Action11] te testen, gebruiken we de applicatie [Advanced Rest Client]:

  • in [1], de URL van de actie [Action11];
  • in [2] wordt deze URL opgevraagd samen met een POST;
  • in [3] selecteert men het tabblad [Form];
  • in [4] de waarden van de vier verwachte parameters. Deze initialisatie is een functie die wordt aangeboden door [ARC]. De daadwerkelijk verzonden parameters zijn te zien in het tabblad [Raw] [5];
  • in [6], de parameters van POST.

Voor deze aanvraag ontvangen we het volgende antwoord:

Laten we eens ongeldige parameters doorgeven:

 

We krijgen dan het volgende antwoord:

4.7. Actiemodel met geldigheidsbeperkingen - 3

Soms volstaan de beschikbare integriteitsbeperkingen niet. In dat geval kunnen we er zelf enkele aanmaken. We kunnen met name een model gebruiken dat de interface [IValidatableObject] implementeert. In dit geval voegen we onze eigen modelcontroles toe aan de methode [Validate] van deze interface. Laten we een voorbeeld bekijken. Het nieuwe model voor de actie wordt de volgende klasse [ActionModel05]:


using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Exemple_02.Models
{
  public class ActionModel05 : IValidatableObject
  {
    [Required(ErrorMessage = "Le paramètre taux est requis")]
    public double? Taux { get; set; }
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
      List<ValidationResult> résultats = new List<ValidationResult>();
      bool ok = Taux < 4.2 || Taux > 6.7;
      if (!ok)
      {
        résultats.Add(new ValidationResult("Le paramètre taux doit être < 4.2 ou > 6.7", new string[] { "Taux" }));
      }
      return résultats;
    }
  }
}
  • regel 6: het model implementeert de interface [IValidatableObject];
  • regel 10: de methode [Validate] van deze interface. Deze methode retourneert een verzameling elementen van het type [ValidationResult]. Dit type kapselt de fouten in die we willen melden;
  • regel 9: een geldig tarief is een tarief <4,2 of > 6,7;
  • regel 12: er wordt een lege lijst met elementen van het type [ValidationResult] aangemaakt;
  • regel 13: de geldigheid van de eigenschap [Taux] wordt gecontroleerd;
  • regels 14-17: als de eigenschap [Taux] ongeldig is, wordt er een element van het type [ValidationResult] aan de resultatenlijst toegevoegd. De eerste parameter is een foutmelding. De tweede parameter, die optioneel is, is een verzameling van de eigenschappen waarop deze fout betrekking heeft.

De actie die dit sjabloon gebruikt, is als volgt:


    // Actie12
    public ContentResult Action12(ActionModel05 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("taux={0}, erreurs={1}", modèle.Taux, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

Hier volgt een voorbeeld van de uitvoering:

 

4.8. Actiesjabloon van het type Tabel of Lijst

Laten we eens kijken naar de volgende actie [Action13]:


// Actie13
    public ContentResult Action13(string[] data)
    {
      string strData = "";
      if (data != null && data.Length != 0)
      {
        strData = string.Join(",", data);
      }
      string texte = string.Format("data=[{0}]", strData);
      return Content(texte, "text/plain", Encoding.UTF8);
    }
  • regel 2: het actiemodel bestaat uit een tabel met de naam [string]. Hiermee kunnen we een parameter met de naam [data] ophalen, die meerdere keren in de queryparameters kan voorkomen, zoals in [?data=data1&data=data2&data=data3]. De verschillende parameters [data] van de verzoek zullen de tabel [data] van het actiemodel vullen. Dit komt voor bij keuzelijsten. De browser stuurt dan de verschillende door de gebruiker geselecteerde waarden, met dezelfde parameternaam.

Hier volgt een voorbeeld:

 

Het model kan ook een lijst zijn:


    // Actie14
    public ContentResult Action14(List<int> data)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string strData = "";
      if (data != null && data.Count != 0)
      {
        strData = string.Join(",", data);
      }
      string texte = string.Format("data=[{0}], erreurs=[{1}]", strData, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

Het model is hier een lijst met gehele getallen (regel 2). Hier volgt een eerste uitvoering:

 

en een tweede:

 

4.9. Een actiemodel filteren

Soms beschikken we over een model, maar willen we dat slechts bepaalde elementen van het model worden geïnitialiseerd door de aanvraag HTTP. Laten we het volgende actiemodel [ActionModel06] eens bekijken:


using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace Exemple_02.Models
{
  [Bind(Exclude = "Info2")]
  public class ActionModel06
  {
    [Required(ErrorMessage = "Le paramètre [info1] est requis")]
    public string Info1 { get; set; }

    public string Info2 { get; set; }
  }
}
  • regels 9-10: de parameter [info1] is verplicht;
  • regel 6: de parameter [info2] van regel 12 is uitgesloten van de koppeling tussen de query HTTP en het bijbehorende sjabloon.

De actie zal als volgt zijn: [Action15]:


    // Actie15
    public ContentResult Action15(ActionModel06 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("valide={0}, info1={1}, info2={2}, erreurs={3}", ModelState.IsValid, modèle.Info1, modèle.Info2, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

Hier volgt een voorbeeld van de uitvoering:

  • in [1]: de parameter [info2] wordt doorgegeven aan URL;
  • in [2]: de eigenschap [Info2] van het actiemodel is leeg gebleven.

4.10. Het gegevenskoppelingsmodel uitbreiden

Laten we nog eens kijken naar de uitvoeringsarchitectuur van een actie:

De actieklasse wordt aan het begin van het verzoek van de klant geïnstantieerd en aan het einde ervan vernietigd. Daarom kan deze niet worden gebruikt om gegevens tussen twee verzoeken op te slaan, zelfs niet als deze herhaaldelijk wordt aangeroepen. Er kunnen twee soorten gegevens worden opgeslagen:

  • gegevens die door alle gebruikers van de webapplicatie worden gedeeld. Dit zijn doorgaans alleen-lezen gegevens. Er worden drie bestanden gebruikt om dit delen van gegevens te implementeren:
    • [Web.Config]: het configuratiebestand van de applicatie
    • [Global.asax, Global.asax.cs]: hiermee kan een klasse worden gedefinieerd, de zogenaamde globale applicatieklasse, waarvan de levensduur gelijk is aan die van de applicatie, evenals handlers voor bepaalde gebeurtenissen van diezelfde applicatie.

Met de globale applicatieklasse kunnen gegevens worden gedefinieerd die beschikbaar zijn voor alle verzoeken van alle gebruikers.

  • gegevens die door de verzoeken van dezelfde client worden gedeeld. Deze gegevens worden opgeslagen in een object dat ‘Session’ wordt genoemd. We spreken dan van een clientsessie om het geheugen van de client aan te duiden. Alle verzoeken van een client hebben toegang tot deze sessie. Ze kunnen er informatie in opslaan en eruit ophalen.

Hierboven laten we de soorten geheugen zien waartoe een actie toegang heeft:

  • het applicatiegeheugen, dat meestal alleen-lezen-gegevens bevat en toegankelijk is voor alle gebruikers;
  • het geheugen van een specifieke gebruiker, of sessie, dat lees- en schrijfgegevens bevat en toegankelijk is voor opeenvolgende verzoeken van dezelfde gebruiker;
  • niet weergegeven hierboven, bestaat er een verzoekgeheugen, of verzoekcontext. Het verzoek van een gebruiker kan door meerdere opeenvolgende acties worden verwerkt. De verzoekcontext stelt actie 1 in staat om informatie door te geven aan actie 2.

Laten we een eerste voorbeeld bekijken dat deze verschillende geheugens illustreert:

Allereerst wijzigen we het bestand [Web.config] van het project [Exemple-02] als volgt:


  <appSettings>
    <add key="webpages:Version" value="2.0.0.0" />
    ...
    <add key="infoAppli1" value="infoAppli1"/>
</appSettings>

We voegen regel 4 toe, die aan de sleutel [infoAppli1] de waarde [infoAppli1] koppelt. Dit wordt onze gegevens met bereik [Application]: deze is toegankelijk voor alle verzoeken van alle gebruikers.

Vervolgens passen we de methode [Application_Start] in het bestand [Global.asax] aan. Deze methode wordt één keer uitgevoerd bij het opstarten van de applicatie. Hier moeten we het bestand [Web.config] gebruiken:


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

      WebApiConfig.Register(GlobalConfiguration.Configuration);
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      BundleConfig.RegisterBundles(BundleTable.Bundles);
      // initialisatie van de applicatie
      Application["infoAppli1"] = ConfigurationManager.AppSettings["infoAppli1"];
}

We voegen regel 10 toe. Deze doet twee dingen:

  • ze haalt de waarde van de sleutel [infoAppli1] op uit het bestand [Web.config] met behulp van de klasse [System.Configuration.ConfigurationManager];
  • ze slaat deze op in het woordenboek [HttpApplication.Application], gekoppeld aan de sleutel [infoAppli1]. Alle acties hebben toegang tot dit woordenboek.

In hetzelfde bestand [Gloabal.asax] wordt de volgende methode [Session_Start] toegevoegd:


    protected void Session_Start()
    {
      // initialisatie teller
      Session["compteur"] = 0;
}

De methode [Session_Start] wordt uitgevoerd voor elke nieuwe gebruiker. Wat is een nieuwe gebruiker? Een gebruiker wordt "gevolgd" door een sessietoken. Dit token wordt:

  • aangemaakt door de webserver en naar de nieuwe gebruiker verzonden in de headers HTTP van het eerste antwoord dat aan hem wordt gegeven;
  • bij elk nieuw verzoek dat de gebruiker doet, door de browser van de gebruiker teruggestuurd. Hierdoor kan de server de gebruiker herkennen en een geheugen voor hem beheren dat de gebruikerssessie wordt genoemd.

De webserver herkent dat hij te maken heeft met een nieuwe gebruiker wanneer deze geen sessietoken verstuurt. De server maakt er dan een voor hem aan.

In regel 4 hierboven plaatsen we in de sessie van de gebruiker een teller die bij elk verzoek van deze gebruiker wordt verhoogd. Dit illustreert het geheugen dat aan een gebruiker is gekoppeld. De klasse [Session] wordt gebruikt als een woordenboek (regel 4).

Vervolgens schrijven we de volgende actie [Action16]:


// Actie16
    public ContentResult Action16()
    {
      // de context van de aanvraag wordt opgehaald HTTP
      HttpContextBase contexte = ControllerContext.HttpContext;
      // de gegevens van het toepassingsbereik worden opgehaald
      string infoAppli1 = contexte.Application["infoAppli1"] as string;
      // en die van het sessiebereik
      int? compteur = contexte.Session["compteur"] as int?;
      compteur++;
      contexte.Session["compteur"] = compteur;
      // het antwoord aan de klant
      string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 5: we halen de context op van de verzoek HTTP dat momenteel wordt verwerkt. Deze context geeft ons toegang tot de gegevens van de scopes [Application] en [Session];
  • regel 7: we halen de scope-informatie van [Application] op;
  • regel 9: we halen de teller uit de sessie op;
  • regels 10-11: deze wordt verhoogd en vervolgens weer in de sessie geplaatst;
  • regels 13-14: beide gegevens worden naar de client verzonden.

Hier volgen enkele uitvoervoorbeelden:

[Action16] wordt een eerste keer opgevraagd, gevolgd door [1], waarna de pagina wordt vernieuwd met [F5], twee keer [2]:

In [2] heeft de klant in totaal drie verzoeken gedaan. Telkens kon hij de teller ophalen die bij het vorige verzoek was bijgewerkt.

Om een tweede gebruiker te simuleren, gebruiken we een tweede browser om hetzelfde URL op te vragen:

In [3] haalt de tweede gebruiker inderdaad dezelfde bereikinformatie op als in [Application], maar heeft hij zijn eigen bereikteller uit [Session].

Laten we teruggaan naar de code van de actie [Action16]:


// Actie16
    public ContentResult Action16()
    {
      // de context van het verzoek wordt opgehaald HTTP
      HttpContextBase contexte = ControllerContext.HttpContext;
      // we halen de gegevens met het bereik 'Application' op
      string infoAppli1 = contexte.Application["infoAppli1"] as string;
      // en die van het sessiebereik
      int? compteur = contexte.Session["compteur"] as int?;
      compteur++;
      contexte.Session["compteur"] = compteur;
      // het antwoord aan de klant
      string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
      return Content(texte, "text/plain", Encoding.UTF8);
}

Een van de doelen van het framework ASP.NET MVC is om controllers en acties afzonderlijk te kunnen testen zonder webserver. Maar op regel 5 zien we dat de context van de verzoek HTTP nodig is om de gegevens uit de scope [Application] en de scope [Session] op te halen. We stellen voor om een nieuwe actie [Action17] aan te maken die de gegevens uit de scopes [Application] en [Session] als parameters zou ontvangen:


    // Actie17
    public ContentResult Action17(ApplicationModel applicationData, SessionModel sessionData)
    {
      // de gegevens van het toepassingsbereik worden opgehaald
      string infoAppli1 = applicationData.InfoAppli1;
      // en die van het bereik 'Sessie'
      int compteur = sessionData.Compteur++;
      // het antwoord aan de klant
      string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
      return Content(texte, "text/plain", Encoding.UTF8);
}

De code is nu niet meer afhankelijk van de query HTTP. Deze kan dus los van een webserver worden getest.

Laten we eens kijken hoe we dit kunnen doen. Allereerst moeten we de klassen [ApplicationModel] en [SessionModel] aanmaken, die respectievelijk de gegevens van de scope [Application] en [Session] zullen inkapselen. Dit zijn de volgende:


namespace Exemple_02.Models
{
  public class ApplicationModel
  {
    public string InfoAppli1 { get; set; }
  }
}

namespace Exemple_02.Models
{
  public class SessionModel
  {
    public int Compteur { get; set; }
    public SessionModel()
    {
      Compteur = 0;
    }
  }
}

Vervolgens moeten we de methoden [Application_Start] en [Session_Start] in het bestand [Global.asax] aanpassen:


public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
      AreaRegistration.RegisterAllAreas();

      WebApiConfig.Register(GlobalConfiguration.Configuration);
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      BundleConfig.RegisterBundles(BundleTable.Bundles);
      // initialisatie van de applicatie - geval 1
      Application["infoAppli1"] = ConfigurationManager.AppSettings["infoAppli1"];
      // initialisatie van de applicatie - geval 2
      ApplicationModel data=new ApplicationModel();
      data.InfoAppli1=ConfigurationManager.AppSettings["infoAppli1"];
      Application["data"] = data;
    }

    protected void Session_Start()
    {
      // initialisatie teller - geval 1
      Session["compteur"] = 0;
      // initialisatie van de teller - geval 2
      Session["data"] = new SessionModel();
    }
  }
  • regel 14: er wordt een instantie van [ApplicationModel] aangemaakt;
  • regel 15: deze wordt geïnitialiseerd;
  • regel 16: en geplaatst in het woordenboek van [Application], gekoppeld aan de sleutel [data]. [Application] is een eigenschap van de klasse [HttpApplication] uit regel 1;
  • regel 24: er wordt een instantie van [SessionModel] aangemaakt en geplaatst in het woordenboek van [Session], gekoppeld aan de sleutel [data]. [Session] is een eigenschap van de klasse [HttpApplication] uit regel 1;

Als we ons houden aan wat we tot nu toe hebben gezien, is de handtekening


    public ContentResult Action17(ApplicationModel applicationData, SessionModel sessionData)

betekent dat de aanvraag HTTP die door de actie wordt verwerkt, parameters met de namen [applicationData] en [sessionData] moet bevatten. Dit zal niet het geval zijn. We moeten een nieuw gegevenskoppelingsmodel aanmaken, zodat wanneer een actie als parameter een type ontvangt:

  • [ApplicationModel] als parameter ontvangt, de gegevens met bereik [Application] en sleutel [data] aan de actie worden doorgegeven;
  • [SessionModel], waarbij de scope-gegevens [Session] en de sleutelgegevens [data] aan deze klasse worden doorgegeven.

Hiervoor moeten klassen worden aangemaakt die de interface [IModelBinder] implementeren.

We beginnen met het aanmaken van een map [Infrastructure] in het project [Exemple-02]:

  

Daarin maken we de volgende klasse [ApplicationModelBinder] aan:


using System.Web.Mvc;

namespace Exemple_02.Infrastructure
{
  public class ApplicationModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      // gegevens van bereik [Application] worden weergegeven
      return controllerContext.RequestContext.HttpContext.Application["data"];
    }
  }
}
  • regel 5: de klasse implementeert de interface [IModelBinder]. Om de code te begrijpen, moet u weten dat deze klasse wordt aangeroepen telkens wanneer een actie een parameter van het type [ApplicationModel] heeft. Deze koppeling [ApplicationModel] --> [ApplicationModelBinder] wordt bij het opstarten van de applicatie tot stand gebracht, in de methode [Application_Start] van [Global.asax];
  • regel 7: de enige methode van de interface [IModelBinder];
  • regel 7: de parameter van het type [ControllerContext] geeft ons toegang tot de aanvraag HTTP die momenteel wordt verwerkt;
  • regel 7: de parameter van het type [ModelBindingContext] geeft ons toegang tot informatie over het te bouwen model, in dit geval het type [ApplicationModel];
  • regel 7: het resultaat van [BindModel] is het object dat aan de gekoppelde parameter zal worden toegewezen, in dit geval een parameter van het type [ApplicationModel];
  • regel 10: we geven simpelweg het object met bereik [Application] en sleutel [data] terug.

De klasse [SessionModelBinder] volgt hetzelfde patroon:


using System.Web.Mvc;

namespace Exemple_02.Infrastructure
{
  public class SessionModelBinder : IModelBinder
  {
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
      // gegevens van bereik [Session] worden weergegeven
      return controllerContext.HttpContext.Session["data"];
    }
  }
}

Nu hoeven we alleen nog maar elk van de modellen [XModel] te koppelen aan zijn binder en [XModelBinder]. Dit gebeurt in de methode [Application_Start] van [Global.asax]:


    protected void Application_Start()
    {
....
      // initialisatie van de applicatie - geval 2
      ApplicationModel data=new ApplicationModel();
      data.InfoAppli1=ConfigurationManager.AppSettings["infoAppli1"];
      Application["data"] = data;
      // modelbinders
      ModelBinders.Binders.Add(typeof(ApplicationModel), new ApplicationModelBinder());
      ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
}
  • regel 9: wanneer een actie een parameter van het type [ApplicationModel] heeft, wordt de methode [ApplicationModelBinder.Bind] aangeroepen. We weten dat deze methode de gegevens met het bereik [Application] teruggeeft die gekoppeld zijn aan de sleutel [data];
  • regel 10: hetzelfde geldt voor het type [SessionModel].

Laten we teruggaan naar onze actie [Action17]:


    // Actie 17
    public ContentResult Action17(ApplicationModel applicationData, SessionModel sessionData)
    {
      // we halen de gegevens op uit het toepassingsbereik
      string infoAppli1 = applicationData.InfoAppli1;
      // en die van het sessiebereik
      sessionData.Compteur++;
      int compteur = sessionData.Compteur;
      // het antwoord aan de klant
      string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 2: wanneer [Action17] wordt aangeroepen, ontvangt deze voor
    • eerste parameter: de scope-gegevens [Application] die gekoppeld zijn aan de sleutel [data],
    • als tweede parameter: de scope-gegevens [Session] die gekoppeld zijn aan de sleutel [data];

Deze twee gegevens kunnen zo complex zijn als men wil en de ene kan alle gegevens van het bereik [Application] omvatten en de andere alle gegevens van het bereik [Session].

Hier volgt een voorbeeld van de uitvoering van de actie [Action17]:

 

4.11. Latere koppeling van het actiemodel

We hebben de volgende actie [Action12] geschreven:


// Actie 12
    public ContentResult Action12(ActionModel05 modèle)
    {
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("taux={0}, erreurs={1}", modèle.Taux, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}

Achter de schermen maakt ASP.NET MVC:

  • maakt een instantie van het type [ActionModel05] aan met behulp van de constructor zonder parameters;
  • initialiseert deze met de informatie uit de aanvraag die dezelfde naam heeft (hoofdlettergevoelig) als een van de eigenschappen van [ActionModel05].

Soms is dit gedrag niet wat we willen. Dit is met name het geval wanneer we een specifieke constructor van het actiemodel willen gebruiken. We kunnen dan als volgt te werk gaan:


    // Actie18
    public ContentResult Action18()
    {
      ActionModel05 modèle = new ActionModel05();
      TryUpdateModel(modèle);
      string erreurs = getErrorMessagesFor(ModelState);
      string texte = string.Format("taux={0}, erreurs={1}", modèle.Taux, erreurs);
      return Content(texte, "text/plain", Encoding.UTF8);
}
  • regel 2: de actie ontvangt geen parameters meer. Er vindt dus geen automatische koppeling van gegevens meer plaats;
  • regel 4: we maken zelf een instantie van het actiemodel aan. Hier zouden we een andere constructor kunnen gebruiken;
  • regel 5: we initialiseren het model met de informatie uit de aanvraag. Dit wordt gedaan door ASP.NET MVC. Dit gebeurt op dezelfde manier als wanneer het model als parameter zou zijn doorgegeven;
  • regel 6: we bevinden ons nu in dezelfde situatie als in de actie [Action12].

Hier volgt een voorbeeld van de uitvoering:

 

4.12. Conclusion

Laten we terugkeren naar de architectuur van een applicatie ASP.NET MVC:

Een verzoek [1] bevat diverse gegevens die ASP.NET MVC aan de actie [2a] presenteert in de vorm van een model dat we het actiemodel hebben genoemd.

  • de aanvraag HTTP van de klant komt binnen in [1];
  • in [2] wordt de informatie in het verzoek omgezet in het actiemodel [3];
  • in [4] genereert de actie op basis van dit model een antwoord. Dit antwoord bestaat uit twee onderdelen: een weergave V [6] en het model M van deze weergave [5];
  • de weergave V [6] gebruikt haar model M [5] om het antwoord HTTP voor de klant te genereren.

In het model MVC maakt de actie [4] deel uit van de C (controller), is het model van de weergave [5] de M en is de weergave [6] de V.

In dit hoofdstuk hebben we de koppelingsmechanismen bestudeerd tussen de informatie die door het verzoek wordt overgedragen – die van nature uit tekenreeksen bestaat – en het model van de actie, dat een klasse kan zijn met eigenschappen van verschillende typen. We hebben ook gezien dat het mogelijk is om de geldigheid van het aan de actie aangeboden model te controleren. Ten slotte hebben we gezien hoe we dit model kunnen uitbreiden naar de gegevens met bereik [Session] en [Application].

We gaan ons nu richten op het einde van de verwerkingsketen van de aanvraag [1]: het aanmaken van de weergave [6] en het bijbehorende model [5]. Deze twee elementen worden gegenereerd door de actie [4].