Table des matières

ASP.NET MVC

ASP.NET MVC est un Framework construit sur le ASP.NET pour appliquer l'architecture MVC.

Démarrer un projet de base

Projet vierge

Le projet qui est offert lorsque vous créez un nouveau projet ASP.NET MVC 2 dans Visual Studio 2010 n'est pas vide. Il est possible de le démarrer (F5) et de le tester. Mais probablement que ce projet ne convient pas exactement à ce qu'on veut faire, alors on supprime les fichiers pour rendre le projet vierge.

Fichiers et dossiers à supprimer

Hello World!

Maintenant que les fichiers inutiles sont supprimés, essayons voir si l'application fonctionne toujours.

Dans le fichier HomeController.cs, entrer le code comme suit :

public class HomeController : Controller
{
    public string Index()
    {
        return "Hello World!";
    }
}

En appuyant sur la touche F5 pour lancer le projet, on devrait voir :

Comment ça fonctionne

Dans l'architecture MVC, les contrôleurs sont responsables de recevoir les requêtes. Dans ASP.NET MVC, les contrôleurs sont de simples classes C#, dérivées de System.Web.Mvc.Controller. Chaque méthode publique du contrôleur sont des méthodes d'action (action methods), ce qui signifie que ces méthodes peuvent être appelées du Web à partir d'URLs. Dans l'exemple précédent, on voit le contrôleur HomeController et l'action Index.

Il y a aussi un système de routing qui décide quel URL mène à quelle action de quel contrôleur. Dans la configuration par défaut, les URLs suivants mène tous à la méthode Index() :

Interface graphique

Il est probablement préférable de revoir le contenu du site pour l'interface générale du site. Les fichiers se trouvent dans le sous-répertoire /Content. Les images et un fichier CSS s'y trouve.

Des modèles d'interfaces sont disponibles gratuitement sur le site de ASP.NET.

Avant d'ajouter des vues au projet, il est préférable de construire une page maitresse qui pourra être appliquée à toutes les vues pour y retrouver un design standard à travers le site. Faire un clic-droit sur les sous-répertoire /Views/Shared et faire AddNew Item…. Dans la liste, sélectionner Master Page que l'on peut appeler Site.Master. Il peut y avoir trois ContentPlaceHolder :

  1. TitleContent
  2. HeadContent
  3. MainContent

Page de connexion simple

Toujours en utilisant un modèle Entity Framework, on peut créer une simple page de connexion. D'autres méthodes existent pour lister les personnes qui ont accès au pages réservées du site, mais ce n'est pas discuté ici. Pour référence, voici les méthodes :

  1. Authentification Windows, utilisé surtout pour les intranets
  2. Authentification par formulaire
  3. Facilités de Membre, Rôles et Profiles, qui est utilisé dans l'application ASP.NET MVC de départ par défaut.

Authentification par formulaire

Première étape, vérifier le fichier web.config pour retrouver ou créer cette section :

<authentication mode="Forms">
  <forms loginUrl="~/Compte/Connecter" timeout="2880"/>
</authentication>

Toutes les demandes de connexion seront redirigées vers le contrôleur CompteController et l'action Connecter sera exécutée. Il faut donc créer le contrôleur et l'action requis.

Contrôleur

public class CompteController : Controller {
    [AcceptVerbs(HttpVerbs.Get)]
    public ViewResult LogOn()
    {
        return View();
    }
 
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult LogOn(string username, string password, string returnUrl)
    {
        if (FormsAuthentication.Authenticate(username, password))
        {
            // Assign a default redirection destination if not set
            returnUrl = returnUrl ?? Url.Action("Index", "Admin");
            // Grant cookie and redirect
            FormsAuthentication.SetAuthCookie(name, false);
            return Redirect(returnUrl); ;
        }
        else
        {
            ViewData["lastLoginFailed"] = true;
            return View();
        }
    }
}

La méthode FormsAuthentication.Authenticate() retourne un booléan, il est donc facile de créer une autre méthode qui fait appel à une entité de Entity Framework. L'utilisateur sera reconnu comme étant connecté grâce au cookie placé par FormsAuthentication.SetAuthCookie(name, false);.

Vue

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Connexion</h2>
    <div>
        <% if((bool?)ViewData["lastLoginFailed"] == true) { %>
        <div class="error">
        Votre connexion n'a pas réussi, veuillez réessayer.
        </div>
        <% } %>
        <% using(Html.BeginForm()) { %>
        <div>Nom d'utilisateur : <%= Html.TextBox("username") %></div>
        <div>Mot de passe : <%= Html.Password("password") %></div>
        <p><%= Html.Hidden("returnUrl", "/")%><input type="submit" value="Connexion" /></p>
        <% } %>
    </div>
</asp:Content>

On peut utiliser le fichier de configuration pour y stocker les informations de connexion, mais ce n'est pas la façon la plus séritaire ni la plus facilement gérable.

<authentication mode="Forms">
  <forms loginUrl="~/Compte/Connecter" timeout="2880">
    <credentials passwordFormat="SHA1">
      <user name="test" password="94ed571854dbfe8be9fe51fadab2fbb9abef436e" />
    </credentials>
  </forms>
</authentication>

Restriction par contrôleur ou par méthode

On peut placer un décorateur [Authorize] sur une classe contrôleur ou sur une méthode.

[Authorize]
public class AdminController : Controller {
    // code
}

Concepts

Routes

Les routes sont la transformation d'un URL entré dans le navigateur à une action précise. Le standard de la route est http://site/controleur/action/donnee/, par exemple http://site/book/view/342.

Les routes sont définies dans Global.asax.cs.

Modèle

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Text;
using System.Net.Mail;
 
namespace WebClient.Models
{
    public class GuestResponse : IDataErrorInfo
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public bool? WillAttend { get; set; }
 
        public void Submit()
        {
            EnsureCurrentlyValid();
            // Send via email
            var message = new StringBuilder();
            message.AppendFormat("Date: {0:yyyy-MM-dd hh:mm}\n", DateTime.Now);
            message.AppendFormat("RSVP from: {0}\n", Name);
            message.AppendFormat("Email: {0}\n", Email);
            message.AppendFormat("Phone: {0}\n", Phone);
            message.AppendFormat("Can come: {0}\n", WillAttend.Value ? "Yes" : "No");
            SmtpClient smtpClient = new SmtpClient();
            smtpClient.Send(new MailMessage(
                "rsvps@example.com",	// From
                "party-organizer@example.com",	// To
                Name + (WillAttend.Value ? " will attend" : " won't attend"), // Subject
                message.ToString()	// Body
            ));
        }
 
        private void EnsureCurrentlyValid()
        {
            // I'm valid if IDataErrorInfo.this[] returns null for every property
            var propsToValidate = new[] { "Name", "Email", "Phone", "WillAttend" };
            bool isValid = propsToValidate.All(x => this[x] == null);
            if (!isValid)
                throw new InvalidOperationException("Can't submit invalid GuestResponse");
        }
 
        public string Error { get { return null; } } // Not required for this example
        public string this[string propName] {
            get
            {
                if ((propName == "Name") && string.IsNullOrEmpty(Name))
                    return "Please enter your name";
                if ((propName == "Email") && !Regex.IsMatch(Email, ".+\\@.+\\..+"))
                    return "Please enter a valid email address";
                if ((propName == "Phone") && string.IsNullOrEmpty(Phone))
                    return "Please enter your phone number";
                if ((propName == "WillAttend") && !WillAttend.HasValue)
                    return "Please specify whether you'll attend";
                return null;
            }
        }
    }
}

Vues

Index.aspx

<body>
    <h1>New Year's Party</h1>
    <p>
        <%= ViewData["greeting"] %>! We're going to have an exciting party.
        (To do: sell it better. Add pictures or something.)
    </p>
    <%= Html.ActionLink("RSVP Now", "RSVPForm") %>
</body>

RSVPForm.aspx

<body>
    <div>
    <h1>RSVP</h1>
 
    <%= Html.ValidationSummary() %>
    <% using (Html.BeginForm())
       { %>
 
       <p>Your name : <%= Html.TextBox("Name") %></p>
       <p>Your email : <%= Html.TextBox("Email") %></p>
       <p>Your phone : <%= Html.TextBox("Phone") %></p>
       <p>
            Will you attend?
            <%= Html.DropDownList("WillAttend", new[] {
                new SelectListItem { Text = "Yes, I'll be there", Value = bool.TrueString },
                new SelectListItem { Text = "No, I can't come", Value = bool.FalseString }
            }, "Choose an option") %> </p>
            <input type="submit" value="Submit RSVP" />
 
    <% } %>
 
    </div>
</body>

Thanks.aspx

<body>
    <div>
    <h1>Thank you, <%= Html.Encode(Model.Name) %>!</h1> <% if(Model.WillAttend == true) { %>
            It's great that you're coming. The drinks are already in the fridge!
        <% } else { %>
            Sorry to hear you can't make it, but thanks for letting us know.
        <% } %>
    </div>
</body>

Contrôleur

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WebClient.Models;
 
namespace WebClient.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ViewResult Index()
        {
            int hour = DateTime.Now.Hour;
            ViewData["greeting"] = (hour < 12 ? "Good morning" : "Good afternoon");
            return View();
        }
 
        [AcceptVerbs(HttpVerbs.Get)]
        public ViewResult RSVPForm()
        {
            return View();
        }
 
        [AcceptVerbs(HttpVerbs.Post)]
        public ViewResult RSVPForm(GuestResponse guestResponse)
        {
            if (ModelState.IsValid)
            {
                guestResponse.Submit();
                return View("Thanks", guestResponse);
            }
            else // Validation error, so redisplay data entry form
                return View();
        }
    }
}

Comment faire

Cookies

var cookie = new HttpCookie("guid", "dsfgafdfasdgdf");
Response.SetCookie(cookie);

JSON

ASP.NET MVC permet de facilement envoyer des données JSON lors d'une requête. Pour traiter avec Javascript. Par exemple, lors de l'utilisation de l'API Google Maps.

public ActionResult MapWithWaypoints()
{
    int? LivraisonCommandeEnCoursId = (int?)Session["LivraisonCommandeEnCoursId"];
    var mapRepository = new MapRepository();
    var map = mapRepository.GetById(LivraisonCommandeEnCoursId.Value);
    return Json(map,JsonRequestBehavior.AllowGet);
}

En Javascript, contenu dans la vue ou dans un fichier à part, on peut faire la requête JSON avec du jQuery :

function getWaypointsFromJSON() {
    $.getJSON("/Commande/MapWithWaypoints", initialise);
}

Sécurité

Authentifier tous les utilisateurs

Authentifier tous les utilisateurs peut servir pour effectuer des tests. Ceci est fait avec une application ASP.NET MVC 5 dans le fichier Global.asax.cs.

protected void Application_AuthenticateRequest()
{
    string[] roles = new string[1];
    roles[0] = "All";
 
    GenericIdentity id = new GenericIdentity("user");
    GenericPrincipal principal = new GenericPrincipal(id, roles);
 
    Context.User = principal;
}

Avec ceci on peut mettre l'attribut [Authorize] sur une classe d'un contrôleur ou sur une méthode Action.

Ressources

Sources