ASP.NET MVC et WEB API

Transcription

ASP.NET MVC et WEB API
1
Asp.Net MVC et WEB API
I.
MVC ......................................................................................................................................................... 4
1. « FROM SCRATCH » - DEMARRER AVEC UN PROJET ASP.NET VIDE................................................................................. 4
2. ROUTES ............................................................................................................................................................. 8
a. RouteConfig ................................................................................................................................................ 8
b. Attribut Route ............................................................................................................................................. 9
c. Area........................................................................................................................................................... 10
d. Récupérer les informations de route ........................................................................................................ 11
3. CONTROLEURS................................................................................................................................................... 12
a. « Action Result » ....................................................................................................................................... 12
b. Index ......................................................................................................................................................... 12
c. Détails ....................................................................................................................................................... 13
d. Create ....................................................................................................................................................... 13
e. Edit ............................................................................................................................................................ 14
f. Delete ........................................................................................................................................................ 14
g. Actions asynchrones ................................................................................................................................. 15
h. Actions Selectors ....................................................................................................................................... 15
i.
Action filters ......................................................................................................................................... 16
j. Contrôleur Mvc comme Web Service ........................................................................................................ 17
4. VIEWS ............................................................................................................................................................. 20
a. _ViewStart ........................................................................................................................................... 22
b. _Layout (Master page/ Page de disposition) ....................................................................................... 22
@RenderSection() ...................................................................................................................................................... 23
c.
d.
e.
Définir le modèle de la vue .................................................................................................................. 23
Html Helpers ........................................................................................................................................ 23
Code expressions pour insérer du C# dans le HTML .......................................................................... 25
Code blocks ................................................................................................................................................................ 25
f.
g.
h.
i.
Scripts .................................................................................................................................................. 25
SelectList .............................................................................................................................................. 26
Moteur de vue Aspx ............................................................................................................................. 26
Ajax Helpers ......................................................................................................................................... 26
Ajax Form ................................................................................................................................................................... 27
Ajax ActionLink........................................................................................................................................................... 28
5. VALIDATION ...................................................................................................................................................... 29
a. Data Annotations................................................................................................................................. 29
b. IValidatableObject ............................................................................................................................... 31
c.
Modifier le style des erreurs ................................................................................................................ 32
d. Validation côté « Client » avec jQuery Validation Plugin ................................................................... 32
6. SECURITE..................................................................................................................................................... 33
a. Windows (Intranet) .............................................................................................................................. 33
b. « From scratch » - Sécurité « Forms » avec un projet Asp.Net vide .................................................... 34
Packages NuGet ......................................................................................................................................................... 34
Préparation ................................................................................................................................................................ 34
« IdentityConfig » (dossier « App_Start ») ............................................................................................................ 34
ApplicationUser..................................................................................................................................................... 36
Classe partielle « Startup » ................................................................................................................................... 37
Base de données ................................................................................................................................................... 38
2
Personnalisation des pages ................................................................................................................................... 38
Contrôleurs et ViewModels .................................................................................................................................. 39
Inscription (register) .................................................................................................................................................. 40
Connexion (Login) ...................................................................................................................................................... 43
Connexion «externe » avec Facebook, Google, Twitter ............................................................................................ 48
Facebook ............................................................................................................................................................... 48
Google ................................................................................................................................................................... 50
Twitter................................................................................................................................................................... 52
Code supplémentaire ............................................................................................................................................ 52
c.
Autorisations (attribut « Authorize »).................................................................................................. 55
ValidateAntiForgeryToken ......................................................................................................................................... 56
AntiXSS ....................................................................................................................................................................... 57
d.
e.
SSL ........................................................................................................................................................ 57
Projet Visual Studio 2012 ..................................................................................................................... 58
InitializeSimpleMembership et WebSecurity ............................................................................................................. 58
SimpleRoleProvider et SimpleMembershipProvider ................................................................................................. 59
Register ...................................................................................................................................................................... 60
Login .......................................................................................................................................................................... 60
7.
LOCALISATION .............................................................................................................................................. 61
Dates et Monnaies........................................................................................................................................ 61
Resources...................................................................................................................................................... 62
8. LESS ........................................................................................................................................................... 63
9. DEPENDENCY INJECTION (DI) .......................................................................................................................... 63
Avec Ninject ............................................................................................................................................................... 63
10.
II.
UPGRADE ................................................................................................................................................ 64
WEB API ................................................................................................................................................. 65
1.
2.
Web Api config..................................................................................................................................... 65
Contrôleur Web Api ............................................................................................................................. 65
a.
b.
c.
d.
3.
4.
HTTP Status codes ............................................................................................................................................ 65
Ajout d’un contrôleur Web Api ........................................................................................................................ 65
Contrôleur Web API avec IhttpActionResult .................................................................................................... 66
Contrôleur Web API avec HttpResponseMessage ............................................................................................ 68
Attribut “Route” ................................................................................................................................... 71
Injection de dépendances .................................................................................................................... 71
Avec Ninject ............................................................................................................................................................... 71
5.
Projet Client ......................................................................................................................................... 72
a.
b.
6.
7.
8.
9.
10.
a.
b.
III.
dataService JavaScript ...................................................................................................................................... 72
HttpClient ......................................................................................................................................................... 73
Cors ...................................................................................................................................................... 75
Formatters pour le « Mapping » .......................................................................................................... 76
OData................................................................................................................................................... 76
Authentification Web Api .................................................................................................................... 77
MongoDB ........................................................................................................................................ 82
Installation de MongoDB C# driver .................................................................................................................. 82
Service WEB ..................................................................................................................................................... 83
BOOTSTRAP ........................................................................................................................................... 86
1.
2.
INSTALLATION ET THEMES ............................................................................................................................... 86
GRID SYSTEM ............................................................................................................................................... 87
a. Cacher une colonne selon une résolution ............................................................................................ 87
b. Décalage de colonnes avec « offset » .................................................................................................. 88
2
3
c.
3.
a.
b.
c.
d.
Image flottante .................................................................................................................................... 88
BASES ......................................................................................................................................................... 88
Typographie ......................................................................................................................................... 88
Boutons, groupes de boutons et dropdowns ....................................................................................... 89
Icones ................................................................................................................................................... 90
Formulaires, listes, et tables ................................................................................................................ 90
« input-group » .......................................................................................................................................................... 90
e.
f.
g.
h.
i.
Navbar desktop et mobile.................................................................................................................... 90
Header et breadcrumb ......................................................................................................................... 91
Pagination............................................................................................................................................ 91
Well ...................................................................................................................................................... 92
Panels .................................................................................................................................................. 92
PLUGINS .............................................................................................................................................................. 92
a. Collapse et accordéon .......................................................................................................................... 92
b. Boite de dialogue ................................................................................................................................. 93
c.
Alert ..................................................................................................................................................... 93
d. Tab ....................................................................................................................................................... 94
e. Tooltip .................................................................................................................................................. 94
f.
Caroussel.............................................................................................................................................. 95
3
4
I.
MVC
ROUTES
Contrôleur
« http://... / » « contrôleur »/« action »/« id »
Va chercher
les données
Modèles/
Données
Action
Result






Vue (Affiche les données)
Vue partielle
« content » (texte, xml)
JSON
Redirection
etc.
1. « From scratch » - Démarrer avec un projet Asp.Net vide
1. Créer un projet Asp.Net vide
2. Installer « Asp.Net Mvc 5 » avec les packages NuGet
3. Ajouter « RouteConfig » dans un dossier « App_Start »
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Web;
System.Web.Mvc;
System.Web.Routing;
namespace MvcFromScratch
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Définition des routes de
l’application. Ici la route par
défaut dirigeant vers l’action
« Index » du contrôleur « Home »
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id =
UrlParameter.Optional }
);
}
}
}
4
5
4. Ajouter fichier de configuration de l’application « Global.asax»
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Web;
System.Web.Mvc;
System.Web.Routing;
namespace MvcFromScratch
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
}
Enregistrement des routes
définies dans le fichier
« RouteConfig »
5. Ajouter un contrôleur « Home » dans un dossier « Controllers »
using
using
using
using
using
System;
System.Collections.Generic;
System.Linq;
System.Web;
System.Web.Mvc;
namespace MvcFromScratch.Controllers
{
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return Content("Bonjour!");
}
}
}
L’ « action result » ici est
une chaine de caractère.
Cela affichera juste ce
message dans une page
Projet minimum
5
6
Avec Vue en « action result »
public ActionResult Index()
{
return View();
}
Vues :


« _ViewStart.cshtml » hérite de « StartPage » (System.Web.Mvc) sert à définir la master
page « par défaut » utilisée par toutes les pages de contenu.
Toutes les autres pages héritent de « WebViewPage » (_Lauout, Index, etc.)
2 possibilités :

Soit définir en haut de chaque vue la page dont elle hérite
Exemple « _ViewStart.cshtml »
@inherits System.Web.WebPages.StartPage
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
« _Layout.cshtml » la master page (ou page de disposition)
@inherits System.Web.Mvc.WebViewPage
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body style="background:red;">
<div>
@RenderBody()
</div>
</body>
</html>
6
7

Soit on crée un fichier de configuration « Web.config » pour le dossier des vues
« Views » pour ne pas avoir à définir sur chaque page
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor"
type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup,
System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection,
System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages"
type="System.Web.WebPages.Razor.Configuration.RazorPagesSection,
System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc,
Version=5.2.2.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="MvcFromScratch" />
</namespaces>
</pages>
</system.web.webPages.razor>
</configuration>
Ainsi on peut simplifier les pages… par exemple « _ViewStart.cshtml »
@{
On supprime l’héritage en
haut des pages
Layout = "~/Views/Shared/_Layout.cshtml";
}
Projet minium avec vues et master page
Les vues sont rangées dans un dossier portant le nom de leur
contrôleur
_Layout.cshtml la master page
_ViewStart.cshtml permet de définir la master page à appliquer par
défaut aux pages de contenu
7
8
2. Routes
a. RouteConfig
Définir les routes de l’application dans « RouteConfig » (dossier « App_Start »)
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", controller="People", id =
UrlParameter.Optional }
);
}
}
Créer une seconde route
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
On place la route au-dessus de
la route « par défaut »
La route
routes.MapRoute(
name: "Ma route",
« http://.../personal/ »
url: "personal/{myparameter}",
defaults: new { controller = "Home", action = "Personal", myparameter =
"Bonjour" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id =
UrlParameter.Optional }
);
}
}
Dans le contrôleur « Home », on ajoute l’action pour cette route
public class HomeController : Controller
{
// code retiré
public ActionResult Personal(string myparameter)
{
return Content(myparameter);
}
}
8
9
Renvoie par défaut la valeur définie dans
« RouteConfig »
Paramètre passé
Dans le cas où on aurait pas défini de paramètre de route
routes.MapRoute(
name: "Ma route",
url: "personal",
defaults: new { controller = "Home", action = "Personal"}
);
… On pourrait quand même passer un paramètre à l’action (code inchangé) en le nommant
b. Attribut Route
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Permettre l’utilisation de
l’attribut « route »
.Attention de bien mettre
avant la route par défaut
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id =
UrlParameter.Optional }
);
}
}
Utilisation : Dans le contrôleur « Home »
[Route("personal")]
public ActionResult Personal()
{
return Content("Route ok!");
}
Si on veut ajouter des paramètres, les mettre entre accolades
[Route("personal/{message}")]
public ActionResult Personal(string message)
{
return Content(message);
}
9
10
c. Area
Il peut être intéressant de découper les gros projets en « Areas »
Exemple
public class MusicAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Music";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Music_default",
"Music/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "AreaDemo.Areas.Music.Controllers" }
);
}
}
« Global.asax »
AreaRegistration.RegisterAllAreas();
10
11
d. Récupérer les informations de route
public class HomeController : Controller
{
// code retiré
public ActionResult Personal()
{
var controller = RouteData.Values["controller"]; //
var action = RouteData.Values["action"]; //
var id = RouteData.Values["id"]; //
var result = string.Format("{0}, {1}, {2}", controller, action, id);
return Content(result);
}
}
Exemple depuis une vue on crée un lien
action
contrôleu
r
paramètre
@Html.ActionLink("A envoyer!", "Personal", new { controller = "Home", id = 10 })
L’adresse du lien « http://localhost:7211/Home/Personal/10 »
Depuis une action du contrôleur
public ActionResult Index()
{
return RedirectToAction("Personal", "Home", new { id = 100 });
}
11
12
3. Contrôleurs
a. « Action Result »
ViewResult : une page complète
public ActionResult Index()
{
IEnumerable<Person> people =
return View(people);
}
_peopleRepository.GetAll();
PartialViewResult : une section de page
return PartialView("_people", people);
ContentResult : texte, xml
return Content("Bonjour!");
JsonResult : objet JSON
return Json(people,JsonRequestBehavior.AllowGet);
RedirectToRouteResult : redirection vers une autre action
return RedirectToRoute(new { controller = "Home", action = "index" });
return RedirectToAction("Index");
JavaScriptResult
return JavaScript("alert('Bonjour!')");
+ EmptyResult, FileResult, HttpUnauthorizedResult, etc.
b. Index
public ActionResult Index()
{
IEnumerable<Person> people =
return View(people);
}
_peopleRepository.GetAll();
12
13
c. Détails
public ActionResult Details(int id)
{
Person person = _peopleRepository.GetOne(id);
if (person == null)
return HttpNotFound("Personne non trouvée");
return View(person);
}
d. Create
public ActionResult Create()
{
Person newPerson = new Person();
return View(newPerson);
}
[HttpPost]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
_peopleRepository.Add(person);
return RedirectToAction("Index");
}
return View(person);
On crée dans un premier temps un
nouvel élément que l’on affiche
dans la vue « Create »
on ajoute le nouvel élément
(post). S’il y a des erreurs de
validation on redirige pour que
l’utilisateur corrige
}
13
14
e. Edit
public ActionResult Edit(int id = 0)
{
Person person = _peopleRepository.GetOne(id);
if (person == null)
return HttpNotFound("Personne non trouvée");
return View(person);
}
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid)
{
_peopleRepository.Update(person);
return RedirectToAction("Index");
}
return View(person);
}
On récupère l’élément à
modifier que l’on affiche
dans la vue « Edit »
on sauve les modifications
apportées s’il n’y a pas
d’erreurs
f. Delete
public ActionResult Delete(int id = 0)
{
Person person = _peopleRepository.GetOne(id);
if (person == null)
return HttpNotFound("Personne non trouvée");
return View(person);
}
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
_peopleRepository.Delete(id);
return RedirectToAction("Index");
}
On récupère l’élément à
supprimer que l’on affiche
Si l’utilisateur confirme la
suppression, on supprime
réellement l’élément.
14
15
g. Actions asynchrones
public async Task<ActionResult> Index()
{
IEnumerable<Person> people = await _peopleRepository.GetAllAsync();
return View(people);
}
h. Actions Selectors
 Attribut « HttpGet » par défaut, inutile de l’indiquer
 Attribut « HttpPost »
ViewBag permet d’afficher des messages passés depuis le contrôleur.
Exemple
Dans le contrôleur
public ActionResult Index()
{
ViewBag.Message1 = "Bonjour!";
ViewBag.Message2 = "Aurevoir :x";
return View();
}
Dans la vue
<h3>@ViewBag.Message1</h3>
<h3>@ViewBag.Message2</h3>
15
16
i.
Action filters
Ce sont des attributs que l’on place au-dessus d’une action ou du contrôleur.

Authorization filter (Attribut « Authorize »)
Autorize et ValidateAntiForgeryToken Voir Authentification
ValidateInput Accepte toutes les entrées (même s’il y a des balises, ce qui peut représenter un
code malveillant)
[HttpPost]
[ValidateInput(true)]
public ActionResult Edit(Person person)
{
//
}
Il est possible également d’utiliser l’attribut « AllowHtml » pour une propriété du modèle
[AllowHtml]
public string LastName { get; set; }


Action filter (faire hériter de « ActionFilterAttribute » et override « OnActionExecuting »,
« OnActionExecuted »)
Result filter : exemple « OutputCache » (met en cache 60 secondes)
//[ChildActionOnly]
[OutputCache(Duration=60)]
public ActionResult Index()
{
IEnumerable<Person> people = _peopleRepository.GetAll();
return View(people);
}

Exception fliter
Pour gérer une « unhandled exception ». Exemple redirige vers une vue
[HandleError(View="Errors")]
public ActionResult Edit(Person person)
{
Pour voir la page erreur comme l’utilisateur (et non page d’erreur « coté serveur »)
16
17
j. Contrôleur Mvc comme Web Service
Même si utiliser un contrôleur Web Api est tout indiqué aujourd’hui, cela reste possible
d’utiliser un contrôleur Mvc comme web service.
public class PeopleWebServiceController : Controller
{
private IPeopleRepository _peopleRepository;
public PeopleWebServiceController()
: this(new FakePeopleRepository())
{ }
public PeopleWebServiceController(IPeopleRepository peopleRepository)
{
this._peopleRepository = peopleRepository;
}
public ActionResult Index()
{
IEnumerable<Person> people = _peopleRepository.GetAll();
return Json(people, JsonRequestBehavior.AllowGet);
}
public ActionResult Details(int id)
{
Person person = _peopleRepository.GetOne(id);
if (person == null)
return HttpNotFound();
return Json(person, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public void Create(Person person)
{
_peopleRepository.Add(person);
}
[HttpPost]
public void Edit(Person person)
{
_peopleRepository.Update(person);
}
public void Delete(int id)
{
_peopleRepository.Delete(id);
}
}
Json.net (documentation)
 Sérialization d’un objet
string json = JsonConvert.SerializeObject(person);
 Désérialization de JSon
JsonConvert.DeserializeObject<Person>(resultContent)
17
18
Tests avec Fiddler
Index et détails
Ajout
Edition
Suppression
18
19
Projet Client
Utilisation de HttpClient(System.Net.Http) et de Json.Net (Projet WinRT, Wpf)
public class PeopleService
{
string urlBase = "http://localhost:15089/peoplewebservice/";
public async Task<Person[]> GetAll()
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(urlBase);
string resultContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Person[]>(resultContent);
}
}
public async Task<Person> GetOne(int personID)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage result = await
client.GetAsync(string.Format("{0}/details/{1}", urlBase, personID));
string resultContent = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Person>(resultContent);
}
}
public async void Add(Person person)
{
using (HttpClient client = new HttpClient())
{
string json = JsonConvert.SerializeObject(person);
HttpResponseMessage response = await
client.PostAsync(string.Format("{0}/create", urlBase),
new StringContent(json, Encoding.UTF8,
"application/json"));
string resultContent = await response.Content.ReadAsStringAsync();
person = JsonConvert.DeserializeObject<Person>(resultContent);
}
}
public async void Update(Person person)
{
using (HttpClient client = new HttpClient())
{
string json = JsonConvert.SerializeObject(person);
HttpResponseMessage response = await
client.PutAsync(string.Format("{0}/edit", urlBase),
new StringContent(json, Encoding.UTF8,
"application/json"));
string resultContent = await response.Content.ReadAsStringAsync();
person = JsonConvert.DeserializeObject<Person>(resultContent);
}
}
public async void Delete(int personID)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await
client.DeleteAsync(string.Format("{0}/delete/{1}", urlBase, personID));
response.EnsureSuccessStatusCode();
}
}
}
19
20
4. Views
Il existe 2 moteurs de vues : Razor (*.cshtml) et Aspx (*.aspx)
(Le code des contrôleurs ne diffère pas selon le moteur de vue utilisé)
On peut ajouter une vue :
- Depuis le menu contextuel sur une action d’un contrôleur
- Sur le répertoire « Views »
Un dossier de vues par contrôleur
Contrôleurs
Un dossier de vues par
contrôleur
Vue
Index
Details
Create
Edit
Delete
…
Action
Index
Details
Create
Edit
Delete
20
21
 Création d’une vue
Choix du modèle de la vue
 Vue partielle
Vue partielle. On a l’habitude
de nommer les vues
partielles avec « _ » pour les
différencier rapidement
Utilisation : Dans la vue
@Html.Partial("_people", Model)
Contrôleur
return PartialView("_people", people);
21
22
a. _ViewStart
Définir la master page par défaut pour les pages de contenu
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
Pour affecter une autre master page à une vue particulière. Définir la master page en haut de la
vue
@{
Layout = "~/Views/Shared/_MyLayout.cshtml";
}
b. _Layout (Master page/ Page de disposition)
(Dossier « Shared »)
Contient tout le HTML commun pour les pages ainsi que RenderBody() pour afficher le contenu
des vues et RenderSection(). On peut créer plusieurs master pages.
Exemple de master page
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<header>
<h1>Titre</h1>
<nav>
<ul>
<li>@Html.ActionLink("Accueil", "Index")</li>
<li>@Html.ActionLink("A propos", "Aboutus")</li>
<li>@Html.ActionLink("Contact", "Contact")</li>
</ul>
</nav>
</header>
<div>
@RenderBody()
</div>
<footer></footer>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
</body>
</html>
22
23
@RenderSection()
Permet de définir ses propres sections
Exemple Dans la master page (_layout.cshtml)
<div>
@if (IsSectionDefined("header"))
{
@RenderSection("header", required: false);
}
else
{
<h3>Header par défaut</h3>
}
</div>
<div class="container">
@RenderBody()
</div>
Dans une vue
@section header
{
<h3>Ma section header</h3>
}
c. Définir le modèle de la vue
Exemple
@model MvcOverview.Models.Person
Ou pour une vue Liste
@model IEnumerable<MvcOverview.Models.Person>
Avec @using
@using MvcOverview.Models
@model IEnumerable<Person>
d. Html Helpers
Documentation
Lien
(Avec paramètre ici)
@Html.ActionLink("Editer", "Edit", new { id = item.Id })
Action
Insère le résultat de l’action (ici la vue « Create »)
<p>@Html.Action("Create")</p>
@url
<a href="@Url.Action("Index","Home")"><img src="~/Images/logo.png" /></a>
23
24
Formulaire
Html
BeginForm
EndForm
TextArea
TextBox
Label
CheckBox
RadioButton
ListBox
Fortement typé
CheckBoxFor
EditorFor
ListBoxFor
RadioButtonFor
TextAreaFor
ValidationMessage
ValidationSummary
Vue en consultation (List, Details)
Label (DisplayNameFor)
@Html.DisplayNameFor(model => model.FirstName)
Affiche la valeur (DisplayFor)
@Html.DisplayFor(model => model.FirstName)
Vue avec édition (Create, Edit)
Label
@Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label
col-md-2" })
… puis champ en edition avec valeur et Validation
@Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "formcontrol" } })
@Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger"
})
Dans un formulaire
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.AntiForgeryToken()
@*
code ici
*@
}
Exemple de formulaire
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>People</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
@* etc. *@
<p>
<input type="submit" value="Valider" />
</p>
</fieldset> }
<div>
@Html.ActionLink("Retour", "Index")
</div>
24
25
e. Code expressions pour insérer du C# dans le HTML
Valeur
<p>there are @Model.Count() people.</p>
@@ Permet de ne pas confondre avec du C#
Plusieurs instructions
@(item.FirstName + ' ' + item.LastName)
Code blocks
Permet par exemple de créer ses variables et les réutiliser
@{
ViewBag.Title = "Index";
var count = Model.Count();
}
<p>there are @count people.</p>
Dans les blocs @if ,@foreach …
@foreach (var item in Model)
{
@:
var fullName = string.Format("{0}, {1}", item.LastName, item.FirstName);
<td>
code block :
</td>
</tr>
@fullName
}
Insérer du texte dans un code block
<text>Mon texte</text>
@:Mon texte
Fonction
@DoSomething()
Exécute la fonction
@helper DoSomething()
{
<span>Bonjour depuis la fonction!</span>
}
f.
Scripts
@section Scripts
{
<script>
$(function () {
});
</script>
}
25
26
g. SelectList
Pour afficher une liste avec sélection de l’élément.
…source, « champ de sélection », « champ affiché »,selectedValue
@Html.DropDownListFor(modelItem => item.CategoryID,new
SelectList(Model.Categories,"CategoryID","CategoryName",item.CategoryID))
public class ClientViewModel
{
public IList<ClientModel> Clients { get; set; }
public IList<CategoryModel> Categories { get; set; }
}
On peut aussi définir « SelectList » dans le modèle
h. Moteur de vue Aspx
Un mot sur le moteur de vue Aspx.Les pages ont pour extension *.aspx.
On utilise :
 <%= %> ou <%: %> pour encadrer une instruction
Ex :<%= Html.ActionLink("Home","Index") %>

<% %> pour encadrer les blocs de code (exemple avec une variable ou
if/foreach)
i. Ajax Helpers
Ajax options
Url
Confirm
OnBegin,OnComplete,OnSuccess,OnFailure
LoadingElementId
LoadingElementDuration
UpdateTargetId
InsertionMode
url requested
Message de confirmation affiché dans une
boite de dialogue
L’élément affiché pendant le « chargement »
(exemple un spinner)
Durée d’affichage de l’élément de chargement
Element modifé
Replace,InsertAfter,InsertBefore
Unobtrusive Ajax
26
27
Ajax Form
Le vue
@model IEnumerable<string>
@{
ViewBag.Title = "Home Page";
}
@* Ajax form *@
@using (Ajax.BeginForm("Index", new AjaxOptions
{
HttpMethod = "get",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "cityList",
LoadingElementDuration = 500,
LoadingElementId = "progress"
}))
{
<input type="search" name="searchTerm" />
<input type="submit" value="Submit" />
}
@* spinner *@
<div id="progress" style="display:none">
<img src="~/Images/spinner.gif" />
</div>
@* partial view *@
@Html.Partial("_cities", Model)
@* unobstrusive Ajax *@
@section scripts
{
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
}
La vue partielle
@model IEnumerable<string>
<div id="cityList">
<table>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item)
</td>
</tr>
}
</table>
</div>
27
28
Le contrôleur
public class HomeController : Controller
{
private static readonly string[] cities =
{
"Shanghai", "Moscou", "Seoul", "Beijing", "Tokyo", "Mexico", "New
York", "Londres","Bangkok","Caire",
"Rio de Janeiro", "Saint Petersburg", "Los Angeles",
"Yokohama","Berlin", "Madrid", "Chicago", "Houston",
"Philadelphie", "Phoenix","San Diego", "Dallas", "Indianapolis", "San
Francisco", "Austin", "Columbus",
"Fort Worth", "Charlotte", "Detroit","Boston", "Washington",
"Denver","Portland", "Las Vegas","Atlanta",
"Colorado Springs", "Omaha", "Miami", "Cleveland",
"Minneapolis","Honolulu", "Buffalo", "Lincoln", "Orlando",
"Chandler", "Laredo", "Madison", "Reno","Irving", "Toronto",
"Montreal", "Vancouver", "Calgary", "Edmonton", "Quebec",
};
public ActionResult Index(string searchTerm = null)
{
if (Request.IsAjaxRequest())
if (searchTerm != null)
{
var result = cities.Where(c =>
c.ToLower().StartsWith(searchTerm.ToLower()));
return PartialView("_cities", result);
}
return View(cities);
}
}
Ajax ActionLink
@Ajax.ActionLink("City List", "Index", new AjaxOptions { UpdateTargetId = "cityList",
HttpMethod = "get" })
28
29
5. Validation
Pour bien faire il faut une validation côté client et une validation à la mise à jour côté serveur.
a. Data Annotations
Documentation
On distingue :
Les attributs servant à mettre en forme




Display
DisplayFormat
DisplayColumn pour la clé étrangère
DataType permet de mettre en forme (exemple password)
Les attributs de validation:







Required
StringLength
MaxLength
MinLength
Compare
Range
RegularExpression
Attribut Key
Namespace « Schema »







Table
Column
ComplexType
DatabaseGeneratded
InverseProperty
ForeignKey
NotMapped
29
30
Custom Attribute
Exemple création d’un attribut permettant de valider Twitter
public class TwitterAttribute : ValidationAttribute
{
private string pattern = "^@([A-Za-z0-9_]+)";
protected override ValidationResult IsValid(object value, ValidationContext
validationContext)
{
if (value != null)
{
var valueAsString = value.ToString();
if (!Regex.IsMatch(valueAsString, "^@([A-Za-z0-9_]+)"))
{
return new ValidationResult("Enter a valide twitter");
}
}
return ValidationResult.Success;
}
private readonly int _maxWords;
}
30
31
Utilisation
[Display(Name = "Twitter : ")]
[Twitter]
public string Twitter { get; set; }
Contrôleur
[HttpPost]
public ActionResult Create(Person person)
{
ValidatePerson(person);
if (ModelState.IsValid)
{
_peopleRepository.Add(person);
return RedirectToAction("Index");
}
return View(person);
}
private void ValidatePerson(Person person)
{
string twitter = person.Twitter;
if (string.IsNullOrWhiteSpace(twitter))
return;
Vérifie si le formulaire
ne contient pas
d’erreurs
on ajoute une erreur au
modèle
if (!Regex.IsMatch(twitter, "^@([A-Za-z0-9_]+)"))
ModelState.AddModelError("Twitter", "Twitter invalide.");
}
b. IValidatableObject
Le modèle peut implémenter IValidatableObject, la méthode « Validate » sera automatiquement
appelée …
public class Person : IValidatableObject
{
// … properties
public IEnumerable<ValidationResult> Validate(ValidationContext
validationContext)
{
if (!Regex.IsMatch(Twitter, "^@([A-Za-z0-9_]+)"))
{
yield return new ValidationResult("Enter a valide twitter", new[] {
"Twitter" });
}
}
}
31
32
c. Modifier le style des erreurs
.input-validation-error {
border: 1px solid #e64343;
box-shadow: 0 0 2px 0 rgba(230, 67, 67, 0.4);
}
.input-validation-error:focus {
background: #fcecec;
border: 1px solid #e64343;
box-shadow: 0 0 2px 0 rgba(230, 67, 67, 0.4);
}
.validationMessage {
color: Red;
}
d. Validation côté « Client » avec jQuery Validation Plugin
http://jqueryvalidation.org/
Référence
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
Exemple pour afficher un message d’erreur
<div>
@Html.LabelFor(model => model.FirstName)
@Html.TextBoxFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
32
33
6. Sécurité
Articles sur la sécurité Asp.Net
a. Windows (Intranet)
<p>Bonjour, @User.Identity.Name!</p>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.1" />
<httpRuntime targetFramework="4.5.1" />
<authentication mode="Windows" />
</system.web>
</configuration>
33
34
b. « From scratch » - Sécurité « Forms » avec un projet Asp.Net vide
Packages NuGet
Besoin de :
 EntityFramework
 AspNet.Identity.Core
 Identity.EntityFramework
 Identity.Owin
 Microsoft.Owin.Host.SystemWeb (méthodes d’extension)
Depuis la console du gestionnaire de package
PM>
PM>
PM>
PM>
PM>
+
Install-Package
Install-Package
Install-Package
Install-Package
Install-Package
EntityFramework
Microsoft.AspNet.Identity.Core
Microsoft.AspNet.Identity.EntityFramework
Microsoft.AspNet.Identity.Owin
Microsoft.Owin.Host.SystemWeb
Si on utilise Facebook, Google, Twitter, etc. pour une connexion externe.
PM> install-package Microsoft.Owin.Security.Facebook
PM> install-package Microsoft.Owin.Security.Google
PM> install-package Microsoft.Owin.Security.Twitter
Préparation
« IdentityConfig » (dossier « App_Start »)
using
using
using
using
using
using
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.Data.Entity;
System.Linq;
System.Security.Claims;
System.Threading.Tasks;
System.Web;
Microsoft.AspNet.Identity;
Microsoft.AspNet.Identity.EntityFramework;
Microsoft.AspNet.Identity.Owin;
Microsoft.Owin;
Microsoft.Owin.Security;
MvcFromScratch.Models;
System.Net.Mail;
namespace MvcFromScratch
{
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
var client = new SmtpClient("smtp.xyz.fr");
client.Send("[email protected]", message.Destination, message.Subject,
message.Body);
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Connectez votre service SMS ici pour envoyer un message texte.
34
35
return Task.FromResult(0);
}
}
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
Configuration de ApplicationUserManager
}
public static ApplicationUserManager
Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new
UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
Configuration du nom d’utilisateur
AllowOnlyAlphanumericUserNames = false,
(autoriser les chiffres dans les
RequireUniqueEmail = true
};
noms ? email uniques ?)
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
Configuration du mot de passe
(longueur min. ? lettre ou digit
requis ? upper case requis ? )
Verrouillage après 5 erreurs
d’authentification
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
manager.RegisterTwoFactorProvider("Code téléphonique ", new
PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Votre code de sécurité est {0}"
});
manager.RegisterTwoFactorProvider("Code d'e-mail", new
EmailTokenProvider<ApplicationUser>
{
Subject = "Code de sécurité",
BodyFormat = "Votre code de sécurité est {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new
DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET
Identity"));
}
return manager;
}
}
// Configurer le gestionnaire de connexion d'application
35
36
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager,
IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser
user)
{
return
user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager
Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext
context)
{
return new
ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(),
context.Authentication);
}
}
}
ApplicationUser
Dans le dossier « Models »
using
using
using
using
using
System.Data.Entity;
System.Security.Claims;
System.Threading.Tasks;
Microsoft.AspNet.Identity;
Microsoft.AspNet.Identity.EntityFramework;
namespace MvcFromScratch.Models
{
// consultez http://go.microsoft.com/fwlink/?LinkID=317594
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this,
DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
36
37
Classe partielle « Startup »
Dans le dossier « App_Start »
using
using
using
using
using
using
using
using
System;
Microsoft.AspNet.Identity;
Microsoft.AspNet.Identity.Owin;
Microsoft.Owin;
Microsoft.Owin.Security.Cookies;
Microsoft.Owin.Security.Google;
Owin;
MvcFromScratch.Models;
namespace MvcFromScratch
{
public partial class Startup
{
// rendez-vous sur http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Autoriser l’application à utiliser un cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Permet à l'application de valider le timbre de sécurité
OnValidateIdentity =
SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) =>
user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie,
TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrow
serCookie);
app.UseFacebookAuthentication(
Connexions
appId: "475813752569575",
« externes »
appSecret: "123456789abcdefg ");
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "903536450598ps3vsah291ef6iekkqtqflc2d0g746hj.apps.googleusercontent.com",
ClientSecret = "123456789abcdefg"
});
// Supprimer les commentaires
//app.UseTwitterAuthentication(
//
consumerKey: "",
//
consumerSecret: "");
}
}
}
37
38
A la racine du projet
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(MvcFromScratch.Startup))]
namespace MvcFromScratch
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
Base de données
 Créer le dossier spécial « App_Data » dans lequel la base de données sera créée
 Chaine de connexion dans « Web.config »
On nomme la base qui sera créée « Identity »
<connectionStrings>
par exemple
<add name="DefaultConnection" connectionString="Data
Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Identity.mdf;Initial
Catalog=Identity;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
« AspNetUsers » Contient tous les utilisateurs
(email, userName, passwordhash,etc.).
Pas de mot de passe pour les utilisateurs
connectés en « externe » (facebook, etc.) , mais
ajout de l’Id dans « AspNetUserLogins »
Personnalisation des pages
Dans une vue partielle(_LoginPartial) ou la master page (_Layout)
@using Microsoft.AspNet.Identity
@if (Request.IsAuthenticated)
{
<h2>Bonjour @User.Identity.GetUserName() !</h2>
@Html.ActionLink("Se déconnecter", "Logoff", "Account")
On pourrait également vérifier si
l’utilisateur appartient à un rôle,
pour donner accès à la gestion du
site par exemple
}
else
{
<li>@Html.ActionLink("S’inscrire", "Register", "Account")</li>
<li>@Html.ActionLink("Se connecter", "Login", "Account")</li>
}
38
39
Contrôleurs et ViewModels
 Créer « AccountController » dans le dossier « Controllers »
using
using
using
using
using
using
using
using
using
using
using
MvcFromScratch.Models;
System;
System.Globalization;
System.Linq;
System.Security.Claims;
System.Threading.Tasks;
System.Web;
System.Web.Mvc;
Microsoft.AspNet.Identity;
Microsoft.AspNet.Identity.Owin;
Microsoft.Owin.Security;
namespace MvcFromScratch.Controllers
{
[Authorize]
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public ApplicationSignInManager SignInManager
{
get { return _signInManager ??
HttpContext.GetOwinContext().Get<ApplicationSignInManager>(); }
private set { _signInManager = value; }
}
public ApplicationUserManager UserManager
{
get { return _userManager ??
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
private set { _userManager = value; }
}
private IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}
private IAuthenticationManager AuthenticationManager
{
Get { return HttpContext.GetOwinContext().Authentication; }
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
ModelState.AddModelError("", error);
}
}
}

Créer « AccountViewModels » qui contiendra tous les ViewModels dans le dossier
« Models »
39
40
Inscription (register)
« AccountController »
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email =
model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
on envoie un email de demande
de confirmation
// Avec confirmation
string code = await
UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new {
userId = user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Confirmez votre
compte", "Confirmez votre compte en cliquant <a href=\"" + callbackUrl +
"\">ici</a>");
ViewBag.Message = "Un email vous a été envoyé afin de confirmer
votre compte.";
Sans demande de confirmation, on connecte
return View("Info");
directement l’utilisateur après l’inscription et le
redirige vers la page d’accueil
// Sans confirmation
// await SignInManager.SignInAsync(user, isPersistent: false,
rememberBrowser: false);
//return RedirectToAction("Index", "Home");
}
AddErrors(result);
Réaffiche le formulaire en cas d’erreurs de validation
}
return View(model);
}
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
40
41
ViewModels
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2}
caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le mot de passe ")]
[Compare("Password", ErrorMessage = "Le mot de passe et le mot de passe de
confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
}
Vues:
-
Register (pour s’inscrire), ConfirmEmail (affichée après que l’utilisateur ait cliqué sur le lien
de confirmation de l’email envoyé) …Vues dans le dossier « Account » des vues « Views »
Info, Error (dossier « Shared »)
Register
@model MvcFromScratch.Models.RegisterViewModel
@{
ViewBag.Title = "S’inscrire";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("Register", "Account", FormMethod.Post))
{
@Html.AntiForgeryToken()
<h4>Créer un nouveau compte.</h4>
<hr />
@Html.ValidationSummary()
<div>
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email)
</div>
<div>
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password)
</div>
<div>
@Html.LabelFor(m => m.ConfirmPassword)
@Html.PasswordFor(m => m.ConfirmPassword)
</div>
<div>
<input type="submit" class="btn btn-default" value="Inscription" />
</div>
}
41
42
ConfirmEmail
@{
ViewBag.Title = "Confirmation de l'e-mail";
}
<h2>@ViewBag.Title.</h2>
<div>
<p>
Merci d'avoir confirmé votre e-mail. Veuillez @Html.ActionLink("Cliquer ici
pour vous connecter", "Login", "Account")
</p>
</div>
Info
@{
ViewBag.Title = "Info";
}
<h2>@ViewBag.Title.</h2>
<h3>@ViewBag.Message</h3>
Error
@{
ViewBag.Title = "Erreur";
}
<h1>Erreur</h1>
<p>@ViewBag.ErrorMessage</p>
42
43
Connexion (Login)
« AccountController »
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
Vérifie que le formulaire ne
if (!ModelState.IsValid)
Vérifie que l’utilisateur a
{
comporte pas d’erreurs
bien validé son compte avec
return View(model);
l’email
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user != null)
{
if (!await UserManager.IsEmailConfirmedAsync(user.Id))
{
ViewBag.ErrorMessage = "Vous devez confirmer votre email.";
return View("Error");
}
}
var result = await SignInManager.PasswordSignInAsync(model.Email,
model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl,
RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Tentative de connexion non
valide.");
return View(model);
}
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
Déconnexion
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
}
43
44
Mot de passe oublié
// GET: /Account/ForgotPassword
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
}
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await
UserManager.IsEmailConfirmedAsync(user.Id)))
{
return View("ForgotPasswordConfirmation");
}
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId
= user.Id, code = code }, protocol: Request.Url.Scheme);
await UserManager.SendEmailAsync(user.Id, "Reset Password", "Cliquez
sur ce lien pour réinitaliser votre mot de passe : " + callbackUrl);
ViewBag.Link = callbackUrl;
return View("ForgotPasswordConfirmation");
}
return View(model);
}
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
var id = Request.QueryString["userid"];
var model = new ResetPasswordViewModel() { Id = id, Code = code };
return code == null ? View("Error") : View(model);
}
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByIdAsync(model.Id);
if (user == null)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code,
model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
44
45
return View();
}
// GET: /Account/ResetPasswordConfirmation
[AllowAnonymous]
public ActionResult ResetPasswordConfirmation()
{
return View();
}
ViewModels
public class LoginViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[Display(Name = "Mémoriser le mot de passe ?")]
public bool RememberMe { get; set; }
}
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "E-mail")]
public string Email { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
public string Id { get; set; }
[Required]
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2}
caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le mot de passe")]
[Compare("Password", ErrorMessage = "Le nouveau mot de passe et le mot de
passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
45
46
Vues:
-
Login (pour se connecter) …Vue dans le dossier « Account » des vues « Views »
Pour la réinitalisalisation de mot de passe : ForgotPassword, ForgotPasswordConfirmation,
ResetPassword, ResetPasswordConfirmation
Lockout (affichée si l’utilsateur se trompe trop de fois dans ses identifiants), Error (dossier
« Shared »)
Login
@using MvcFromScratch.Models
@model LoginViewModel
@{
ViewBag.Title = "Connexion";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl },
FormMethod.Post))
{
@Html.AntiForgeryToken()
<h4>Utilisez un compte local pour vous connecter.</h4>
<hr />
@Html.ValidationSummary(true)
<div>
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email)
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
</div>
<div>
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password)
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger"
})
</div>
<div>
@Html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)
</div>
<div>
<input type="submit" value="Connexion" />
</div>
<p>
@Html.ActionLink("Mot de passe oublié?", "ForgotPassword")
</p>
<p>
@Html.ActionLink("S'inscrire comme nouvel utilisateur", "Register")
</p>
}
<div>
@Html.Partial("_ExternalLoginsListPartial", new ExternalLoginListViewModel {
ReturnUrl = ViewBag.ReturnUrl })
</div>
46
47
ForgotPasword
@model MvcFromScratch.Models.ForgotPasswordViewModel
@{
ViewBag.Title = "Vous avez oublié votre mot de passe ?";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post))
{
@Html.AntiForgeryToken()
<h4>Entrez une adresse de messagerie.</h4>
<hr />
@Html.ValidationSummary()
<div>
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email)
</div>
<div>
<input type="submit" value="Lien de courrier électronique" />
</div>
}
ForgotPasswordConfirmation
@{
ViewBag.Title = "Confirmation du mot de passe oublié";
}
<hgroup class="title">
<h1>@ViewBag.Title.</h1>
</hgroup>
<div>
<p>
Vérifiez votre adresse de messagerie pour réinitialiser votre mot de passe.
</p>
</div>
ResetPasword
@model MvcFromScratch.Models.ResetPasswordViewModel
@{
ViewBag.Title = "Réinitialiser le mot de passe";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post))
{
@Html.AntiForgeryToken()
<h4>Réinitialisez votre mot de passe.</h4>
<hr />
@Html.ValidationSummary()
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.Code)
<div>
@Html.LabelFor(m => m.Password)
@Html.PasswordFor(m => m.Password)
</div>
<div>
@Html.LabelFor(m => m.ConfirmPassword)
@Html.PasswordFor(m => m.ConfirmPassword)
</div>
<div>
<input type="submit" value="Réinitialiser" />
</div>
}
47
48
ResetPasswordConfirmation
@{
ViewBag.Title = "Mot de passe réinitalisé";
}
<hgroup class="title">
<h1>@ViewBag.Title.</h1>
</hgroup>
<div>
<p>
Votre mot de passe a été réinitialisé. Veuillez @Html.ActionLink("cliquer ici
pour vous connecter", "Login", "Account", routeValues: null, htmlAttributes: new { id
= "loginLink" })
</p>
</div>
Connexion «externe » avec Facebook, Google, Twitter
Facebook
Développeurs Facebook
1. Créer une nouvelle application : Menu « My Apps » « add a new app » … web
2. Donner un nom à l’application puis « Create App Id »
3. Donner l’url du site « http://localhost:7211/ » par exemple et un email
Cliquer sur show
(demande le mot de
passe facebook) pour
afficher « app secret »
48
49
4. Passer le statut de l’application en « public » (on peut créer aussi une application de test)
5. Si ce n’est fait installer le package …
PM> install-package Microsoft.Owin.Security.Facebook
6. Rendez-vous dans « Startup.Auth.cs » dossier « App_Start », dé-commenter et entrer l’app id
et l’app secret
app.UseFacebookAuthentication(
appId: "475813752569575",
appSecret: "123456789abcdefg");
L’utilisateur est ajouté dans la table « Asp.NetUsers » sans mot de passe ainsi que dans la table
« AspNetUserLogins » avec son « UserId »
49
50
Google
Développeurs Google
1. Créer un projet (donner un nom)
2. Créer un identifiant client
Toujours indiquer « signin-google » en url
de redirection
50
51
3. Activer « Google+ Api »
4. Si ce n’est fait installer le package …
PM> install-package Microsoft.Owin.Security.Google
5. Rendez-vous dans « Startup.Auth.cs » dossier « App_Start » et dé-commenter et entrer le
« Client id » et le « Client secret »
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "903536450598ps3vsah291ef6iekkqtqflc2d0g746hj.apps.googleusercontent.com",
ClientSecret = "123456789abcdefg"
});
51
52
Twitter
Développeurs Twitter
Code supplémentaire
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Demandez une redirection vers le fournisseur de connexions externe
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback",
"Account", new { ReturnUrl = returnUrl }));
}
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
var result = await SignInManager.ExternalSignInAsync(loginInfo,
isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl,
RememberMe = false });
case SignInStatus.Failure:
default:
// Si l'utilisateur n'a pas de compte
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new
ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult>
ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Manage");
}
if (ModelState.IsValid)
{
// Obtenez des informations sur l’utilisateur auprès du fournisseur
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
52
53
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser { UserName = model.Email, Email =
model.Email };
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false,
rememberBrowser: false);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
internal class ChallengeResult : HttpUnauthorizedResult
{
private const string XsrfKey = "XsrfId";
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri =
RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties,
LoginProvider);
}
}
53
54
ViewModels
public class ExternalLoginListViewModel
{
public string ReturnUrl { get; set; }
}
public class ExternalLoginConfirmationViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
}
Vues
_ExternalLoginsListPartial
@model MvcFromScratch.Models.ExternalLoginListViewModel
@using Microsoft.Owin.Security
<h4>Utilisez un autre service pour vous connecter.</h4>
<hr />
@{
var loginProviders =
Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes();
if (loginProviders.Count() == 0) {
<div>
<p>
Il n'existe aucun service d'authentification externe configuré.
Consultez <a href="http://go.microsoft.com/fwlink/?LinkId=403804">cet article</a>
pour des détails sur la configuration de cette application ASP.NET en
vue de la prise en charge de la connexion via des services externes.
</p>
</div>
}
else {
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl =
Model.ReturnUrl })) {
@Html.AntiForgeryToken()
<div id="socialLoginList">
<p>
@foreach (AuthenticationDescription p in loginProviders) {
<button type="submit" class="btn btn-default"
id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType"
title="Connexion avec votre compte @p.Caption">@p.AuthenticationType</button>
}
</p>
</div>
}
}
}
54
55
ExternalLoginConfirmation
@model MvcFromScratch.Models.ExternalLoginConfirmationViewModel
@{
ViewBag.Title = "S’inscrire";
}
<h2>@ViewBag.Title.</h2>
<h3>Associer votre compte @ViewBag.LoginProvider.</h3>
@using (Html.BeginForm("ExternalLoginConfirmation", "Account", FormMethod.Post))
{
@Html.AntiForgeryToken()
<h4>Formulaire d'association</h4>
<hr />
@Html.ValidationSummary(true)
<p>
Vous avez été authentifié avec succès avec
<strong>@ViewBag.LoginProvider</strong>.
Veuillez entrer ci-dessous un nom d'utilisateur pour ce site et cliquer sur le
bouton S'inscrire pour valider la
connexion.
</p>
<div>
@Html.LabelFor(m => m.Email)
@Html.TextBoxFor(m => m.Email)
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
</div>
<div>
<input type="submit" value="Inscription" />
</div>
}
ExternalLoginFailure
@{
ViewBag.Title = "Échec de la connexion";
}
<hgroup>
<h2>@ViewBag.Title.</h2>
<h3 class="text-danger">Échec de la connexion auprès du service.</h3>
</hgroup>
Exemples : Tutorial, et ici
PM> Install-Package Microsoft.AspNet.Identity.Samples –Pre
c. Autorisations (attribut « Authorize »)
On peut l’appliquer à une action d’un contrôleur
public class PeopleController : Controller
{
[Authorize]
public ActionResult Index()
{
return View();
}
}
55
56
… ou à tout le contrôleur.
[Authorize]
public class PeopleController : Controller
{
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
}
Permet un accès anonyme à
une action
… Autoriser l’accès à un rôle, utilisateur, etc.
[Authorize(Roles="admin, member")]
ReturnUrl
Si on essaie d’accéder à une vue nécessitant d’être authentifié on est redirigé vers la page de
login.
Une fois authentifié on est redirigé …
ValidateAntiForgeryToken
Pour contrer les attaques malveillantes
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
_peopleRepository.Add(person);
return RedirectToAction("Index");
}
return View(person);
}
…Dans la vue
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
@Html.AntiForgeryToken()
56
57
AntiXSS
[HttpPost]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
person.LastName = Sanitizer.GetSafeHtmlFragment(person.LastName);
_peopleRepository.Add(person);
return RedirectToAction("Index");
}
return View(person);
}
d. SSL
// GET: /Account/Login
[RequireHttps]
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
57
58
e. Projet Visual Studio 2012
InitializeSimpleMembership et WebSecurity
Spécification de la chaine de connexion utilisée(DefaultConnection)
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId",
"UserName", autoCreateTables: true);
La base de données correspondant à la chaine de connexion est créée lors de l’enregistrement du
premier membre si elle n’existe pas.
Il est possible d’ajouter ses champs personnalisés et modifier la table « UserProfile » de la base
avec la commande (console du gestionnaire de packages)
PM> Update-Database -Verbose
+ enable-migrations
On peut également supprimer « InitializeSimpleMembership » et utiliser sa propre connexion
.Ajouter « WebSecurity.InitializeDatabaseConnection » dans global.asax et remplacer
« UsersContext » par son propre DbContext.
58
59
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
// persist cookie = true
bool isLogged = WebSecurity.Login("username", "password",true);
WebSecurity.CreateUserAndAccount("username", "password");



Le password est haché automatiquement
Si l’utilisateur coche « mémoriser le mot de passe » lorsqu’il se entre ses
informations de connexion dans la page de login, un cookie est créé.
OAuth et OpenID avec projet Visual Studio 2012
SimpleRoleProvider et SimpleMembershipProvider
Web.config
<system.web>
<!-- etc. -->
<roleManager enabled="true" defaultProvider="simple">
<providers>
<clear/>
<add name="simple" type="WebMatrix.WebData.SimpleRoleProvider,
WebMatrix.WebData"/>
</providers>
</roleManager>
<membership defaultProvider="simple">
<providers>
<clear/>
<add name="simple" type="WebMatrix.WebData.SimpleMembershipProvider,
WebMatrix.WebData"/>
</providers>
</membership>
</system.web>
var roles = (SimpleRoleProvider)Roles.Provider;
var membership = (SimpleMembershipProvider)Membership.Provider;
if (!roles.RoleExists("Admin"))
roles.CreateRole("Admin");
if (membership.GetUser("Marie", false) == null)
membership.CreateUserAndAccount("Marie", "passwword");
if (!roles.GetRolesForUser("Marie").Contains("Admin"))
roles.AddUsersToRoles(new[] { "Marie" }, new[] { "Admin" });
59
60
Register
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser() { UserName = model.Email, Email =
model.Email };
IdentityResult result = await UserManager.CreateAsync(user,
model.Password);
if (result.Succeeded)
{
await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
AddErrors(result);
}
}
return View(model);
}
Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Nom d'utilisateur ou mot de passe
non valide.");
}
}
return View(model);
}
60
61
7. Localisation
Dates et Monnaies
Dépendent de la culture courante
Exemple : dans la vue on affiche une monnaie et la date, leur formatage change selon la culture.
@{
var money = 9.99m;
var today = DateTime.Now.ToShortDateString();
}
<div>@money.ToString("c")</div>
<div>@today</div>
Web.config
<system.web>
<!---->
<globalization culture="auto" uiCulture="auto"/>
</system.web>
Changer les préférences linguistiques d’IE
… En montant English
…En montant Français
61
62
Resources
Ajouter un fichier de ressources
Ajouter un fichier de ressources pour la langue par défaut (exemple : Resources.resx) et un
fichier de ressources pour chaque langue supportée par le site (exemple : Resources.en.resx)
Indiquer l’outil personnalisé pour uniquement Resources par défaut.
Exemple en changeant la langue dans les préférences linguistiques d’IE
(LocalizationDemo est le nom du projet)
<div>@LocalizationDemo.Views.Home.Resources.Greeting</div>
On peut également récupérer les ressources dans les contrôleurs
var greeting = Resources.Greeting;
Et utiliser les ressources avec les data annotations
[Display(ResourceType=typeof(Resources),Name="Greeting")]
public string FullName { get; set; }
62
63
8. Less
Installer l’ extension pour Visual Studio « Web Essentials » (aperçu CSS, génération de la feuille
de Styles, minimisation, etc.)
9. Dependency Injection (DI)
Avec Ninject
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
RegisterServices();
}
private void RegisterServices()
{
var kernel = new StandardKernel();
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
kernel.Bind<IPeopleRepository>().To<FakePeopleRepository>();
}
}
public class NinjectDependencyResolver :IDependencyResolver
{
private readonly IKernel _kernel;
public NinjectDependencyResolver(IKernel kernel)
{
this._kernel = kernel;
}
public object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType, new IParameter[0]);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType, new IParameter[0]);
}
}
63
64
10.
Upgrade
Visual Studio 2013 ne prend en charge MVC qu’à partir de la version 4.
 Upgrade de MVC2 vers MVC3 avec ASP.NET MVC 3 Application Upgrader
 Upgrade de MVC3 vers MVC4
Ouvrir la solution (dont le projet Mvc ne peut être chargé) dans Visual Studio 2013
… chercher « Auto upgrade mvc 3 to mvc4 » avec le gestionnaire de package NuGet


Il se peut qu’il faille ajouter une référence à
« Microsoft.AspNet.Web.Optimization » (package NuGet) ensuite.
Cibler le Framework 4.0 peut aussi éviter des problèmes de versions de
fichiers.
64
65
II.
Web Api
1. Web Api config
La route par défaut pour un contrôleur Web Api
2. Contrôleur Web Api
a.






HTTP Status codes
GET : 200 (ok), 404 (not found), 400, 500
POST : 201 (created), 400 (bad request), 500 (internal server error)
PUT : 200, 404, 400, 500
PATCH : 200,404,400,500
DELETE : 204 (no content), 404, 400, 500
Général : 401 (unauthorized), 403 (forbidden), 405 (method not allowed)
HttpStatusCode
b. Ajout d’un contrôleur Web Api
Menu contextuel sur le dossier « Controllers » du projet
65
66
c. Contrôleur Web API avec IhttpActionResult
Permet de simplifier l’écriture (et réduire le code) de réponses HTTP en utilisant des méthodes :
Classe implémentant de IhttpResult
OkResult
NotFoundResult
ExceptionResult
UnauthorizedResult
BadRequestResult
ConflictResult
RedirectResult
InvalidModelStateResult
Méthode
Ok
NotFound
Unauthorized
BadRequest
Conflict
Redirect
public class PeopleController : ApiController
{
private IPeopleRepository peopleRepository;
public PeopleController()
: this(new FakePeopleRepository()) { }
public PeopleController(IPeopleRepository peopleRepository)
{
this.peopleRepository = peopleRepository;
}
Injection de
dépendances
[HttpGet]
public IHttpActionResult Get()
{
try
{
var people = peopleRepository.GetAll();
return Ok(people);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[HttpGet]
public IHttpActionResult Get(int id)
{
try
{
var person = peopleRepository.GetOne(id);
if (person != null)
{
return Ok(person);
}
else
{
return NotFound();
}
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[HttpPost]
public IHttpActionResult Post([FromBody]Person person)
{
66
67
try
{
var result = peopleRepository.Add(person);
return Created(Request.RequestUri + "/" + result.Id.ToString(), result);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[HttpPut]
public IHttpActionResult Put(int id, [FromBody]Person person)
{
try
{
if (person == null)
return BadRequest();
var personToUpdate = peopleRepository.GetOne(id);
if (personToUpdate == null)
{
return NotFound();
}
var result = peopleRepository.Update(person);
return Ok(result);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[HttpDelete]
public IHttpActionResult Delete(int id)
{
try
{
var personToDelete = peopleRepository.GetOne(id);
if (personToDelete == null)
{
return NotFound();
}
peopleRepository.Delete(id);
return StatusCode(HttpStatusCode.NoContent);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
}
67
68
d. Contrôleur Web API avec HttpResponseMessage
public class PeopleController : ApiController
{
private IPeopleRepository peopleRepository;
public PeopleController()
: this(new FakePeopleRepository()) { }
public PeopleController(IPeopleRepository peopleRepository)
{
this.peopleRepository = peopleRepository;
}
On pourrait également retourner
[HttpGet]
une liste générique en résultat par
public HttpResponseMessage Get()
{
exemple
try
{
var people = peopleRepository.GetAll();
return Request.CreateResponse(HttpStatusCode.OK,people);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,ex);
}
}
On pourrait également retourner
[HttpGet]
une instance de classe (exemple
public HttpResponseMessage GetOne(int id)
« Person »)
{
try
{
var person = peopleRepository.GetOne(id);
if (person != null)
{
return Request.CreateResponse(HttpStatusCode.OK,person);
}
else
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
[HttpPost]
public HttpResponseMessage Post([FromBody]Person person)
{
try
{
var result = peopleRepository.Add(person);
return Request.CreateResponse(HttpStatusCode.Created, result);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
[HttpPut]
public HttpResponseMessage Put(int id,[FromBody]Person person)
{
try
68
69
{
if (person == null)
return Request.CreateResponse(HttpStatusCode.BadRequest);
var personToUpdate = peopleRepository.GetOne(id);
if (personToUpdate == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
var result = peopleRepository.Update(person);
return Request.CreateResponse(HttpStatusCode.OK, result);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
[HttpDelete]
public HttpResponseMessage Delete(int id)
{
try
{
var personToDelete = peopleRepository.GetOne(id);
if (personToDelete == null)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
peopleRepository.Delete(id);
return Request.CreateResponse(HttpStatusCode.NoContent);
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
}
}
}
Retour au format Json par défaut. Si on désire un retour au format Xml…DataMember(membres
à intégrer dans la réponse) et DataContract (System.Runtime.Serialization)
[Serializable]
[DataContract]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Twitter { get; set; }
}
…Et dans le header accept :text/xml
69
70
Test du Service avec Fiddler
GET
POST
« get one »
Ajout du type de contenu
Retour au format JSON de l’élément créé
PUT
Ajout du paramètre « id » à
l’url et passage de l’élément
modifié
DELETE
70
71
3. Attribut “Route”
Permet de personnaliser ses routes et d’éviter les conflits entre routes.
[HttpGet]
[Route("api/people/{id}/twitter")]
public HttpResponseMessage GetTwitter(int id)
{
// etc.
}
Route
[Route("api/people/{id}/twitter")]
[Route("api/people/{id:int:min(1)}/twitter")]
[Route("api/people/{id:int}")]
[Route("api/people/{name:alpha}")]
[Route("api/people/{id?}")]
[RoutePrefix("api/people")]
[RouteArea("admin")]
Description
Route personnalisée
Contraintes
Multiples routes
Appliquer à tout le contrôleur
Prefix pour toutes les routes du contrôleur
Mvc Areas
4. Injection de dépendances
Avec Ninject
Ajout du package NuGet « NInject »
… ainsi que (pour Web Api)
Global.asax
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
RegisterServices();
}
private void RegisterServices()
{
var kernel = new StandardKernel();
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
kernel.Bind<IPeopleRepository>().To<PeopleRepository>();
}
}
71
72
Le contrôleur n’a plus besoin de constructeur par défaut
5. Projet Client
a. dataService JavaScript
(Pour projet SPA, Angular, Durandal,…). Avec Knockout par exemple :
var dataService = function () {
'use strict'
var urlBase = 'http://localhost:12107/api/People',
getPeople = function () {
return $.getJSON(urlBase);
},
searchPeople = function (search) {
return $.getJSON(urlBase + '/find/' + search);
},
getPerson = function (id) {
return $.getJSON(urlBase + '/' + id);
},
addPerson = function (person) {
return $.ajax({
url: urlBase,
type: 'POST',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(person)
});
},
updatePerson = function (person) {
return $.ajax({
url: urlBase,
type: 'PUT',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: ko.toJSON(person)
});
},
deletePerson = function (id) {
return $.ajax({
url: urlBase + '/' + id,
type: 'DELETE'
});
};
return {
getPeople: getPeople,
searchPeople: searchPeople,
getPerson: getPerson,
updatePerson: updatePerson,
addPerson: addPerson,
deletePerson: deletePerson
};
}();
72
73
b. HttpClient
(projets WinRT, Wpf,etc.)
public class PeopleService
{
string urlBase ="http://localhost:12107/api/People";
public async Task<Person[]> GetAll()
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(urlBase);
string resultContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Person[]>(resultContent);
}
}
public async Task<Person> GetOne(int personID)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage result = await client.GetAsync(string.Format("{0}/{1}", urlBase,
personID));
string resultContent = await result.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Person>(resultContent);
}
}
public async void Add(Person person)
{
using (HttpClient client = new HttpClient())
{
string json = JsonConvert.SerializeObject(person);
HttpResponseMessage response = await client.PostAsync(urlBase,
new StringContent(json, Encoding.UTF8, "application/json"));
string resultContent = await response.Content.ReadAsStringAsync();
person = JsonConvert.DeserializeObject<Person>(resultContent);
}
}
public async void Update(Person person)
{
using (HttpClient client = new HttpClient())
{
string json = JsonConvert.SerializeObject(person);
HttpResponseMessage response = await client.PutAsync(urlBase,
new StringContent(json, Encoding.UTF8, "application/json"));
string resultContent = await response.Content.ReadAsStringAsync();
person = JsonConvert.DeserializeObject<Person>(resultContent);
}
}
public async void Delete(int personID)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.DeleteAsync(string.Format("{0}/{1}", urlBase,
personID));
response.EnsureSuccessStatusCode();
}
}
}
73
74
Il existe également des librairies facilitant encore l’utilisation de HtppClient comme EasyHttp
public class EasyHttpPeopleService
{
string urlBase = "http://localhost:12107/api/People";
public Person[] GetAll()
{
HttpClient client = new HttpClient();
HttpResponse response = client.Get(urlBase);
Person[] result = response.StaticBody<Person[]>();
return result;
}
public Person GetOne(int personID)
{
HttpClient client = new HttpClient();
HttpResponse response = client.Get(string.Format("{0}/{1}", urlBase, personID));
Person result = response.StaticBody<Person>();
return result;
}
public void Add(Person person)
{
HttpClient client = new HttpClient();
client.Post(urlBase, person, "application/json");
}
public void Update(Person person)
{
HttpClient client = new HttpClient();
client.Put(urlBase, person, "application/json");
}
public void Delete(int personID)
{
HttpClient client = new HttpClient();
client.Delete(string.Format("{0}/{1}", urlBase, personID));
}
}
74
75
Les ports ne correspondant pas,
on a une erreur
6. Cors
Installer avec les packages NuGet
PM> Install-Package Microsoft.AspNet.WebApi.Cors
… Dans WebApiConfig
… Sur le contrôleur Web API
Ajouter l’attribut « EnableCorsAttribute ». Le premier
argument correspondant à l’URL du client (on
pourrait également mettre « * »), le second aux
headers puis les méthodes.
[EnableCorsAttribute("http://localhost:63679", "*","*")]
public class PeopleController : ApiController
{
public IHttpActionResult Get()
{
var file = HostingEnvironment.MapPath(@"~/App_Data/people.json");
var json = File.ReadAllText(file);
var people = JsonConvert.DeserializeObject<List<Person>>(json);
return Ok(people);
}
}
75
76
7. Formatters pour le « Mapping »
Dans le Fichier Json les noms des propriétés sont en camelCase, alors que les propriétés des modèles
eux sont en PascalCase.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Configuration et services de l'API Web
// Configurer l'API Web pour utiliser uniquement l'authentification de jeton du porteur.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Itinéraires de l'API Web
config.MapHttpAttributeRoutes();
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
config.EnableCors();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
8. OData
Installer avec les packages NuGet
PM> Install-Package Microsoft.AspNet.WebApi.OData
Puis pour les méthodes des contrôleurs
Ajouter l’attribut
« EnableQuery »
[EnableQuery()]
[ResponseType(typeof(Person))]
public IHttpActionResult Get()
{
try
{
var result = peopleRepository.GetAll();
return Ok(result.AsQueryable());
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
Si on n’utilise pas
« IHttpActionResult » retourner
« IQueryable<Person> » dans
l’exemple
Résultat retourné AsQueryable.
Site, Documentation
76
77
9. Authentification Web Api
Server
« PeopleController » réclame d’être authentifié .
[Authorize]
public class PeopleController : ApiController
{
private static List<Person> people = new List<Person>() {
new Person(1,"John","Papa","@john_papa"),
new Person(2, "Ryan", "Niemeyer", "@rpniemeyer"),
new Person(3, "Steve", "Sanderson", "@stevensanderson"),
new Person(4, "Ward", "Bell", "@wardbell"),
new Person(5, "Scott", "Allen","@OdeToCode"),
new Person(6,"Yacine","Khammal","@YacineKhammal")
};
[HttpGet]
public IEnumerable<Person> Get()
{
return people;
}
}
Startup.Auth et AccountController différent de la version
« Mvc ».L’ApplicationUserManager(IdentityConfig.cs) change peu.
AccountController
Startup.Auth.cs
77
78
Client : On n’est autorisé à afficher la liste des personnes qu’une fois authentifié.
<body>
<header>
<!--connexion -->
<div id="login">
<form id="loginForm">
<input type="text" name="userName" placeholder="Nom d'utilisateur"
value="[email protected]" />
<input type="password" name="password" placeholder="Mot de passe"
value="Abc123_" />
<input type="submit" id="loginInput" value="Valider" />
<input type="button" id="showRegisterForm" value="Créer un compte?" />
</form>
</div>
<!-- enregistrement -->
<div id="register">
<form id="registerForm">
<input type="text" id="userName" name="userName"
value="[email protected]" />
<input type="text" name="email" value="[email protected]" />
<input type="password" id="password" name="password" value="Abc123_"
/>
<input type="password" name="confirmPassword" value="Abc123_" />
<input type="submit" id="registerInput" value="Valider" />
<input type="button" id="showLoginForm" value="Connexion" />
</form>
</div>
<!--connecté -->
<div id="loggedIn"></div>
</header>
<section>
<button id="getPeopleListButton">Lister les personnes</button>
<div id="container"></div>
</section>
<script src="Scripts/jquery-2.1.3.js"></script>
<script>
$(function () {
// Show / hide
$("#register").hide();
$("#getPeopleListButton").hide();
$("#loggedIn").hide();
$("#showLoginForm").on("click", function () {
$("#register").hide();
});
$("#showRegisterForm").on("click", function () {
$("#register").show();
});
var accessToken = "";
var loggedIn = false;
var register = function () {
var url = "api/account/register";
var data = $("#registerForm").serialize(); // récupère les infos
saisies dans le formulaire
$.post(url, data)
.done(function () {
alert("Bienvenue !");
login(); // connexion
78
79
})
.fail(function () {
alert("Echec lors de la création du compte.");
});
return false;
}
// login
var login = function () {
var url = "Token";
var data = $("#loginForm").serialize();
//userName=abc%40laposte.net&email=abc%40laposte.net&password=Abc123_&confirmPassword=
Abc123_
data = data + "&grant_type=password";
$.post(url, data)
.success(function (data) {
accessToken = data.access_token;
loggedIn = true;
$("#login").hide();
$("#register").hide();
var welcomeMessage = "<h2>Bonjour " + data.userName +
"!</h2>";
$("#loggedIn").html(welcomeMessage);
$("#loggedIn").show();
$("#getPeopleListButton").show();
})
.fail(function () {
alert("Impossible de se connecter. vérifiez vos
identifiants.");
});
return false;
}
var getAll = function () {
var url = "api/people";
if (loggedIn) {
$.ajax(url, {
type: "GET",
headers: { "Authorization": "Bearer " + accessToken, "ContentType": "application/x-www-form-urlencoded" }
}).success(function (data) {
var tableHTML =
"<table><tr><th>Nom</th><th>Twitter</th></tr>";
data.forEach(function (person) {
tableHTML += "<tr><td>" + person.Name + "</td><td>" +
person.Twitter + "</td></tr>";
});
tableHTML += "</table>";
$("#container").html(tableHTML);
})
.fail(function (error) {
alert(JSON.stringify(error, null, 4));
});
}
79
80
else {
alert("Veuillez vous authentifier svp.");
}
}
$("#loginInput").on("click", login);
$("#registerInput").on("click", register);
$("#getPeopleListButton").on("click", getAll);
});
</script>
</body>
Login…on récupère le token …
Récupération de la liste de personnes …
80
81
Activer SSL
Depuis les propriétés du projet
… Puis dans l’onglet Web changer l’URL
URL SSL
81
82
10. MongoDB
Connexion à une base de données NoSQL MongoDB avec un service WEB MVC.
Drivers :
-
MongoDB C# Driver
Simple-mongodb
FluentMongo
a. Installation de MongoDB C# driver
Documentation
Deux références sont ajoutées :
people
peopledb
…
Serveur
…
MongoClient
MongoServer
MongoDatabase
MongoCollection
82
83
b. Service WEB
Création d’une classe permettant de se connecter à la base de données.
using
using
using
using
MongoDB.Bson;
MongoDB.Bson.Serialization.Attributes;
MongoDB.Driver;
MongoDBWithMVC.Properties;
namespace MongoDBWithMVC.Models
{
public class MongoDBContext
{
public MongoDatabase database;
public MongoDBContext()
{
//mongodb://localhost:27017/
var client = new MongoClient(Settings.Default.MongoDBConnectionString);
var server = client.GetServer();
database = server.GetDatabase(Settings.Default.DatabaseName); //peopledb
}
public MongoCollection<Person> People
{
get
{
return database.GetCollection<Person>("people");
}
}
}
}
Accès à la collection « people » de la
base
Le modèle utilisé pour la définition des documents
public class Person
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElementAttribute("name")]
public string Name { get; set; }
[BsonElementAttribute("twitter")]
public string Twitter { get; set; }
Pour l’identifiant (_id) attribué
à l’insertion d’un document
Pour la sérialisation lorsque le
nom des éléments est différent
}
83
84
Service WEB API
public class PeopleController : ApiController
{
MongoDBContext context;
public PeopleController()
{
context = new MongoDBContext();
}
On utilise la classe de contexte créée
pour se connecter et accéder à la
collection « people »
public IHttpActionResult Get()
{
try
Méthode « FindAll » pour obtenir
{
var result = context.People.FindAll();
tous les documents
return Ok(result);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
public IHttpActionResult Get(string id)
Obtention du document par l’id
{
try
{
var person = context.People.FindOneById(new ObjectId(id));
if (person != null)
{
return Ok(person);
}
else
{
return NotFound();
}
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[HttpPost]
public IHttpActionResult Post([FromBody]Person person)
{
Ajout d’un document à la collection
try
{
« people »
var result = context.People.Insert(person);
return Created(Request.RequestUri + "/" + person.Id.ToString(),
person);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[HttpPut]
public IHttpActionResult Put(string id, [FromBody]Person person)
{
try
{
84
85
var personToUpdate = context.People.FindOneById(new ObjectId(id));
if (personToUpdate == null)
{
Modification puis sauvegarde du
return NotFound();
document.
}
personToUpdate.Name = person.Name;
personToUpdate.Twitter = person.Twitter; On pourrait aussi utiliser la méthode
update+ updatebuilder
context.People.Save(personToUpdate);
return Ok(personToUpdate);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
[HttpDelete]
public IHttpActionResult Delete(string id)
{
try
{
var personToDelete = context.People.FindOneById(new ObjectId(id));
if (personToDelete == null)
Suppression du document par
{
return NotFound();
l’id
}
context.People.Remove(Query.EQ("_id", new ObjectId(id)));
return StatusCode(HttpStatusCode.NoContent);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
}
85
86
III.
Bootstrap
1. Installation et thèmes
On peut « customiser » en sélectionnant seulement les éléments à inclure
Ajout au projet : Télécharger depuis le site ou avec packages NuGet (ou sur Github pour avoir les
fichiers LESS)
Thèmes pour bootstrap
Thème à télécharger sur boostwatch. On renomme le thème par exemple « flatly-bootstrap.css »
que l’on ajoute au projet et on remplace le thème par défaut par celui-ci
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, inital-scale=1" />
<title>Bootstrap demo</title>
Thème « par défaut » et thème
<link href="css/bootstrap.css" rel="stylesheet" />
téléchargé sur boostwatch
<link href="css/bootstrap-theme.css" rel="stylesheet" />
<!--<link href="css/themes/flatly-bootstrap.css" rel="stylesheet" />-->
<link href="css/default.css" rel="stylesheet" />
</head>
<body>
<script src="js/jquery-2.1.3.js"></script>
<script src="js/bootstrap.js"></script>
</body>
</html>
86
87
2. Grid system
Grid system sur 12 colonnes : exemple de col-md-1 à col-md-12 occupant toute la largeur.
Taille
>=1200px
de 992 à 1200px
De 768 à 991px
<768px
Classe
col-lg-xx
col-md-xx
col-sm-xx
col-xs-xx
<div class="row">
<div id="main" class="col-md-8 col-sm-10 col-xs-6"><p>Lorem ipsum ... </p></div>
<div id="sidebar" class="col-md-4 col-sm-2 col-xs-6">Lorem ... </div>
</div>
La taille des colonnes s’adapte selon les résolutions
col-md-8 et col-md-4 pour la
« sidebar » sur taille « middle »
Sur mobile 2 colonnes égales (col-xs6)
a. Cacher une colonne selon une résolution
Cachera la colonne sur mobile
<div id="sidebar" class="col-md-4 hidden-xs">Lorem ipsum ... </div>
87
88
b. Décalage de colonnes avec « offset »
De col-xx-offset-1 à col-xx-offset-12
Ex
<div class="row">
<div class="col-md-8 col-md-offset-4">Lorem ipsum …</div>
</div>
Colonne « col-md-8 » avec
décalage de 4 colonnes « col-mdoffset-4 »
c. Image flottante
Thumbnails
<div class="row">
<div class="col-md-12">
<p><img src="images/foodies.jpg" width="300" class="pull-left
thumbnail"/>Lorem …</p>
</div>
</div>
« pull-left » place l’image à gauche (« pull-right »
dispo)
« thumbnail » ajoute une bordure et effet à la
photo
3. Bases
a. Typographie
Documentation
Classes commençant par « text-xx »
<p class="text-muted">Lorem ipsum dolor sit amet</p>
88
89
b. Boutons, groupes de boutons et dropdowns
Buttons
<button class="btn btn-danger">En savoir plus ...</button>
Dropdowns
On pourrait utiliser des
boutons radio avec label
<div class="form-group">
<div class="btn-group">
<button class="btn btn-default active">Un</button>
<button class="btn btn-default">Deux</button>
<div class="btn-group">
<button id="option" class="btn btn-default">Choisir
...</button>
<button class="btn btn-default" data-toggle="dropdown"><span
class="caret"></span></button>
<ul id="options" class="dropdown-menu">
<li><a href="#">Option un</a></li>
<li><a href="#">Option deux</a></li>
<li class="divider"></li>
<li class="disabled"><a href="#">Option trois</a></li>
</ul>
</div>
</div>
</div>
Script pour changer le texte du bouton
<script>
$(document).ready(function () {
$("#options li a").on("click", function () {
var option = $(this).text();
$("#option").text(option);
})
});
</script>
89
90
c. Icones
Glyphicons
<button class="btn btn-default navbar-toggle" data-toggle="collapse" datatarget=".navbar-collapse"><span class="glyphicon glyphicon-chevrondown"></span></button>
d. Formulaires, listes, et tables
Forms, nav, tables
« input-group »
Documentation
<div class="form-group">
<div class="col-md-10">
<div class="input-group">
<span class="input-group-addon">@</span>
<input type="email" class="form-control"
placeholder="Email"/>
</div>
</div>
</div>
e. Navbar desktop et mobile
Documentation
<header class="container">
<div id="menu" class="nav navbar-default">
<div class="navbar-header">
<button class="btn btn-default navbar-toggle" datatoggle="collapse" data-target=".navbar-collapse"><span class="glyphicon glyphiconchevron-down"></span></button>
<h4><a href="#">Boostrap demo</a></h4>
</div>
<nav class="navbar navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li class="nav active"><a href="#">Accueil</a></li>
<li class="nav"><a href="#">A propos</a></li>
<li class="nav"><a href="#">Contact</a></li>
</ul>
</nav>
</div>
</header>
Version desktop
90
91
Avec thème « flatly »
Version mobile
Avec thème « flatly »
f. Header et breadcrumb
Header, breadcrumb
« breadcrumb »
« header » mis en
forme
<div id="body" class="container">
<ol class="breadcrumb">
<li><a href="index.html">Accueil</a></li>
<li class="active">A propos</li>
</ol>
<div class="page-header">
<h1>Bootstrap demo</h1>
<p>Une démonstration du Framework</p>
</div>
g. Pagination
Documentation
91
92
<ul class="pagination">
<li class="disabled"><a href="#">&laquo;</a></li>
<li class="active"><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">&raquo;</a></li>
</ul>
h. Well
On pourrait ajouter la classe « lead » pour mettre plus en avant
<p class="well">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec lacinia metus quis
vulputate semper.
</p>
i. Panels
Documentation
Plugins
a. Collapse et accordéon
Documentation
92
93
b. Boite de dialogue
Documentation
<a href="#mydialogbox" class="btn btn-danger" data-toggle="modal">Ouvrir la boite de
dialogue ...</a>
<div id="mydialogbox" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<a href="#" class="close" data-dissmiss="modal">&times;</a>
<h3>Une boite de dialogue</h3>
</div>
<div class="modal-body">
Le contenu de la boite!
</div>
<div class="modal-footer">
le pied
</div>
</div>
</div>
</div>
Depuis jQuery
$("#mydialogbox").modal("show");
c. Alert
Documentation
On place une alerte au-dessus d’un formulaire
« collapse » par défaut que l’on affiche si le
formulaire comporte des erreurs par exemple
93
94
<div class="alert alert-warning collapse" id="formmessage">
<a href="#" class="close" data-dissmiss="alert">&times;</a>
<p>Vous devez remplir tous les champs!</p>
</div>
<form>
<!-- ... -->
$("form").on("submit", function (e) {
if($("#email").text()=="") {
e.preventDefault();
$("#formmessage").show();
}
})
d. Tab
Documentation
<ul class="nav nav-tabs nav-justified">
<li class="active"><a href="#formtab" data-toggle="tab">Contact</a></li>
<li><a href="#maptab" data-toggle="tab">Map</a></li>
</ul>
<div class="tab-content">
<div class="well tab-pane active" id="formtab">
<form>
<!-- ... -->
</form>
</div>
<div class="well tab-pane" id="maptab">
<!-- ... -->
</div>
</div>
e. Tooltip
Documentation
94
95
Exemple toolptip sur les images (affiche la propriété title)
$("img").tooltip();
On pourrait définir du « html » dans l’attribut « title »
<img id="foodies" src="images/foodies.jpg" width="300" class="pull-left thumbnail"
data-html="true" title="<h3>Un petit café?</h3><img src='images/foodies.jpg'
class='img-responsive'/>" />
« data-html »
f. Caroussel
Documentation
95
96
Contrôles
Caption
Indicateurs
Permet de lancer auto.
(data-interval : intervalle
entre chaque slide en ms)
<div class="container">
<div class="row visible-lg visible-md">
<div class="col-lg-8 col-lg-offset-2 col-md-8 col-md-offset-2">
<div id="carousel" class="carousel slide" data-ride="carousel" data-interval="2000">
<!-- indicateurs -->
<ol class="carousel-indicators">
<li data-target="#carousel" data-slide-to="0" class="active"></li>
<li data-target="#carousel" data-slide-to="1"></li>
<li data-target="#carousel" data-slide-to="2"></li>
</ol>
<!-- slides -->
<div class="carousel-inner">
<div class="item active">
<img src="images/image_1.jpg" class="img-responsive" alt="" />
<div class="carousel-caption"><h4>Un petit café?</h4></div>
</div>
<div class="item">
<img src="images/image_2.jpg" class="img-responsive" alt="" />
<div class="carousel-caption"><h4>Un poussin</h4></div>
</div>
<div class="item">
<img src="images/image_3.jpg" class="img-responsive" alt="" />
<div class="carousel-caption"><h4>La grande roue</h4></div>
</div>
</div>
<!-- Controls -->
<a class="left carousel-control" href="#carousel" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
</a>
<a class="right carousel-control" href="#carousel" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
</a>
</div>
</div>
</div>
</div>
jQuery
96
97
$("#carousel").carousel();
97