Asp.net core mvc

De Banane Atomic
Aller à la navigationAller à la recherche

Arborescence

  • Program.cs - application console qui appelle Startup
  • Startup.cs - Configure et ConfigureServices

Startup.cs

Startup.cs
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // injection de dépendance: service MVC
    services.AddMvc();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        // afficher les exceptions plutôt qu'une page blanche
        app.UseDeveloperExceptionPage();
    }
    else
    {
        // en production, redirection vers /Home/Error en cas d'erreur
        app.UseExceptionHandler("/Home/Error");
        // HTTP Strict Transport Security, force l'utilisation de https
        app.UseHsts();
    }

    // au besoin, ajoute à la requête un des fichiers par défaut s'il existe (index.html)
    app.UseDefaultFiles();

    // permet de faire des requêtes sur les fichiers statiques (*.html) du dossier wwwroot
    app.UseStaticFiles();
    // permet de mapper un dossier externe comme s'il était dans wwwroot
    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = new PhysicalFileProvider("/path/MyFolder"),
        // allow you access the directory as /myfolder
        RequestPath = new PathString("/myfolder")
    });

    // active MVC et definit les règles de routage
    app.UseMvc(routes =>
    {
        // route par défaut controller/action/id (avec ud optionnel)
        // controller par défaut:Home, action par défaut:Index
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Controller

Controllers/ListController.cs
public class ListController : Controller
{
    // /list/index
    public IActionResult Index()
    {
        // rend des données accessible dans la vue
        ViewBag.Items = new List<string> { "Item1", "Item2" };
        // retourne la vue Views/List/Index.cshtml
        return View();
    }
}

Attributs de routing

Controllers/ListController.cs
public class ListController : Controller
{
    // routing par défaut: /list/index
    // redéfinit à /list/items
    [HttpGet("items")]
    public IActionResult Index()
    { }
}

Views

Views/List/Index.cshtml
@{
    ViewData["Title"] = "List";
}

<h2>Items List</h2>

<ul>
@foreach (var item in ViewBag.Items)
{
    <li>item</li>
}
</ul>

_ViewStart.cshtml

Permet de définir du code commun à toutes les vues. Disponible depuis ASP.NET MVC 3.

Views/_ViewStart.cshtml
@{
    Layout = "_Layout";  @* Views/Shared/_Layout.cshtml *@
}

_ViewImports.cshtml

Définit les namespaces pour toutes les vues.
Dans MVC5 cela était fait dans le fichier Views/web.config.
Pour l'ajouter: clique-droit sur le projet → Add → New Item → Installed → Visual C# → ASP.NET Core → Web → Razor View Imports

Views/_ViewImports.cshtml
@* ajoute des namespaces *@
@using MyApp
@using MyApp.Models
@* ajoute tous les TagHelper (*) du namespace Microsoft.AspNetCore.Mvc.TagHelpers *@
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Layout

Views/Shared/_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - MyApp</title>

    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <!-- menu -->
    </nav>

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 - MyApp</p>
        </footer>
    </div>

    <script src="~/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>

TagHelpers

Cshtml.svg
<a asp-controller="MyController" asp-action="MyAction" asp-route-id="@item.Id">My Link</a>

Razor Pages

Utile pour les pages simples contenant du texte statique, inutile dans ce cas de passer par un controller.

Pages pouvant être appelées directement sans passer par un controller.
Mécanisme similaire aux views mais indépendant (copier le fichiers _ViewStart.cshtml de Views vers Pages).
Accessible directement via /error

Pages/Error.cshtml
@page
<h2>Oups!</h2>

Passer le Model à la View

Controllers/MyController.cs
public IActionResult Index()
{
    // get all item from the db
    var items = db.GetAllItems();
    // create a vm for the view
    var model = items.Select(item => new ItemViewModel(item));
    // pass the vm to the view and use it as Model property
    return View(model);
}
Views/Item.cshtml
@model ItemViewModel

@foreach (var item in Model)
{
    <li>
        @Html.DisplayFor(modelItem => item.Name)
    </li>
}

Validation

Controllers/MyController.cs
[HttpPost]
public IActionResult Item(ItemViewModel item)
{
    if (ModelState.IsValid)
    {
        // ...
        ModelState.Clear();
    }
    else
    {
        // afficher les erreurs
    }
    return View();
}
ViewModels/ItemViewModel.cs
public class ItemViewModel
{
    [Required]
    [Display(Name="My Property:")]  // texte du label
    public string MyProperty { get; set; }

    [MinLength(5, ErrorMessage = "Trop court")]
    [Display(Name="My Property 2:")]
    public string MyProperty2 { get; set; }
}

Avec Bower, ajouter jquery-validation et jquery-validation-unobtrusive

Views/Item.cshtml
<form asp-controller="Item" asp-action="Edit" method="post">
    <!-- affiche toutes les erreurs -->
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>

    <div class="form-group">
        <label asp-for="MyProperty" class="control-label"></label>
        <input asp-for="MyProperty" class="form-control" />
        <!-- affiche les erreurs de MyProperty -->
        <span asp-validation-for="MyProperty" class="text-danger"></span>
    </div>

    <div class="form-group">
        <input type="submit" value="Save" class="btn btn-default" />
    </div>

Services

IMyService
public interface IMyService
{
   void MyMethod();
MyService1
public class MyService1 : IMyService
{
    public void MyMethod() { }
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService1>();
    services.AddSingleton<IMyService, MyService1>(); // service en mode singleton
Controllers/MyController.cs
private readonly IMyService  _myService;

// injection de IMyService
public MyController(IMyService myService)
{
    _myService = myService;
}

Path

MyController.cs
public class MyController : Controller
{
    private readonly IHostingEnvironment _hostingEnvironment;

    public MyController(IHostingEnvironment hostingEnvironment)
    {
        _hostingEnvironment = hostingEnvironment;
    }

    [HttpGet]
    public IActionResult Get()
    {
        // c:\users\<user>\MyProject\wwwroot
        _hostingEnvironment.WebRootPath;  
        // c:\users\<user>\MyProject
        _hostingEnvironment.ContentRootPath;