Examinando os métodos de ação de edição e exibições para o controlador de filme

por Rick Anderson

Observação

Uma versão atualizada deste tutorial está disponível aqui usando a versão mais recente do Visual Studio. O novo tutorial usa ASP.NET Core MVC, que fornece muitas melhorias em relação a este tutorial.

Este tutorial ensina a usar o ASP.NET Core MVC com controladores e exibições. O Razor Pages é uma nova alternativa no ASP.NET Core, um modelo de programação baseado em página que torna a criação da interface do usuário da Web mais fácil e produtiva. É recomendável que você tente o tutorial das Páginas Razor antes da versão do MVC. O tutorial Páginas do Razor:

  • É mais fácil de acompanhar.
  • Aborda mais recursos.
  • É a abordagem preferencial para o desenvolvimento de novos aplicativos.

Nesta seção, você examinará os métodos de ação e exibições gerados Edit para o controlador de filme. Mas primeiro vamos fazer um pequeno desvio para melhorar a data de lançamento. Abra o arquivo Modelos\Movie.cs e adicione as linhas destacadas mostradas abaixo:

using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int ID { get; set; }
        public string Title { get; set; }

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }

    public class MovieDBContext : DbContext
    {
        public DbSet<Movie> Movies { get; set; }
    }
}

Você também pode tornar a cultura de data específica assim:

[Display(Name = "Release Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }

Abordaremos DataAnnotations no próximo tutorial. O atributo Display especifica o que deve ser exibido no nome de um campo (neste caso, “Release Date” em vez de “ReleaseDate”). O atributo DataType especifica o tipo dos dados, nesse caso, é uma data, portanto, as informações de hora armazenadas no campo não são exibidas. O atributo DisplayFormat é necessário para um bug no navegador Chrome que renderiza formatos de data incorretamente.

Execute o aplicativo e navegue até o Movies controlador. Mantenha o ponteiro do mouse sobre um link Editar para ver o URL ao qual ele está vinculado.

EditLink_sm

O link Editar foi gerado pelo Html.ActionLink método no modo de exibição Views\Movies\Index.cshtml :

@Html.ActionLink("Edit", "Edit", new { id=item.ID })

Html.ActionLink

O Html objeto é um auxiliar exposto usando uma propriedade na classe base System.Web.Mvc.WebViewPage . O ActionLink método do auxiliar facilita a geração dinâmica de hiperlinks HTML vinculados a métodos de ação em controladores. O primeiro argumento para o ActionLink método é o texto do link a ser renderizado (por exemplo, <a>Edit Me</a>). O segundo argumento é o nome do método de ação a ser invocado (nesse caso, a Edit ação). O argumento final é um objeto anônimo que gera os dados da rota (nesse caso, a ID de 4).

O link gerado mostrado na imagem anterior é http://localhost:1234/Movies/Edit/4. A rota padrão (estabelecida em App_Start\RouteConfig.cs) usa o padrão de {controller}/{action}/{id}URL. Portanto, ASP.NET se http://localhost:1234/Movies/Edit/4 traduz em uma solicitação ao Edit método de ação do Movies controlador com o parâmetro ID igual a 4. Examine o código a seguir no arquivo App_Start\RouteConfig.cs . O método MapRoute é usado para rotear solicitações HTTP para o controlador e o método de ação corretos e fornecer o parâmetro ID opcional. O método MapRoute também é usado pelos HtmlHelpers , como ActionLink para gerar URLs de acordo com o controlador, o método de ação e quaisquer dados de rota.

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

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

Você também pode passar parâmetros de método de ação usando uma cadeia de caracteres de consulta. Por exemplo, a URL http://localhost:1234/Movies/Edit?ID=3 também passa o parâmetro ID 3 para o Edit método de ação do Movies controlador.

EditQueryString

Abra o Movies controlador. Os dois Edit métodos de ação são mostrados abaixo.

// GET: /Movies/Edit/5
public ActionResult Edit(int? id)
{
    if (id == null)
    {
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }
    Movie movie = db.Movies.Find(id);
    if (movie == null)
    {
        return HttpNotFound();
    }
    return View(movie);
}

// POST: /Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

Observe se o segundo método de ação Edit é precedido pelo atributo HttpPost. Esse atributo especifica que a Edit sobrecarga do método pode ser invocada somente para solicitações POST. Você pode aplicar o HttpGet atributo ao primeiro método de edição, mas isso não é necessário porque é o padrão. (Vamos nos referir aos métodos de ação que são atribuídos implicitamente ao HttpGet atributo como HttpGet métodos.) O atributo Bind é outro mecanismo de segurança importante que impede que hackers publiquem dados em excesso em seu modelo. Você só deve incluir propriedades no atributo bind que deseja alterar. Você pode ler sobre overposting e o atributo bind na minha nota de segurança de overposting. No modelo simples usado neste tutorial, vincularemos todos os dados no modelo. O atributo ValidateAntiForgeryToken é usado para evitar a falsificação de uma solicitação e é emparelhado com @Html.AntiForgeryToken() no arquivo de exibição de edição (Views\Movies\Edit.cshtml), uma parte é mostrada abaixo:

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>

@Html.AntiForgeryToken() Gera um token anti-falsificação de forma oculta que deve corresponder ao Edit método do Movies controlador. Você pode ler mais sobre falsificação de solicitação entre sites (também conhecida como XSRF ou CSRF) em meu tutorial Prevenção de XSRF/CSRF no MVC.

O HttpGet Edit método usa o parâmetro ID do filme, pesquisa o filme usando o método Entity Framework Find e retorna o filme selecionado para o modo de exibição Editar. Se um filme não puder ser encontrado, HttpNotFound será retornado. Quando o sistema de scaffolding criou a exibição de Edição, ele examinou a classe Movie e o código criado para renderizar os elementos <label> e <input> de cada propriedade da classe. O seguinte exemplo mostra a exibição de Edição que foi gerada pelo sistema de scaffolding do Visual Studio:

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()    
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        @Html.ValidationSummary(true)
        @Html.HiddenFor(model => model.ID)

        <div class="form-group">
            @Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.Title)
                @Html.ValidationMessageFor(model => model.Title)
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.ReleaseDate, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.EditorFor(model => model.ReleaseDate)
                @Html.ValidationMessageFor(model => model.ReleaseDate)
            </div>
        </div>
        @*Genre and Price removed for brevity.*@        
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Observe como o modelo de exibição tem uma @model MvcMovie.Models.Movie instrução na parte superior do arquivo — isso especifica que a exibição espera que o modelo para o modelo de exibição seja do tipo Movie.

O código scaffolded usa vários métodos auxiliares para simplificar a marcação HTML. O Html.LabelFor auxiliar exibe o nome do campo ("Título", "Data de lançamento", "Gênero" ou "Preço"). O Html.EditorFor auxiliar renderiza um elemento HTML <input> . O Html.ValidationMessageFor auxiliar exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue até a URL / Movies . Clique em um link Editar. No navegador, exiba a origem da página. O HTML do elemento de formulário é mostrado abaixo.

<form action="/movies/Edit/4" method="post">
   <input name="__RequestVerificationToken" type="hidden" value="UxY6bkQyJCXO3Kn5AXg-6TXxOj6yVBi9tghHaQ5Lq_qwKvcojNXEEfcbn-FGh_0vuw4tS_BRk7QQQHlJp8AP4_X4orVNoQnp2cd8kXhykS01" />  <fieldset class="form-horizontal">
      <legend>Movie</legend>

      <input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />

      <div class="control-group">
         <label class="control-label" for="Title">Title</label>
         <div class="controls">
            <input class="text-box single-line" id="Title" name="Title" type="text" value="GhostBusters" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Title" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="ReleaseDate">Release Date</label>
         <div class="controls">
            <input class="text-box single-line" data-val="true" data-val-date="The field Release Date must be a date." data-val-required="The Release Date field is required." id="ReleaseDate" name="ReleaseDate" type="date" value="1/1/1984" />
            <span class="field-validation-valid help-inline" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="Genre">Genre</label>
         <div class="controls">
            <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Comedy" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="control-group">
         <label class="control-label" for="Price">Price</label>
         <div class="controls">
            <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" type="text" value="7.99" />
            <span class="field-validation-valid help-inline" data-valmsg-for="Price" data-valmsg-replace="true"></span>
         </div>
      </div>

      <div class="form-actions no-color">
         <input type="submit" value="Save" class="btn" />
      </div>
   </fieldset>
</form>

Os <input> elementos estão em um elemento HTML <form> cujo action atributo está definido para postar na URL /Movies/Edit . Os dados do formulário serão postados no servidor quando o botão Salvar for clicado. A segunda linha mostra o token XSRF oculto gerado pela @Html.AntiForgeryToken() chamada.

Processando a solicitação POST

A lista a seguir mostra a versão HttpPost do método de ação Edit.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (ModelState.IsValid)
    {
        db.Entry(movie).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(movie);
}

O atributo ValidateAntiForgeryToken valida o token XSRF gerado pela @Html.AntiForgeryToken() chamada na exibição.

O associador de modelo MVC ASP.NET usa os valores de formulário postados e cria um Movie objeto que é passado como o movie parâmetro. O ModelState.IsValid verifica se os dados enviados no formulário podem ser usados para modificar (editar ou atualizar) um Movie objeto. Se os dados forem válidos, os dados do filme serão salvos na Movies coleção da db(MovieDBContext instância). Os novos dados do filme são salvos no banco de dados chamando o SaveChanges método de MovieDBContext. Depois de salvar os dados, o código redireciona o usuário para o método de ação Index da classe MoviesController, que exibe a coleção de filmes, incluindo as alterações feitas recentemente.

Assim que a validação do lado do cliente determinar que o valor de um campo não é válido, uma mensagem de erro será exibida. Se o JavaScript estiver desabilitado, a validação do lado do cliente será desabilitada. No entanto, o servidor detecta que os valores postados não são válidos e os valores do formulário são exibidos novamente com mensagens de erro.

A validação é examinada com mais detalhes posteriormente no tutorial.

Os Html.ValidationMessageFor auxiliares no modelo de exibição Edit.cshtml cuidam da exibição de mensagens de erro apropriadas.

abcNotValid

Todos os HttpGet métodos seguem um padrão semelhante. Eles obtêm um objeto de filme (ou lista de objetos, no caso de Index) e passam o modelo para a exibição. O Create método passa um objeto de filme vazio para o modo de exibição Create. Todos os métodos que criam, editam, excluem ou, de outro modo, modificam dados fazem isso na sobrecarga HttpPost do método. Modificar dados em um método HTTP GET é um risco de segurança. A modificação de dados em um método GET também viola as práticas recomendadas de HTTP e o padrão REST de arquitetura, que especifica que as solicitações GET não devem alterar o estado do aplicativo. Em outras palavras, a execução de uma operação GET deve ser uma operação segura que não tem efeitos colaterais e não modifica os dados persistentes.

Validação do jQuery para localidades diferentes do inglês

Se você estiver usando um computador em inglês dos EUA, poderá pular esta seção e ir para o próximo tutorial. Você pode baixar a versão Globalize deste tutorial aqui. Para um excelente tutorial de duas partes sobre Internacionalização, consulte o ASP.NET MVC 5 de Nadeem Internacionalização.

Observação

para oferecer suporte à validação do jQuery para localidades que não são em inglês que usam uma vírgula (",") para um ponto decimal e formatos de data que não são em inglês dos EUA, você deve incluir globalize.js e suas culturas/globalize.cultures.js arquivo(from https://github.com/jquery/globalize ) e JavaScript específicos para usar Globalize.parseFloato . Você pode obter a validação do jQuery que não está em inglês do NuGet. (Não instale Globalize se você estiver usando uma localidade em inglês.)

  1. No menu Ferramentas, clique em Gerenciador de Pacotes NuGet e, em seguida, clique em Gerenciar Pacotes NuGet para Solução.

    Captura de tela do menu Ferramentas para iniciar a validação do jQuery para localidades que não são em inglês.

  2. No painel esquerdo, selecione Procurar*.*(Veja a imagem abaixo.)

  3. Na caixa de entrada, insira Globalize*.

    Captura de tela da caixa de entrada para entrar em Globalizar.

    Escolha jQuery.Validation.Globalize, escolha MvcMovie e clique em Instalar. O arquivo Scripts\jquery.globalize\globalize.js será adicionado ao seu projeto. A pasta *Scripts\jquery.globalize\cultures* conterá muitos arquivos JavaScript de cultura. Observe que pode levar cinco minutos para instalar este pacote.

    O código a seguir mostra as modificações no arquivo Views\Movies\Edit.cshtml:

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")

<script src="~/Scripts/globalize/globalize.js"></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.@(System.Threading.Thread.CurrentThread.CurrentCulture.Name).js"></script>
<script>
    $.validator.methods.number = function (value, element) {
        return this.optional(element) ||
            !isNaN(Globalize.parseFloat(value));
    }
    $(document).ready(function () {
        Globalize.culture('@(System.Threading.Thread.CurrentThread.CurrentCulture.Name)');
    });
</script>
<script>
    jQuery.extend(jQuery.validator.methods, {
        range: function (value, element, param) {
            //Use the Globalization plugin to parse the value
            var val = Globalize.parseFloat(value);
            return this.optional(element) || (
                val >= param[0] && val <= param[1]);
        }
    });
    $.validator.methods.date = function (value, element) {
        return this.optional(element) ||
            Globalize.parseDate(value) ||
            Globalize.parseDate(value, "yyyy-MM-dd");
    }
</script>
}

Para evitar a repetição desse código em cada modo de exibição Editar, você pode movê-lo para o arquivo de layout. Para otimizar o download do script, consulte meu tutorial Agrupamento e Minificação.

Para obter mais informações, consulte ASP.NET Internacionalização do MVC 3 e ASP.NET Internacionalização do MVC 3 – Parte 2 (NerdDinner).

Como uma correção temporária, se você não conseguir que a validação funcione em sua localidade, poderá forçar seu computador a usar o inglês dos EUA ou desabilitar o JavaScript em seu navegador. Para forçar seu computador a usar o inglês dos EUA, você pode adicionar o elemento de globalização ao arquivo web.config raiz do projeto. O código a seguir mostra o elemento de globalização com a cultura definida como inglês dos Estados Unidos.

<system.web>
    <globalization culture ="en-US" />
    <!--elements removed for clarity-->
  </system.web>

No próximo tutorial, implementaremos a funcionalidade de pesquisa.