Parte 6, os métodos e as exibições do controlador no ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e .NET Core. Para a versão atual, consulte a versão do .NET 10 deste artigo.

Temos um bom começo para o aplicativo de filmes, mas a apresentação não é ideal. Por exemplo, ReleaseDate deveria ser escrito em duas palavras.

Exibição de índice: data de lançamento é uma palavra (sem espaço).

Abra o arquivo Models/Movie.cs e adicione as linhas realçadas mostradas abaixo:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    
    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
}

DataAnnotations são explicados 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 (Data).

Usando as anotações de dados acima, execute o aplicativo e atualize a /Movies página. Como o markup de exibição usa os métodos @Html.DisplayNameFor e @Html.DisplayFor para renderizar o nome e o valor da propriedade, a visão atualizada Index mostra todos os campos corretamente formatados. Por exemplo, a Data de Lançamento agora é duas palavras.

Exibição de índice: a Data de Lançamento é composta por duas palavras.

A anotação de dados [Column(TypeName = "decimal(18, 2)")] é necessária para que o Entity Framework Core possa mapear corretamente Price como moeda no banco de dados. Para obter mais informações, veja Tipos de Dados.

Procure o controlador Movies e mantenha o ponteiro do mouse pressionado sobre um link Editar para ver a URL de destino.

São mostrados uma janela do navegador com o mouse sobre o link de edição e um link da URL.

Os links Editar, Detalhes e Excluir são gerados pelo Auxiliar de Marcação de Âncora do MVC Core no arquivo Views/Movies/Index.cshtml.

        <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
        <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
        <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
    </td>
</tr>

Os Tag Helpers permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos Razor. No código acima, o AnchorTagHelper gera dinamicamente o valor do atributo href HTML com base na ID de rota e no método de ação do controlador. Use a opção Exibir Código-fonte em seu navegador favorito ou as ferramentas do desenvolvedor para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

Lembre-se do formato do roteamento definido no arquivo Program.cs:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}")
    .WithStaticAssets();

O ASP.NET Core converte https://localhost:5001/Movies/Edit/4 de uma solicitação no método de ação Edit do controlador Movies com o parâmetro Id igual a 4. (Os métodos do controlador também são conhecidos como métodos de ação.)

Os Auxiliares de Marcação são um dos novos recursos mais populares do ASP.NET Core. Para obter mais informações, consulte Conteúdo relacionado.

Abra o Movies controlador e examine os dois Edit métodos de ação. O código a seguir mostra o método HTTP GET Edit, que busca o filme e popula o formulário de edição gerado pelo arquivo Edit.cshtmlRazor do Razor.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

O código a seguir mostra o método HTTP POST Edit, que processa os valores de filmes postados:

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [Bind] é uma maneira de proteger contra o over-posting. Você somente deve incluir no atributo [Bind] as propriedades que deseja alterar. Para obter mais informações, consulte Proteger o controlador contra o excesso de postagem. ViewModels fornecem uma abordagem alternativa para evitar over-posting.

Observe se o segundo método de ação Edit é precedido pelo atributo [HttpPost].

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo HttpPost especifica que esse Edit método pode ser invocado somente para requisições POST. Você pode aplicar o atributo [HttpGet] ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O atributo ValidateAntiForgeryToken é usado para prevenir a falsificação de uma solicitação e é associado a um token antifalsificação gerado no arquivo de exibição de edição (Views/Movies/Edit.cshtml). O arquivo de exibição de edição gera o token antifalsificação com o Auxiliar de Marcação de Formulário.

<form asp-action="Edit">

O Auxiliar de Marcação de Formulário gera um token antifalsificação oculto que deve corresponder ao token antifalsificação gerado [ValidateAntiForgeryToken] no método Edit do controlador Movies. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.

O método HttpGet Edit recebe o parâmetro de filme ID, procura o filme usando o método FindAsync do Entity Framework e retorna o filme selecionado para a view Edit. Se um filme não for encontrado, será retornado NotFound (HTTP 404).

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Quando o sistema de scaffolding criou a exibição Edit, ele examinou a classe Movie e criou código para renderizar os elementos <label> e <input> para 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

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Observe como o modelo de visualização tem uma instrução @model MvcMovie.Models.Movie no topo do arquivo. @model MvcMovie.Models.Movie especifica que o modelo esperado pelo template de exibição é do tipo Movie.

O código com scaffolding usa vários métodos Tag Helper para simplificar a marcação HTML. O Auxiliar de Marcação de Rótulo exibe o nome do campo (“Title”, “ReleaseDate”, “Genre” ou “Price”). O Input Tag Helper renderiza um elemento <input> HTML. O Auxiliar de Marcação de Validação exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue para a URL /Movies. Clique em um link Editar. No navegador, veja o código-fonte da página. O HTML gerado para o elemento <form> é mostrado abaixo.

<form action="/Movies/Edit/7" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div class="text-danger" />
        <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre" />
            <div class="col-md-10">
                <input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-2" for="Price" />
            <div class="col-md-10">
                <input class="form-control" type="text" 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" value="3.99" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
            </div>
        </div>
        <!-- Markup 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>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>

Os elementos <input> estão em um elemento HTML <form> cujo atributo action está definido para ser postado para a URL /Movies/Edit/id. Os dados do formulário serão enviados ao servidor quando o botão Save for clicado. A última linha antes do elemento </form> de fechamento mostra o token XSRF oculto gerado pelo Tag Helper de Formulário.

Processando a solicitação POST

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

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [ValidateAntiForgeryToken] valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Tag Helper de Formulário

O sistema de model binding usa os valores de formulário postados e cria um objeto Movie que é passado como o parâmetro movie. A propriedade ModelState.IsValid verifica se os dados enviados no formulário podem ser usados para modificar (editar ou atualizar) um objeto Movie. Se os dados forem válidos, eles serão salvos. Os dados de filmes atualizados (editados) são salvos no banco de dados ao chamar o método SaveChangesAsync do contexto de banco de dados. 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 recém-feitas.

Antes que o formulário seja postado no servidor, a validação do lado do cliente verifica as regras de validação nos campos. Se houver erros de validação, será exibida uma mensagem de erro e o formulário não será enviado. Se o JavaScript estiver desabilitado, você não terá a validação do lado do cliente, mas o servidor detectará os valores postados que não forem válidos e os valores de formulário serão exibidos novamente com mensagens de erro. Mais adiante no tutorial, examinamos a Validação de Modelos mais detalhadamente. O Tag Helper de Validação no modelo de exibição Views/Movies/Edit.cshtml é responsável por exibir as mensagens de erro apropriadas.

Exibição de edição: uma exceção para um valor de Preço incorreto 'abc' afirma que o campo Preço deve ser um número. Uma exceção para um valor de Data de Lançamento incorreto afirma: Insira uma data válida.

Todos os métodos do controlador de filmes HttpGet seguem um padrão semelhante. Eles obtêm um objeto de filme (ou uma lista de objetos, no caso de Index) e passam o objeto (modelo) para a view. O método Create passa um objeto de filme vazio para a visualização Create. Todos os métodos que criam, editam, excluem ou, de outro modo, modificam dados fazem isso na sobrecarga [HttpPost] do método. A modificação de dados em um método HTTP GET é um risco de segurança. A modificação de dados em um método HTTP GET também viola as melhores práticas 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.

Temos um bom começo para o aplicativo de filmes, mas a apresentação visual não está ideal, por exemplo, ReleaseDate deveria ser escrito em duas palavras.

Exibição de índice: a Data de Lançamento é uma palavra (sem espaço) e cada data de lançamento do filme mostra o horário 12h

Abra o arquivo Models/Movie.cs e adicione as linhas realçadas mostradas abaixo:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    
    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
}

DataAnnotations são explicados 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 de dados (Date) e, portanto, as informações de hora armazenadas no campo não são exibidas.

Usando as anotações de dados acima, execute o aplicativo e atualize a /Movies página. Como o markup de exibição usa os métodos @Html.DisplayNameFor e @Html.DisplayFor para renderizar o nome e o valor da propriedade, a visão atualizada Index mostra todos os campos corretamente formatados. Por exemplo, a Data de Lançamento agora é duas palavras e as informações de hora não são mais mostradas.

Visualização de índice: a data de lançamento é composta de duas palavras e cada data de lançamento do filme é exibida sem o horário.

A anotação de dados [Column(TypeName = "decimal(18, 2)")] é necessária para que o Entity Framework Core possa mapear corretamente o Price como moeda no banco de dados. Para obter mais informações, veja Tipos de Dados.

Navegue até o controlador Movies e mantenha o ponteiro do mouse sobre o link Editar para ver a URL de destino.

São mostrados uma janela do navegador com o mouse sobre o link de edição e um link da URL https://localhost:5001/Movies/Edit/5

Os links Editar, Detalhes e Excluir são gerados pelo Tag Helper Âncora do Core MVC no arquivo Views/Movies/Index.cshtml.

        <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
        <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
        <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
    </td>
</tr>

Os Tag Helpers permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos Razor. No código acima, o AnchorTagHelper gera dinamicamente o valor do atributo href HTML com base na ID de rota e no método de ação do controlador. Use a opção Exibir Código-fonte em seu navegador favorito ou as ferramentas do desenvolvedor para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

Lembre-se do formato do roteamento definido no arquivo Program.cs:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

O ASP.NET Core converte https://localhost:5001/Movies/Edit/4 de uma solicitação no método de ação Edit do controlador Movies com o parâmetro Id igual a 4. (Os métodos do controlador também são conhecidos como métodos de ação.)

Os Tag Helpers são um dos novos recursos mais populares do ASP.NET Core. Para obter mais informações, consulte Conteúdo relacionado.

Abra o controlador Movies e examine os dois métodos de ação Edit. O código a seguir mostra o método HTTP GET Edit, que busca o filme e popula o formulário de edição gerado pelo arquivo Edit.cshtmlRazor do Razor.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

O código a seguir apresenta o método HTTP POST Edit, que processa os valores do filme enviados:

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [Bind] é uma maneira de proteger contra o excesso de postagem. Você somente deve incluir as propriedades do atributo [Bind] que deseja alterar. Para obter mais informações, consulte Proteger o controlador contra o excesso de postagem. ViewModels fornecem uma abordagem alternativa para evitar over-posting.

Observe se o segundo método de ação Edit é precedido pelo atributo [HttpPost].

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo HttpPost especifica que esse método Edit pode ser invocado somente para solicitações POST. Você pode aplicar o atributo [HttpGet] ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O atributo ValidateAntiForgeryToken é usado para prevenir a falsificação de uma solicitação e é associado a um token antifalsificação gerado no arquivo de exibição de edição (Views/Movies/Edit.cshtml). O arquivo de exibição de edição gera o token antifalsificação com o Form Tag Helper.

<form asp-action="Edit">

O Auxiliar de Marcação de Formulário gera um token antifalsificação oculto que deve corresponder ao token antifalsificação gerado [ValidateAntiForgeryToken] no método Edit do controlador Movies. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.

O método HttpGet Edit usa o parâmetro ID de filme, pesquisa o filme usando o método FindAsync do Entity Framework e retorna o filme selecionado para a exibição de Edição. Se um filme não for encontrado, NotFound (HTTP 404) será retornado.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Quando o sistema de scaffolding criou a exibição Edit, ele examinou a classe Movie e criou código para renderizar os elementos <label> e <input> para 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

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

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

O código com scaffolding usa vários métodos de Auxiliares de Marcação para simplificar a marcação HTML. O Tag Helper Label exibe o nome do campo (“Title”, “ReleaseDate”, “Genre” ou “Price”). O Auxiliar de Marcação de Entrada renderiza um elemento <input> HTML. O Tag Helper de Validação exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue para a URL /Movies. Clique em um link Editar. No navegador, exiba o código-fonte da página. O HTML gerado para o elemento <form> é mostrado abaixo.

<form action="/Movies/Edit/7" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div class="text-danger" />
        <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre" />
            <div class="col-md-10">
                <input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-2" for="Price" />
            <div class="col-md-10">
                <input class="form-control" type="text" 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" value="3.99" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
            </div>
        </div>
        <!-- Markup 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>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>

Os elementos <input> estão em um elemento HTML <form> cujo atributo action está definido para ser postado para a URL /Movies/Edit/id. Os dados de formulário serão postados para o servidor quando o botão Save receber um clique. A última linha antes do elemento </form> de fechamento mostra o token XSRF oculto gerado pelo Form Tag Helper.

Processando a requisição POST

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

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [ValidateAntiForgeryToken] valida o token XSRF oculto gerado pelo gerador de token antifalsificação no Tag Helper de Formulário

O sistema de model binding usa os valores de formulário postados e cria um objeto Movie que é passado como o parâmetro movie. A propriedade ModelState.IsValid verifica se os dados enviados no formulário podem ser usados para modificar (editar ou atualizar) um objeto Movie. Se os dados forem válidos, eles serão salvos. Os dados de filmes atualizados (editados) são salvos no banco de dados chamando o método SaveChangesAsync do contexto de banco de dados. 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.

Antes que o formulário seja postado no servidor, a validação do lado do cliente verifica as regras de validação nos campos. Se houver erros de validação, será exibida uma mensagem de erro e o formulário não será postado. Se o JavaScript estiver desabilitado, você não terá a validação do lado do cliente, mas o servidor detectará os valores postados que não forem válidos e os valores de formulário serão exibidos novamente com mensagens de erro. Mais adiante no tutorial, examinamos a Validação de Modelos mais detalhadamente. O Tag Helper de Validação no modelo de exibição Views/Movies/Edit.cshtml é responsável por exibir as mensagens de erro apropriadas.

Exibição de edição: uma exceção para o valor de Preço incorreto abc informa que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento dos estados xyz; insira uma data válida.

Todos os métodos no HttpGet controlador de filme seguem um padrão semelhante. Eles obtêm um objeto de filme (ou uma lista de objetos, no caso de Index) e passam o objeto (modelo) para a exibição. O método Create passa um objeto de filme vazio para a 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. A modificação de dados em um método HTTP GET é um risco de segurança. A modificação de dados em um método HTTP GET também viola as melhores práticas de HTTP e o padrão REST de arquitetura, que especifica que as solicitações GET não devem mudar o estado da sua aplicação. 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.

Temos um bom começo para o aplicativo de filme, mas a apresentação não é ideal. Por exemplo, ReleaseDate deveria ser separado em duas palavras.

Exibição de índice: a Data de Lançamento é uma palavra (sem espaço) e cada data de lançamento do filme mostra o horário 12h

Abra o arquivo Models/Movie.cs e adicione as linhas realçadas mostradas abaixo:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    
    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
}

DataAnnotations são explicados 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 de dados (Date) e, portanto, as informações de hora armazenadas no campo não são exibidas.

A anotação de dados [Column(TypeName = "decimal(18, 2)")] é necessária para que o Entity Framework Core possa mapear corretamente o Price para a moeda no banco de dados. Para obter mais informações, veja Tipos de Dados.

Procure o controlador Movies e mantenha o ponteiro do mouse pressionado sobre um link Editar para ver a URL de destino.

É mostrada uma janela do navegador com o mouse sobre o link de edição e uma URL de link de https://localhost:5001/Movies/Edit/5 é exibida

Os links Editar, Detalhes e Excluir são gerados pelo Tag Helper Âncora do Core MVC no arquivo Views/Movies/Index.cshtml.

        <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
        <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
        <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
    </td>
</tr>

Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor. No código acima, o AnchorTagHelper gera dinamicamente o valor do atributo href HTML com base na ID de rota e no método de ação do controlador. Use a opção Exibir Código-fonte em seu navegador favorito ou as ferramentas do desenvolvedor para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

Lembre-se do formato do roteamento definido no arquivo Program.cs:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

O ASP.NET Core converte https://localhost:5001/Movies/Edit/4 de uma solicitação no método de ação Edit do controlador Movies com o parâmetro Id igual a 4. (Os métodos do controlador também são conhecidos como métodos de ação.)

Os Tag Helpers são um dos novos recursos mais populares do ASP.NET Core. Para obter mais informações, consulte Conteúdo relacionado.

Abra o controlador Movies e examine os dois métodos de ação Edit. O código a seguir mostra o método HTTP GET Edit, que busca o filme e popula o formato de edição gerado pelo arquivo Edit.cshtmlRazor do Razor.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

O código a seguir mostra o método HTTP POST Edit, que processa os valores de filme enviados:

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [Bind] é uma maneira de proteger contra o over-posting. Você somente deve incluir no atributo [Bind] as propriedades que deseja alterar. Para obter mais informações, consulte Proteger o controlador contra o excesso de postagem. ViewModels fornece uma abordagem alternativa para prevenir o excesso de postagem.

Observe que o segundo método de ação Edit é precedido pelo atributo [HttpPost].

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo HttpPost especifica que esse método Edit pode ser invocado somente para solicitações POST. Você pode aplicar o atributo [HttpGet] ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O atributo ValidateAntiForgeryToken é usado para prevenir a falsificação de uma solicitação e é associado a um token antifalsificação gerado no arquivo de exibição de edição (Views/Movies/Edit.cshtml). O arquivo de exibição de edição gera o token antifalsificação com o Tag Helper de Formulário.

<form asp-action="Edit">

O Tag Helper de Formulário gera um token antifalsificação oculto que deve corresponder ao [ValidateAntiForgeryToken] token antifalsificação gerado no método Edit do controlador Movies. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.

O método HttpGet Edit usa o parâmetro ID de filme, pesquisa o filme usando o método FindAsync do Entity Framework e retorna o filme selecionado para a exibição de Edição. Se um filme não for encontrado, NotFound (HTTP 404) será retornado.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Quando o sistema de scaffolding criou a exibição Edit, ele examinou a classe Movie e criou código para renderizar os elementos <label> e <input> para 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

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Observe que o modelo de visualização tem uma declaração @model MvcMovie.Models.Movie na parte superior do arquivo. @model MvcMovie.Models.Movie especifica que a exibição espera que o modelo de exibição seja do tipo Movie.

O código gerado por scaffolding usa diversos métodos de Tag Helpers para simplificar o código HTML. O Auxiliar de Marcação de Rótulo exibe o nome do campo (“Title”, “ReleaseDate”, “Genre” ou “Price”). O Auxiliar de Marcação de Entrada renderiza um elemento <input> HTML. O Auxiliar de Marcação de Validação exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue para a URL /Movies. Clique em um link Editar. No navegador, exiba a origem da página. O HTML gerado para o elemento <form> é mostrado abaixo.

<form action="/Movies/Edit/7" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div class="text-danger" />
        <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre" />
            <div class="col-md-10">
                <input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-2" for="Price" />
            <div class="col-md-10">
                <input class="form-control" type="text" 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" value="3.99" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
            </div>
        </div>
        <!-- Markup 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>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>

Os elementos <input> estão em um elemento HTML <form> cujo atributo action está definido para postar para a URL /Movies/Edit/id. Os dados de formulário serão postados para o servidor quando o botão Save receber um clique. A última linha antes do elemento </form> de fechamento mostra o token XSRF oculto gerado pelo Auxiliar de Marcação de Formulário.

Processamento da requisição POST

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

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [ValidateAntiForgeryToken] valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Form Tag Helper

O sistema de model binding usa os valores de formulário postados e cria um objeto Movie que é passado como o parâmetro movie. A propriedade ModelState.IsValid verifica se os dados enviados no formulário podem ser usados para modificar (editar ou atualizar) um objeto Movie. Se os dados forem válidos, eles serão salvos. Os dados de filmes atualizados (editados) são salvos no banco de dados chamando o método SaveChangesAsync do contexto de banco de dados. 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 que acabaram de ser feitas.

Antes que o formulário seja postado no servidor, a validação do lado do cliente verifica as regras de validação nos campos. Se houver erros de validação, será exibida uma mensagem de erro e o formulário não será enviado. Se o JavaScript estiver desabilitado, você não terá a validação do lado do cliente, mas o servidor detectará os valores postados que não forem válidos e os valores de formulário serão exibidos novamente com mensagens de erro. Mais adiante no tutorial, examinamos a Validação de Modelos mais detalhadamente. O Auxiliar de Marcação de Validação no modelo de exibição Views/Movies/Edit.cshtml é responsável por exibir as mensagens de erro apropriadas.

Exibição de edição: uma exceção para o valor de Preço incorreto abc informa que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento dos estados xyz; insira uma data válida.

Todos os métodos HttpGet no controlador de filme seguem um padrão semelhante. Eles obtêm um objeto de filme (ou uma lista de objetos, no caso de Index) e passam o objeto (modelo) para a view. O método Create passa um objeto de filme vazio para a 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. A modificação de dados em um método HTTP GET é um risco de segurança. A modificação de dados em um método HTTP GET também viola as melhores práticas 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.

Temos um bom começo para o aplicativo de filme, mas a apresentação não é ideal. Por exemplo, ReleaseDate deveria ser separado em duas palavras.

Exibição de índice: a Data de Lançamento é uma palavra (sem espaço) e cada data de lançamento do filme mostra o horário 12h

Abra o arquivo Models/Movie.cs e adicione as linhas realçadas mostradas abaixo:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    
    [Display(Name = "Release Date")]
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    [Column(TypeName = "decimal(18, 2)")]
    public decimal Price { get; set; }
}

DataAnnotations são explicados no próximo tutorial. O atributo Display especifica o que deve ser exibido para o nome de um campo (neste caso, “Release Date” em vez de “ReleaseDate”). O atributo DataType especifica o tipo de dados (Data) e, portanto, as informações de hora armazenadas no campo não são exibidas.

O atributo de anotação de dados [Column(TypeName = "decimal(18, 2)")] é necessário para que Entity Framework Core possa mapear corretamente Price como moeda no banco de dados. Para obter mais informações, veja Tipos de Dados.

Navegue até o controlador Movies e mantenha o ponteiro do mouse sobre um link Editar para ver a URL de destino.

É mostrada uma janela do navegador com o mouse sobre o link Editar e com a URL do link sendo https://localhost:5001/Movies/Edit/5

Os links Editar, Detalhes e Excluir são gerados pelo Tag Helper de âncora do Core MVC no arquivo Views/Movies/Index.cshtml.

        <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
        <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
        <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
    </td>
</tr>

Os Tag Helpers permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos Razor. No código acima, o AnchorTagHelper gera dinamicamente o valor do atributo href HTML com base na ID de rota e no método de ação do controlador. Use a opção Exibir Código-fonte em seu navegador favorito ou as ferramentas do desenvolvedor para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

Lembre-se do formato do roteamento definido no arquivo Program.cs:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

O ASP.NET Core traduz https://localhost:5001/Movies/Edit/4 em uma solicitação para o método de ação Edit do controlador Movies com o parâmetro Id igual a 4. (Os métodos do controlador também são conhecidos como métodos de ação.)

Os Tag Helpers são um dos novos recursos mais populares do ASP.NET Core. Para obter mais informações, consulte Conteúdo relacionado.

Abra o controlador Movies e examine os dois métodos de ação Edit. O código a seguir mostra o método HTTP GET Edit, que busca o filme e popula o formato de edição gerado pelo arquivo Edit.cshtmlRazor do Razor.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

O código a seguir mostra o método HTTP POST Edit, que processa os valores de filmes postados:

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [Bind] é uma maneira de proteger contra a postagem excessiva de dados. Você somente deve incluir as propriedades do atributo [Bind] que deseja alterar. Para obter mais informações, consulte Proteger o controlador contra o excesso de postagem. ViewModels fornece uma abordagem alternativa para prevenir o excesso de postagem.

Observe que o segundo método de ação Edit é precedido pelo atributo [HttpPost].

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo HttpPost especifica que esse Edit método pode ser invocado somente para POST solicitações. Você pode aplicar o atributo [HttpGet] ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O atributo ValidateAntiForgeryToken é usado para prevenir a falsificação de uma solicitação e é associado a um token antifalsificação gerado no arquivo de exibição de edição (Views/Movies/Edit.cshtml). O arquivo de exibição de edição gera o token antifalsificação com o Auxiliar de Marcação de Formulário.

<form asp-action="Edit">

O Auxiliar de Marcação de Formulário gera um token antifalsificação oculto que deve corresponder ao token antifalsificação gerado [ValidateAntiForgeryToken] no método Edit do controlador Movies. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.

O método HttpGet Edit recebe o parâmetro de filme ID, pesquisa o filme usando o método FindAsync do Entity Framework e retorna o filme selecionado para a exibição de Edição. Se um filme não for encontrado, NotFound (HTTP 404) será retornado.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

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 o modo de exibição Editar que foi gerado pelo sistema de scaffolding do Visual Studio:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

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

O código com scaffolding usa vários métodos de Auxiliares de Marcação para simplificar a marcação HTML. O Label Tag Helper exibe o nome do campo (“Title”, “ReleaseDate”, “Genre” ou “Price”). O Auxiliar de Marcação de Entrada renderiza um elemento <input> HTML. O Auxiliar de Marcação de Validação exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue para a URL /Movies. Clique em um link Editar. No navegador, exiba o código-fonte da página. O HTML gerado para o elemento <form> é mostrado abaixo.

<form action="/Movies/Edit/7" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div class="text-danger" />
        <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre" />
            <div class="col-md-10">
                <input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-2" for="Price" />
            <div class="col-md-10">
                <input class="form-control" type="text" 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" value="3.99" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
            </div>
        </div>
        <!-- Markup 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>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>

Os elementos <input> estão em um elemento HTML <form> cujo atributo action está definido para ser postado para a URL /Movies/Edit/id. Os dados de formulário serão postados com o servidor quando o botão Save receber um clique. A última linha antes do elemento </form> de fechamento mostra o token XSRF oculto gerado pelo Tag Helper de Formulário.

Processando a solicitação POST

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

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [ValidateAntiForgeryToken] valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Form Tag Helper

O sistema de model binding usa os valores de formulário postados e cria um objeto Movie que é passado como o parâmetro movie. A propriedade ModelState.IsValid verifica se os dados enviados no formulário podem ser usados para modificar (editar ou atualizar) um objeto Movie. Se os dados forem válidos, eles serão salvos. Os dados de filmes atualizados (editados) são salvos no banco de dados chamando o método SaveChangesAsync do contexto de banco de dados. 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.

Antes que o formulário seja postado no servidor, a validação do lado do cliente verifica as regras de validação nos campos. Se houver erros de validação, será exibida uma mensagem de erro e o formulário não será postado. Se o JavaScript estiver desabilitado, você não terá a validação do lado do cliente, mas o servidor detectará os valores postados que não forem válidos e os valores de formulário serão exibidos novamente com mensagens de erro. Mais adiante no tutorial, examinamos a Validação de Modelos mais detalhadamente. O Auxiliar de Marcação de Validação no modelo de exibição Views/Movies/Edit.cshtml é responsável por exibir as mensagens de erro apropriadas.

Exibição de edição: uma exceção para o valor de Preço incorreto abc informa que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento dos estados xyz; insira uma data válida.

Todos os métodos HttpGet no controlador de filme seguem um padrão semelhante. Eles obtêm um objeto de filme (ou uma lista de objetos, no caso de Index) e passam o objeto (modelo) para a exibição. O método Create passa um objeto de filme vazio para a 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. Modificar dados em um método HTTP GET também viola as melhores práticas de HTTP e o padrão arquitetural REST, 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.

Temos um bom começo para o aplicativo de filme, mas a apresentação não é ideal. Por exemplo, ReleaseDate deveria ser separado em duas palavras.

Exibição de índice:

Abra o arquivo Models/Movie.cs e adicione as linhas realçadas mostradas abaixo:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

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

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string? Genre { get; set; }

        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { get; set; }
    }
}

DataAnnotations são explicados 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 de dados (Date) e, portanto, as informações de hora armazenadas no campo não são exibidas.

A anotação de dados [Column(TypeName = "decimal(18, 2)")] é necessária para que o Entity Framework Core possa mapear corretamente o Price para o tipo monetário no banco de dados. Para obter mais informações, veja Tipos de Dados.

Procure o controlador Movies e mantenha o ponteiro do mouse pressionado sobre um link Editar para ver a URL de destino.

São mostrados uma janela do navegador com o mouse sobre o link de edição e um link da URL https://localhost:5001/Movies/Edit/5

Os links Editar, Detalhes e Excluir são gerados pelo Tag Helper de âncora do Core MVC no arquivo Views/Movies/Index.cshtml.

        <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
        <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
        <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
    </td>
</tr>

Os Tag Helpers permitem que o código do servidor participe da criação e da renderização de elementos HTML em arquivos Razor. No código acima, o AnchorTagHelper gera dinamicamente o valor do atributo href HTML com base na ID de rota e no método de ação do controlador. Use a opção Exibir Código-fonte em seu navegador favorito ou as ferramentas do desenvolvedor para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

Lembre-se do formato do roteamento definido no arquivo Program.cs:

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

O ASP.NET Core converte https://localhost:5001/Movies/Edit/4 em uma solicitação para o método de ação Edit do controlador Movies com o parâmetro Id igual a 4. (Os métodos do controlador também são conhecidos como métodos de ação.)

Os Auxiliares de Marcação são um recurso popular no ASP.NET Core. Para obter mais informações sobre eles, consulte Conteúdo relacionado.

Abra o controlador Movies e examine os dois métodos de ação Edit. O código a seguir mostra o método HTTP GET Edit, que busca o filme e popula o formulário de edição gerado pelo arquivo Edit.cshtmlRazor do Razor.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

O código a seguir apresenta o método HTTP POST Edit, que processa os valores do filme enviados:

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [Bind] é uma maneira de proteger contra o excesso de postagem. Você somente deve incluir as propriedades do atributo [Bind] que deseja alterar. Para obter mais informações, consulte Proteger o controlador contra o excesso de postagem. ViewModels fornecem uma abordagem alternativa para evitar over-posting.

Observe se o segundo método de ação Edit é precedido pelo atributo [HttpPost].

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo HttpPost especifica que esse método Edit pode ser invocado somente para solicitações POST. Você pode aplicar o atributo [HttpGet] ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O atributo ValidateAntiForgeryToken é usado para prevenir a falsificação de uma solicitação e é associado a um token antifalsificação gerado no arquivo de exibição de edição (Views/Movies/Edit.cshtml). O arquivo de exibição de edição gera o token antifalsificação com o Form Tag Helper.

<form asp-action="Edit">

O Auxiliar de Marcação de Formulário gera um token antifalsificação oculto que deve corresponder ao token antifalsificação gerado [ValidateAntiForgeryToken] no método Edit do controlador Movies. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.

O método HttpGet Edit usa o parâmetro ID de filme, pesquisa o filme usando o método FindAsync do Entity Framework e retorna o filme selecionado para a exibição de Edição. Se um filme não for encontrado, NotFound (HTTP 404) será retornado.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Quando o sistema de scaffolding criou a exibição Edit, ele examinou a classe Movie e criou código para renderizar os elementos <label> e <input> para 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

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

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

O código com scaffolding usa vários métodos de Auxiliares de Marcação para simplificar a marcação HTML. O Tag Helper Label exibe o nome do campo (“Title”, “ReleaseDate”, “Genre” ou “Price”). O Auxiliar de Marcação de Entrada renderiza um elemento <input> HTML. O Tag Helper de Validação exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue para a URL /Movies. Clique em um link Editar. No navegador, exiba o código-fonte da página. O HTML gerado para o elemento <form> é mostrado abaixo.

<form action="/Movies/Edit/7" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div class="text-danger" />
        <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre" />
            <div class="col-md-10">
                <input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-2" for="Price" />
            <div class="col-md-10">
                <input class="form-control" type="text" 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" value="3.99" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
            </div>
        </div>
        <!-- Markup 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>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>

Os elementos <input> estão em um elemento HTML <form> cujo atributo action está definido para ser postado para a URL /Movies/Edit/id. Os dados de formulário serão postados para o servidor quando o botão Save receber um clique. A última linha antes do elemento </form> de fechamento mostra o token XSRF oculto gerado pelo Form Tag Helper.

Processando a requisição POST

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

// POST: Movies/Edit/5
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("Id,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
    if (id != movie.Id)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.Id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [ValidateAntiForgeryToken] valida o token XSRF oculto gerado pelo gerador de token antifalsificação no Tag Helper de Formulário

O sistema de model binding usa os valores de formulário postados e cria um objeto Movie que é passado como o parâmetro movie. A propriedade ModelState.IsValid verifica se os dados enviados no formulário podem ser usados para modificar (editar ou atualizar) um objeto Movie. Se os dados forem válidos, eles serão salvos. Os dados de filmes atualizados (editados) são salvos no banco de dados chamando o método SaveChangesAsync do contexto de banco de dados. 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.

Antes que o formulário seja postado no servidor, a validação do lado do cliente verifica as regras de validação nos campos. Se houver erros de validação, será exibida uma mensagem de erro e o formulário não será postado. Se o JavaScript estiver desabilitado, você não terá a validação do lado do cliente, mas o servidor detectará os valores postados que não forem válidos e os valores de formulário serão exibidos novamente com mensagens de erro. Mais adiante no tutorial, examinamos a Validação de Modelos mais detalhadamente. O Tag Helper de Validação no modelo de exibição Views/Movies/Edit.cshtml é responsável por exibir as mensagens de erro apropriadas.

Exibição de edição: uma exceção para o valor de Preço incorreto abc informa que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento dos estados xyz; insira uma data válida.

Todos os métodos HttpGet no controlador de filme seguem um padrão semelhante. Eles obtêm um objeto de filme (ou uma lista de objetos, no caso de Index) e passam o objeto (modelo) para a view. O método Create passa um objeto de filme vazio para a 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. A modificação de dados em um método HTTP GET é um risco de segurança. A modificação de dados em um método HTTP GET também viola as melhores práticas 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.

Temos um bom começo para o aplicativo de filme, mas a apresentação não é ideal. Por exemplo, ReleaseDate deveria ser separado em duas palavras.

Exibição de índice: DataDeLançamento é uma palavra (sem espaço) e a data de lançamento de cada filme mostra o horário 12h

Abra o arquivo Models/Movie.cs e adicione as linhas realçadas mostradas abaixo:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

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

        [Display(Name = "Release Date")]
        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }

        [Column(TypeName = "decimal(18, 2)")]
        public decimal Price { 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 de dados (Data) e, portanto, as informações de hora armazenadas no campo não são exibidas.

A anotação de dados [Column(TypeName = "decimal(18, 2)")] é necessária para que o Entity Framework Core possa mapear corretamente o Price para a moeda no banco de dados. Para obter mais informações, veja Tipos de Dados.

Procure o controlador Movies e mantenha o ponteiro do mouse pressionado sobre um link Editar para ver a URL de destino.

São mostrados uma janela do navegador com o mouse sobre o link de edição e um link da URL https://localhost:5001/Movies/Edit/5

Os links Editar, Detalhes e Excluir são gerados pelo Auxiliar de Marcação de Âncora do MVC Core no arquivo Views/Movies/Index.cshtml.

        <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
        <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
        <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
    </td>
</tr>

Os Tag Helpers permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos Razor. No código acima, o AnchorTagHelper gera dinamicamente o valor do atributo href HTML com base na ID de rota e no método de ação do controlador. Use a opção Exibir Código-fonte em seu navegador favorito ou as ferramentas do desenvolvedor para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:

 <td>
    <a href="/Movies/Edit/4"> Edit </a> |
    <a href="/Movies/Details/4"> Details </a> |
    <a href="/Movies/Delete/4"> Delete </a>
</td>

Lembre-se do formato do roteamento definido no Startup.cs arquivo:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

O ASP.NET Core converte https://localhost:5001/Movies/Edit/4 de uma solicitação no método de ação Edit do controlador Movies com o parâmetro Id igual a 4. (Os métodos do controlador também são conhecidos como métodos de ação.)

Para obter mais informações sobre Tag Helpers, consulte o conteúdo relacionado.

Abra o controlador Movies e examine os dois métodos de ação Edit. O código a seguir mostra o método HTTP GET Edit, que busca o filme e popula o formulário de edição gerado pelo arquivo Edit.cshtmlRazor do Razor.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

O código a seguir mostra o método HTTP POST Edit, que processa os valores de filmes postados:

// POST: Movies/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for 
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (id != movie.ID)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction("Index");
    }
    return View(movie);
}

O atributo [Bind] é uma maneira de proteger contra o excesso de postagem. Você somente deve incluir as propriedades do atributo [Bind] que deseja alterar. Para obter mais informações, consulte Proteger o controlador contra o excesso de postagem. ViewModels fornece uma abordagem alternativa para prevenir o excesso de postagem.

Observe que o segundo método de ação Edit é precedido pelo atributo [HttpPost].

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (id != movie.ID)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo HttpPost especifica que esse método Edit pode ser invocado somente para solicitações POST. Você pode aplicar o atributo [HttpGet] ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O atributo ValidateAntiForgeryToken é usado para prevenir a falsificação de uma solicitação e é associado a um token antifalsificação gerado no arquivo de exibição de edição (Views/Movies/Edit.cshtml). O arquivo de exibição de edição gera o token antifalsificação com o Auxiliar de Marcação de Formulário.

<form asp-action="Edit">

O Auxiliar de Marcação de Formulário gera um token antifalsificação oculto que deve corresponder ao token antifalsificação gerado [ValidateAntiForgeryToken] no método Edit do controlador Movies. Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.

O método HttpGet Edit usa o parâmetro ID de filme, pesquisa o filme usando o método FindAsync do Entity Framework e retorna o filme selecionado para a exibição de Edição. Se um filme não for encontrado, NotFound (HTTP 404) será retornado.

// GET: Movies/Edit/5
public async Task<IActionResult> Edit(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie.FindAsync(id);
    if (movie == null)
    {
        return NotFound();
    }
    return View(movie);
}

Quando o sistema de scaffolding criou a exibição de Edição, ele examinou a classe Movie e criou código para renderizar os elementos <label> e <input> para 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

@{
    ViewData["Title"] = "Edit";
}

<h1>Edit</h1>

<h4>Movie</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ReleaseDate" class="control-label"></label>
                <input asp-for="ReleaseDate" class="form-control" />
                <span asp-validation-for="ReleaseDate" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Genre" class="control-label"></label>
                <input asp-for="Genre" class="form-control" />
                <span asp-validation-for="Genre" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Price" class="control-label"></label>
                <input asp-for="Price" class="form-control" />
                <span asp-validation-for="Price" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

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

O código com scaffolding usa vários métodos de Tag Helper para simplificar a marcação HTML. O Auxiliar de Marcação de Rótulo exibe o nome do campo (“Title”, “ReleaseDate”, “Genre” ou “Price”). O Auxiliar de Marcação de Entrada renderiza um elemento <input> HTML. O Tag Helper de Validação exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue para a /Movies URL. Clique em um link Editar. No navegador, veja o código-fonte da página. O HTML gerado para o elemento <form> é mostrado abaixo.

<form action="/Movies/Edit/7" method="post">
    <div class="form-horizontal">
        <h4>Movie</h4>
        <hr />
        <div class="text-danger" />
        <input type="hidden" data-val="true" data-val-required="The ID field is required." id="ID" name="ID" value="7" />
        <div class="form-group">
            <label class="control-label col-md-2" for="Genre" />
            <div class="col-md-10">
                <input class="form-control" type="text" id="Genre" name="Genre" value="Western" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
            </div>
        </div>
        <div class="form-group">
            <label class="control-label col-md-2" for="Price" />
            <div class="col-md-10">
                <input class="form-control" type="text" 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" value="3.99" />
                <span class="text-danger field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
            </div>
        </div>
        <!-- Markup 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>
    <input name="__RequestVerificationToken" type="hidden" value="CfDJ8Inyxgp63fRFqUePGvuI5jGZsloJu1L7X9le1gy7NCIlSduCRx9jDQClrV9pOTTmqUyXnJBXhmrjcUVDJyDUMm7-MF_9rK8aAZdRdlOri7FmKVkRe_2v5LIHGKFcTjPrWPYnc9AdSbomkiOSaTEg7RU" />
</form>

Os elementos <input> estão em um elemento HTML <form> cujo atributo action está configurado para enviar para a URL /Movies/Edit/id. Os dados de formulário serão postados com o servidor quando o botão Save receber um clique. A última linha antes do elemento </form> de fechamento mostra o token XSRF oculto gerado pelo Auxiliar de Marcação de Formulário.

Processando a solicitação POST

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

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
    if (id != movie.ID)
    {
        return NotFound();
    }

    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(movie);
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!MovieExists(movie.ID))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }
        return RedirectToAction(nameof(Index));
    }
    return View(movie);
}

O atributo [ValidateAntiForgeryToken] valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Auxiliar de Marcação de Formulário

O sistema de model binding usa os valores de formulário postados e cria um objeto Movie que é passado como o parâmetro movie. A propriedade ModelState.IsValid verifica se os dados enviados no formulário podem ser usados para modificar (editar ou atualizar) um objeto Movie. Se os dados forem válidos, eles serão salvos. Os dados de filmes atualizados (editados) são salvos no banco de dados chamando o método SaveChangesAsync do contexto de banco de dados. 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.

Antes que o formulário seja postado no servidor, a validação do lado do cliente verifica as regras de validação nos campos. Se houver erros de validação, será exibida uma mensagem de erro e o formulário não será postado. Se o JavaScript estiver desabilitado, você não terá a validação do lado do cliente, mas o servidor detectará os valores postados que não forem válidos e os valores de formulário serão exibidos novamente com mensagens de erro. Mais adiante no tutorial, examinamos a Validação de Modelos mais detalhadamente. O Tag Helper de Validação no modelo de exibição Views/Movies/Edit.cshtml é responsável por exibir as mensagens de erro apropriadas.

Exibição de edição: uma exceção para o valor de Preço incorreto abc informa que o campo Preço deve ser um número. Uma exceção para o valor incorreto de Data de Lançamento xyz informa: insira uma data válida.

Todos os métodos HttpGet no controlador de filme seguem um padrão semelhante. Eles obtêm um objeto de filme (ou uma lista de objetos, no caso de Index) e passam o objeto (modelo) para a view. O método Create passa um objeto de filme vazio para a 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. A modificação de dados em um método HTTP GET é um risco de segurança. A modificação de dados em um método HTTP GET também viola as melhores práticas de HTTP e o padrão REST de arquitetura, que especifica que as solicitações GET não devem alterar o estado do seu 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.