Partilhar via


Parte 6, métodos de controlo e vistas 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 .NET 10 deste artigo.

Advertência

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

Por Rick Anderson

Temos um bom início para a aplicação de filmes, mas a apresentação não é ideal, por exemplo, ReleaseDate deve ser duas palavras.

Visualização do índice: A data de estreia é uma palavra (sem espaço) e cada data de estreia de filmes indica as 12h

Abra o Models/Movie.cs ficheiro e adicione as linhas destacadas 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 explicadas no próximo tutorial. O atributo Display especifica o que mostrar para o nome de um campo (neste caso "Data de Lançamento" em vez de "Data de Lançamento"). O atributo DataType especifica o tipo de dados (Data), por isso a informação de hora armazenada no campo não é apresentada.

Usando as anotações de dados acima, execute a aplicação e atualize a /Movies página. Como a marcação de vista utiliza os métodos @Html.DisplayNameFor e @Html.DisplayFor para renderizar o nome e o valor da propriedade, a vista atualizada Index mostra todos os campos devidamente formatados. Por exemplo, Data de Lançamento agora é composta por duas palavras e a informação de tempo já não é exibida.

Visualização do índice:

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

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

Janela do navegador com rato sobre o link Editar e uma URL do link é mostrada https://localhost:5001/Movies/Edit/5

Os links de Editar, Detalhes e Eliminar são gerados pelo Core MVC Anchor Tag Helper no Views/Movies/Index.cshtml ficheiro.

        <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>

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor. No código acima, gera AnchorTagHelper dinamicamente o valor do atributo HTML href a partir do método de ação do controlador e do id de rota. Utiliza-se o Ver Fonte do seu navegador favorito ou as ferramentas de programador para analisar 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>

Recorde o formato de roteamento definido no Program.cs ficheiro:

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

ASP.NET Core traduz-se https://localhost:5001/Movies/Edit/4 num pedido ao Edit método de ação do Movies controlador com o parâmetro Id 4. (Os métodos controladores também são conhecidos como métodos de ação.)

Os Tag Helpers são uma das funcionalidades novas mais populares no ASP.NET Core. Para mais informações, consulte Recursos adicionais.

Abra o Movies controlador e examine os dois Edit métodos de ação. O código seguinte mostra o HTTP GET Edit método, que recupera o filme e preenche o formulário de edição gerado pelo Edit.cshtmlRazor ficheiro.

// 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 seguinte mostra o HTTP POST Edit método que processa os valores dos filmes publicados:

// 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 forma de proteger contra publicações excessivas. Deve incluir apenas propriedades no [Bind] atributo que pretende alterar. Para mais informações, veja Proteger o seu comando contra excesso de publicação. Os ViewModels oferecem uma abordagem alternativa para evitar publicações excessivas.

Note que o método da segunda Edit ação é precedido pelo [HttpPost] atributo.

// 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 HttpPost atributo especifica que este Edit método pode ser invocado para POST pedidos. Podes aplicar o [HttpGet] atributo ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O ValidateAntiForgeryToken atributo é usado para evitar a falsificação de um pedido e é emparelhado com um token antifalsificação gerado no ficheiro de visualização de edição (Views/Movies/Edit.cshtml). O ficheiro de vista de edição gera o token antifalsificação com o Form Tag Helper.

<form asp-action="Edit">

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

O HttpGet Edit método pega no parâmetro do filme ID , pesquisa o filme usando o método Entity Framework FindAsync e devolve o filme selecionado à vista de Editar. Se um filme não for encontrado, NotFound (HTTP 404) é devolvido.

// 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 andaimes criava a vista Edit, examinava a Movie classe e criava código para renderizar <label> e <input> elementos para cada propriedade da classe. O exemplo seguinte mostra a vista de Editar que foi gerada pelo sistema de andaimes 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");}
}

Repare como o modelo de vista tem uma @model MvcMovie.Models.Movie instrução no topo do ficheiro. @model MvcMovie.Models.Movie especifica que a vista espera que o modelo para o modelo da visualização seja do tipo Movie.

O código estruturado utiliza vários métodos Tag Helper para simplificar a marcação HTML. O Tag Helper de Etiquetas mostra o nome do campo ("Título", "DataDeLançamento", "Género" ou "Preço"). O Input Tag Helper renderiza um elemento HTML <input> . O Validation Tag Helper apresenta quaisquer mensagens de validação associadas a essa propriedade.

Executa a aplicação e navega até ao /Movies URL. Clique num link de Editar . No navegador, veja a fonte da página. O HTML gerado para o <form> elemento é 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 <input> elementos estão num HTML <form> elemento cujo action atributo está definido para ser publicado na /Movies/Edit/id URL. Os dados do formulário serão publicados no servidor quando o Save botão for clicado. A última linha antes do elemento de encerramento </form> mostra o token XSRF oculto gerado pelo Form Tag Helper.

Processamento do Pedido POST

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

// 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 [ValidateAntiForgeryToken] atributo valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Form Tag Helper

O sistema de binding de modelos pega nos valores do formulário publicados e cria um Movie objeto que é passado como parâmetro movie . A ModelState.IsValid propriedade verifica se os dados submetidos no formulário podem ser usados para modificar (editar ou atualizar) um Movie objeto. Se os dados forem válidos, são guardados. Os dados atualizados (editados) do filme são guardados na base de dados chamando o SaveChangesAsync método de contexto da base de dados. Após guardar os dados, o código redireciona o utilizador para o Index método de ação da MoviesController classe, que mostra a coleção de filmes, incluindo as alterações recentemente feitas.

Antes de o formulário ser publicado no servidor, a validação do lado do cliente verifica quaisquer regras de validação nos campos. Se houver algum erro de validação, é exibida uma mensagem de erro e o formulário não é publicado. Se o JavaScript estiver desativado, não terá validação do lado do cliente, mas o servidor irá detetar os valores publicados que não são válidos, e os valores do formulário serão reapresentados com mensagens de erro. Mais adiante no tutorial, analisamos a Validação de Modelos com mais detalhe. O Assistente de Etiqueta de Validação no Views/Movies/Edit.cshtml modelo de visualização trata de mostrar as mensagens de erro apropriadas.

Edit: Uma exceção para um valor incorreto de preço de abc indica que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento de xyz indica Por favor, introduza uma data válida.

Todos os HttpGet métodos no comando de filmes seguem um padrão semelhante. Eles recebem um objeto de filme (ou lista de objetos, no caso de Index), e passam o objeto (modelo) para a vista. O Create método passa um objeto de filme vazio para a Create vista. Todos os métodos que criam, editam, eliminam ou de outra forma modificam dados fazem-no na [HttpPost] sobrecarga do método. Modificar dados num HTTP GET método é um risco de segurança. Modificar dados num HTTP GET método também viola as melhores práticas HTTP e o padrão arquitetónico REST , que especifica que os pedidos GET não devem alterar o estado da sua aplicação. Ou seja, realizar uma operação GET deve ser segura, sem efeitos secundários e sem modificar os seus dados persistentes.

Recursos adicionais

Temos um bom início para a aplicação de filmes, mas a apresentação não é ideal, por exemplo, ReleaseDate deve ser duas palavras.

Visualização do índice: A data de estreia é uma palavra (sem espaço) e cada data de estreia de filmes indica as 12h

Abra o Models/Movie.cs ficheiro e adicione as linhas destacadas 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 explicadas no próximo tutorial. O atributo Display especifica o que mostrar para o nome de um campo (neste caso "Data de Lançamento" em vez de "Data de Lançamento"). O atributo DataType especifica o tipo de dados (Data), por isso a informação de hora armazenada no campo não é apresentada.

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

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

Janela do navegador com rato sobre o link Editar e uma URL do link é mostrada https://localhost:5001/Movies/Edit/5

Os links de Editar, Detalhes e Eliminar são gerados pelo Core MVC Anchor Tag Helper no Views/Movies/Index.cshtml ficheiro.

        <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>

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor. No código acima, gera AnchorTagHelper dinamicamente o valor do atributo HTML href a partir do método de ação do controlador e do id de rota. Utiliza-se o Ver Fonte do seu navegador favorito ou as ferramentas de programador para analisar 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>

Recorde o formato de roteamento definido no Program.cs ficheiro:

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

ASP.NET Core traduz-se https://localhost:5001/Movies/Edit/4 num pedido ao Edit método de ação do Movies controlador com o parâmetro Id 4. (Os métodos controladores também são conhecidos como métodos de ação.)

Os Tag Helpers são uma das funcionalidades novas mais populares no ASP.NET Core. Para mais informações, consulte Recursos adicionais.

Abra o Movies controlador e examine os dois Edit métodos de ação. O código seguinte mostra o HTTP GET Edit método, que recupera o filme e preenche o formulário de edição gerado pelo Edit.cshtmlRazor ficheiro.

// 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 seguinte mostra o HTTP POST Edit método que processa os valores dos filmes publicados:

// 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 forma de proteger contra publicações excessivas. Deve incluir apenas propriedades no [Bind] atributo que pretende alterar. Para mais informações, veja Proteger o seu comando contra excesso de publicação. Os ViewModels oferecem uma abordagem alternativa para evitar publicações excessivas.

Note que o método da segunda Edit ação é precedido pelo [HttpPost] atributo.

// 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 HttpPost atributo especifica que este Edit método pode ser invocado para POST pedidos. Podes aplicar o [HttpGet] atributo ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O ValidateAntiForgeryToken atributo é usado para evitar a falsificação de um pedido e é emparelhado com um token antifalsificação gerado no ficheiro de visualização de edição (Views/Movies/Edit.cshtml). O ficheiro de vista de edição gera o token antifalsificação com o Form Tag Helper.

<form asp-action="Edit">

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

O HttpGet Edit método pega no parâmetro do filme ID , pesquisa o filme usando o método Entity Framework FindAsync e devolve o filme selecionado à vista de Editar. Se um filme não for encontrado, NotFound (HTTP 404) é devolvido.

// 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 andaimes criava a vista Edit, examinava a Movie classe e criava código para renderizar <label> e <input> elementos para cada propriedade da classe. O exemplo seguinte mostra a vista de Editar que foi gerada pelo sistema de andaimes 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");}
}

Repare como o modelo de vista tem uma @model MvcMovie.Models.Movie instrução no topo do ficheiro. @model MvcMovie.Models.Movie especifica que a vista espera que o modelo para o modelo da visualização seja do tipo Movie.

O código estruturado utiliza vários métodos Tag Helper para simplificar a marcação HTML. O Tag Helper de Etiquetas mostra o nome do campo ("Título", "DataDeLançamento", "Género" ou "Preço"). O Input Tag Helper renderiza um elemento HTML <input> . O Validation Tag Helper apresenta quaisquer mensagens de validação associadas a essa propriedade.

Executa a aplicação e navega até ao /Movies URL. Clique num link de Editar . No navegador, veja a fonte da página. O HTML gerado para o <form> elemento é 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 <input> elementos estão num HTML <form> elemento cujo action atributo está definido para ser publicado na /Movies/Edit/id URL. Os dados do formulário serão publicados no servidor quando o Save botão for clicado. A última linha antes do elemento de encerramento </form> mostra o token XSRF oculto gerado pelo Form Tag Helper.

Processamento do Pedido POST

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

// 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 [ValidateAntiForgeryToken] atributo valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Form Tag Helper

O sistema de binding de modelos pega nos valores do formulário publicados e cria um Movie objeto que é passado como parâmetro movie . A ModelState.IsValid propriedade verifica se os dados submetidos no formulário podem ser usados para modificar (editar ou atualizar) um Movie objeto. Se os dados forem válidos, são guardados. Os dados atualizados (editados) do filme são guardados na base de dados chamando o SaveChangesAsync método de contexto da base de dados. Após guardar os dados, o código redireciona o utilizador para o Index método de ação da MoviesController classe, que mostra a coleção de filmes, incluindo as alterações recentemente feitas.

Antes de o formulário ser publicado no servidor, a validação do lado do cliente verifica quaisquer regras de validação nos campos. Se houver algum erro de validação, é exibida uma mensagem de erro e o formulário não é publicado. Se o JavaScript estiver desativado, não terá validação do lado do cliente, mas o servidor irá detetar os valores publicados que não são válidos, e os valores do formulário serão reapresentados com mensagens de erro. Mais adiante no tutorial, analisamos a Validação de Modelos com mais detalhe. O Assistente de Etiqueta de Validação no Views/Movies/Edit.cshtml modelo de visualização trata de mostrar as mensagens de erro apropriadas.

Edit: Uma exceção para um valor incorreto de preço de abc indica que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento de xyz indica Por favor, introduza uma data válida.

Todos os HttpGet métodos no comando de filmes seguem um padrão semelhante. Eles recebem um objeto de filme (ou lista de objetos, no caso de Index), e passam o objeto (modelo) para a vista. O Create método passa um objeto de filme vazio para a Create vista. Todos os métodos que criam, editam, eliminam ou de outra forma modificam dados fazem-no na [HttpPost] sobrecarga do método. Modificar dados num HTTP GET método é um risco de segurança. Modificar dados num HTTP GET método também viola as melhores práticas HTTP e o padrão arquitetónico REST , que especifica que os pedidos GET não devem alterar o estado da sua aplicação. Ou seja, realizar uma operação GET deve ser segura, sem efeitos secundários e sem modificar os seus dados persistentes.

Recursos adicionais

Temos um bom início para a aplicação de filmes, mas a apresentação não é ideal, por exemplo, ReleaseDate deve ser duas palavras.

Visualização do índice: A data de estreia é uma palavra (sem espaço) e cada data de estreia de filmes indica as 12h

Abra o Models/Movie.cs ficheiro e adicione as linhas destacadas 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 explicadas no próximo tutorial. O atributo Display especifica o que mostrar para o nome de um campo (neste caso "Data de Lançamento" em vez de "Data de Lançamento"). O atributo DataType especifica o tipo de dados (Data), por isso a informação de hora armazenada no campo não é apresentada.

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

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

Janela do navegador com rato sobre o link Editar e uma URL do link é mostrada https://localhost:5001/Movies/Edit/5

Os links de Editar, Detalhes e Eliminar são gerados pelo Core MVC Anchor Tag Helper no Views/Movies/Index.cshtml ficheiro.

        <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>

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor. No código acima, gera AnchorTagHelper dinamicamente o valor do atributo HTML href a partir do método de ação do controlador e do id de rota. Utiliza-se o Ver Fonte do seu navegador favorito ou as ferramentas de programador para analisar 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>

Recorde o formato de roteamento definido no Program.cs ficheiro:

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

ASP.NET Core traduz-se https://localhost:5001/Movies/Edit/4 num pedido ao Edit método de ação do Movies controlador com o parâmetro Id 4. (Os métodos controladores também são conhecidos como métodos de ação.)

Os Tag Helpers são uma das funcionalidades novas mais populares no ASP.NET Core. Para mais informações, consulte Recursos adicionais.

Abra o Movies controlador e examine os dois Edit métodos de ação. O código seguinte mostra o HTTP GET Edit método, que recupera o filme e preenche o formulário de edição gerado pelo Edit.cshtmlRazor ficheiro.

// 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 seguinte mostra o HTTP POST Edit método que processa os valores dos filmes publicados:

// 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 forma de proteger contra publicações excessivas. Deve incluir apenas propriedades no [Bind] atributo que pretende alterar. Para mais informações, veja Proteger o seu comando contra excesso de publicação. Os ViewModels oferecem uma abordagem alternativa para evitar publicações excessivas.

Note que o método da segunda Edit ação é precedido pelo [HttpPost] atributo.

// 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 HttpPost atributo especifica que este Edit método pode ser invocado para POST pedidos. Podes aplicar o [HttpGet] atributo ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O ValidateAntiForgeryToken atributo é usado para evitar a falsificação de um pedido e é emparelhado com um token antifalsificação gerado no ficheiro de visualização de edição (Views/Movies/Edit.cshtml). O ficheiro de vista de edição gera o token antifalsificação com o Form Tag Helper.

<form asp-action="Edit">

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

O HttpGet Edit método pega no parâmetro do filme ID , pesquisa o filme usando o método Entity Framework FindAsync e devolve o filme selecionado à vista de Editar. Se um filme não for encontrado, NotFound (HTTP 404) é devolvido.

// 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 andaimes criava a vista Edit, examinava a Movie classe e criava código para renderizar <label> e <input> elementos para cada propriedade da classe. O exemplo seguinte mostra a vista de Editar que foi gerada pelo sistema de andaimes 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");}
}

Repare como o modelo de vista tem uma @model MvcMovie.Models.Movie instrução no topo do ficheiro. @model MvcMovie.Models.Movie especifica que a vista espera que o modelo para o modelo da visualização seja do tipo Movie.

O código estruturado utiliza vários métodos Tag Helper para simplificar a marcação HTML. O Tag Helper de Etiquetas mostra o nome do campo ("Título", "DataDeLançamento", "Género" ou "Preço"). O Input Tag Helper renderiza um elemento HTML <input> . O Validation Tag Helper apresenta quaisquer mensagens de validação associadas a essa propriedade.

Executa a aplicação e navega até ao /Movies URL. Clique num link de Editar . No navegador, veja a fonte da página. O HTML gerado para o <form> elemento é 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 <input> elementos estão num HTML <form> elemento cujo action atributo está definido para ser publicado na /Movies/Edit/id URL. Os dados do formulário serão publicados no servidor quando o Save botão for clicado. A última linha antes do elemento de encerramento </form> mostra o token XSRF oculto gerado pelo Form Tag Helper.

Processamento do Pedido POST

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

// 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 [ValidateAntiForgeryToken] atributo valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Form Tag Helper

O sistema de binding de modelos pega nos valores do formulário publicados e cria um Movie objeto que é passado como parâmetro movie . A ModelState.IsValid propriedade verifica se os dados submetidos no formulário podem ser usados para modificar (editar ou atualizar) um Movie objeto. Se os dados forem válidos, são guardados. Os dados atualizados (editados) do filme são guardados na base de dados chamando o SaveChangesAsync método de contexto da base de dados. Após guardar os dados, o código redireciona o utilizador para o Index método de ação da MoviesController classe, que mostra a coleção de filmes, incluindo as alterações recentemente feitas.

Antes de o formulário ser publicado no servidor, a validação do lado do cliente verifica quaisquer regras de validação nos campos. Se houver algum erro de validação, é exibida uma mensagem de erro e o formulário não é publicado. Se o JavaScript estiver desativado, não terá validação do lado do cliente, mas o servidor irá detetar os valores publicados que não são válidos, e os valores do formulário serão reapresentados com mensagens de erro. Mais adiante no tutorial, analisamos a Validação de Modelos com mais detalhe. O Assistente de Etiqueta de Validação no Views/Movies/Edit.cshtml modelo de visualização trata de mostrar as mensagens de erro apropriadas.

Edit: Uma exceção para um valor incorreto de preço de abc indica que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento de xyz indica Por favor, introduza uma data válida.

Todos os HttpGet métodos no comando de filmes seguem um padrão semelhante. Eles recebem um objeto de filme (ou lista de objetos, no caso de Index), e passam o objeto (modelo) para a vista. O Create método passa um objeto de filme vazio para a Create vista. Todos os métodos que criam, editam, eliminam ou de outra forma modificam dados fazem-no na [HttpPost] sobrecarga do método. Modificar dados num HTTP GET método é um risco de segurança. Modificar dados num HTTP GET método também viola as melhores práticas HTTP e o padrão arquitetónico REST , que especifica que os pedidos GET não devem alterar o estado da sua aplicação. Ou seja, realizar uma operação GET deve ser segura, sem efeitos secundários e sem modificar os seus dados persistentes.

Recursos adicionais

Temos um bom início para a aplicação de filmes, mas a apresentação não é ideal, por exemplo, ReleaseDate deve ser duas palavras.

Visualização do índice: A data de estreia é uma palavra (sem espaço) e cada data de estreia de filmes indica as 12h

Abra o Models/Movie.cs ficheiro e adicione as linhas destacadas 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 explicadas no próximo tutorial. O atributo Display especifica o que mostrar para o nome de um campo (neste caso "Data de Lançamento" em vez de "Data de Lançamento"). O atributo DataType especifica o tipo de dados (Data), por isso a informação de hora armazenada no campo não é apresentada.

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

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

Janela do navegador com rato sobre o link Editar e uma URL do link é mostrada https://localhost:5001/Movies/Edit/5

Os links de Editar, Detalhes e Eliminar são gerados pelo Core MVC Anchor Tag Helper no Views/Movies/Index.cshtml ficheiro.

        <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>

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor. No código acima, gera AnchorTagHelper dinamicamente o valor do atributo HTML href a partir do método de ação do controlador e do id de rota. Utiliza-se o Ver Fonte do seu navegador favorito ou as ferramentas de programador para analisar 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>

Recorde o formato de roteamento definido no Program.cs ficheiro:

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

ASP.NET Core traduz-se https://localhost:5001/Movies/Edit/4 num pedido ao Edit método de ação do Movies controlador com o parâmetro Id 4. (Os métodos controladores também são conhecidos como métodos de ação.)

Os Ajudantes de Etiquetas são uma funcionalidade popular no ASP.NET Core. Para mais informações sobre eles, consulte Recursos adicionais.

Abra o Movies controlador e examine os dois Edit métodos de ação. O código seguinte mostra o HTTP GET Edit método, que recupera o filme e preenche o formulário de edição gerado pelo Edit.cshtmlRazor ficheiro.

// 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 seguinte mostra o HTTP POST Edit método que processa os valores dos filmes publicados:

// 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 forma de proteger contra publicações excessivas. Deve incluir apenas propriedades no [Bind] atributo que pretende alterar. Para mais informações, veja Proteger o seu comando contra excesso de publicação. Os ViewModels oferecem uma abordagem alternativa para evitar publicações excessivas.

Note que o método da segunda Edit ação é precedido pelo [HttpPost] atributo.

// 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 HttpPost atributo especifica que este Edit método pode ser invocado para POST pedidos. Podes aplicar o [HttpGet] atributo ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O ValidateAntiForgeryToken atributo é usado para evitar a falsificação de um pedido e é emparelhado com um token antifalsificação gerado no ficheiro de visualização de edição (Views/Movies/Edit.cshtml). O ficheiro de vista de edição gera o token antifalsificação com o Form Tag Helper.

<form asp-action="Edit">

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

O HttpGet Edit método pega no parâmetro do filme ID , pesquisa o filme usando o método Entity Framework FindAsync e devolve o filme selecionado à vista de Editar. Se um filme não for encontrado, NotFound (HTTP 404) é devolvido.

// 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 andaimes criava a vista Edit, examinava a Movie classe e criava código para renderizar <label> e <input> elementos para cada propriedade da classe. O exemplo seguinte mostra a vista de Editar que foi gerada pelo sistema de andaimes 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");}
}

Repare como o modelo de vista tem uma @model MvcMovie.Models.Movie instrução no topo do ficheiro. @model MvcMovie.Models.Movie especifica que a vista espera que o modelo para o modelo da visualização seja do tipo Movie.

O código estruturado utiliza vários métodos Tag Helper para simplificar a marcação HTML. O Tag Helper de Etiquetas mostra o nome do campo ("Título", "DataDeLançamento", "Género" ou "Preço"). O Input Tag Helper renderiza um elemento HTML <input> . O Validation Tag Helper apresenta quaisquer mensagens de validação associadas a essa propriedade.

Executa a aplicação e navega até ao /Movies URL. Clique num link de Editar . No navegador, veja a fonte da página. O HTML gerado para o <form> elemento é 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 <input> elementos estão num HTML <form> elemento cujo action atributo está definido para ser publicado na /Movies/Edit/id URL. Os dados do formulário serão publicados no servidor quando o Save botão for clicado. A última linha antes do elemento de encerramento </form> mostra o token XSRF oculto gerado pelo Form Tag Helper.

Processamento do Pedido POST

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

// 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 [ValidateAntiForgeryToken] atributo valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Form Tag Helper

O sistema de binding de modelos pega nos valores do formulário publicados e cria um Movie objeto que é passado como parâmetro movie . A ModelState.IsValid propriedade verifica se os dados submetidos no formulário podem ser usados para modificar (editar ou atualizar) um Movie objeto. Se os dados forem válidos, são guardados. Os dados atualizados (editados) do filme são guardados na base de dados chamando o SaveChangesAsync método de contexto da base de dados. Após guardar os dados, o código redireciona o utilizador para o Index método de ação da MoviesController classe, que mostra a coleção de filmes, incluindo as alterações recentemente feitas.

Antes de o formulário ser publicado no servidor, a validação do lado do cliente verifica quaisquer regras de validação nos campos. Se houver algum erro de validação, é exibida uma mensagem de erro e o formulário não é publicado. Se o JavaScript estiver desativado, não terá validação do lado do cliente, mas o servidor irá detetar os valores publicados que não são válidos, e os valores do formulário serão reapresentados com mensagens de erro. Mais adiante no tutorial, analisamos a Validação de Modelos com mais detalhe. O Assistente de Etiqueta de Validação no Views/Movies/Edit.cshtml modelo de visualização trata de mostrar as mensagens de erro apropriadas.

Edit: Uma exceção para um valor incorreto de preço de abc indica que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento de xyz indica Por favor, introduza uma data válida.

Todos os HttpGet métodos no comando de filmes seguem um padrão semelhante. Eles recebem um objeto de filme (ou lista de objetos, no caso de Index), e passam o objeto (modelo) para a vista. O Create método passa um objeto de filme vazio para a Create vista. Todos os métodos que criam, editam, eliminam ou de outra forma modificam dados fazem-no na [HttpPost] sobrecarga do método. Modificar dados num HTTP GET método é um risco de segurança. Modificar dados num HTTP GET método também viola as melhores práticas HTTP e o padrão arquitetónico REST , que especifica que os pedidos GET não devem alterar o estado da sua aplicação. Ou seja, realizar uma operação GET deve ser segura, sem efeitos secundários e sem modificar os seus dados persistentes.

Recursos adicionais

Temos um bom início para a aplicação de filmes, mas a apresentação não é ideal, por exemplo, ReleaseDate deve ser duas palavras.

Visualização do índice: A data de estreia é uma palavra (sem espaço) e cada data de estreia de filmes indica as 12h

Abra o Models/Movie.cs ficheiro e adicione as linhas destacadas 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; }
    }
}

Abordamos DataAnnotations no próximo tutorial. O atributo Display especifica o que mostrar para o nome de um campo (neste caso "Data de Lançamento" em vez de "Data de Lançamento"). O atributo DataType especifica o tipo de dados (Data), por isso a informação de hora armazenada no campo não é apresentada.

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

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

Janela do navegador com rato sobre o link Editar e uma URL do link é mostrada https://localhost:5001/Movies/Edit/5

Os links de Editar, Detalhes e Eliminar são gerados pelo Core MVC Anchor Tag Helper no Views/Movies/Index.cshtml ficheiro.

        <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>

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor. No código acima, gera AnchorTagHelper dinamicamente o valor do atributo HTML href a partir do método de ação do controlador e do id de rota. Utiliza-se o Ver Fonte do seu navegador favorito ou as ferramentas de programador para analisar 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>

Recorde o formato de roteamento definido no Startup.cs ficheiro:

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

ASP.NET Core traduz-se https://localhost:5001/Movies/Edit/4 num pedido ao Edit método de ação do Movies controlador com o parâmetro Id 4. (Os métodos controladores também são conhecidos como métodos de ação.)

Para mais informações sobre os Ajudantes de Etiquetas, consulte Recursos adicionais.

Abra o Movies controlador e examine os dois Edit métodos de ação. O código seguinte mostra o HTTP GET Edit método, que recupera o filme e preenche o formulário de edição gerado pelo Edit.cshtmlRazor ficheiro.

// 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 seguinte mostra o HTTP POST Edit método que processa os valores dos filmes publicados:

// 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 forma de proteger contra publicações excessivas. Deve incluir apenas propriedades no [Bind] atributo que pretende alterar. Para mais informações, veja Proteger o seu comando contra excesso de publicação. Os ViewModels oferecem uma abordagem alternativa para evitar publicações excessivas.

Note que o método da segunda Edit ação é precedido pelo [HttpPost] atributo.

[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 HttpPost atributo especifica que este Edit método pode ser invocado para POST pedidos. Podes aplicar o [HttpGet] atributo ao primeiro método de edição, mas isso não é necessário porque [HttpGet] é o padrão.

O ValidateAntiForgeryToken atributo é usado para evitar a falsificação de um pedido e é emparelhado com um token antifalsificação gerado no ficheiro de visualização de edição (Views/Movies/Edit.cshtml). O ficheiro de vista de edição gera o token antifalsificação com o Form Tag Helper.

<form asp-action="Edit">

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

O HttpGet Edit método pega no parâmetro do filme ID , pesquisa o filme usando o método Entity Framework FindAsync e devolve o filme selecionado à vista de Editar. Se um filme não for encontrado, NotFound (HTTP 404) é devolvido.

// 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 andaimes criava a vista Edit, examinava a Movie classe e criava código para renderizar <label> e <input> elementos para cada propriedade da classe. O exemplo seguinte mostra a vista de Editar que foi gerada pelo sistema de andaimes 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");}
}

Repare como o modelo de vista tem uma @model MvcMovie.Models.Movie instrução no topo do ficheiro. @model MvcMovie.Models.Movie especifica que a vista espera que o modelo para o modelo da visualização seja do tipo Movie.

O código estruturado utiliza vários métodos Tag Helper para simplificar a marcação HTML. O Tag Helper de Etiquetas mostra o nome do campo ("Título", "DataDeLançamento", "Género" ou "Preço"). O Input Tag Helper renderiza um elemento HTML <input> . O Validation Tag Helper apresenta quaisquer mensagens de validação associadas a essa propriedade.

Executa a aplicação e navega até ao /Movies URL. Clique num link de Editar . No navegador, veja a fonte da página. O HTML gerado para o <form> elemento é 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 <input> elementos estão num HTML <form> elemento cujo action atributo está definido para ser publicado na /Movies/Edit/id URL. Os dados do formulário serão publicados no servidor quando o Save botão for clicado. A última linha antes do elemento de encerramento </form> mostra o token XSRF oculto gerado pelo Form Tag Helper.

Processamento do Pedido POST

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

[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 [ValidateAntiForgeryToken] atributo valida o token XSRF oculto gerado pelo gerador de tokens antifalsificação no Form Tag Helper

O sistema de binding de modelos pega nos valores do formulário publicados e cria um Movie objeto que é passado como parâmetro movie . A ModelState.IsValid propriedade verifica se os dados submetidos no formulário podem ser usados para modificar (editar ou atualizar) um Movie objeto. Se os dados forem válidos, são guardados. Os dados atualizados (editados) do filme são guardados na base de dados chamando o SaveChangesAsync método de contexto da base de dados. Após guardar os dados, o código redireciona o utilizador para o Index método de ação da MoviesController classe, que mostra a coleção de filmes, incluindo as alterações recentemente feitas.

Antes de o formulário ser publicado no servidor, a validação do lado do cliente verifica quaisquer regras de validação nos campos. Se houver algum erro de validação, é exibida uma mensagem de erro e o formulário não é publicado. Se o JavaScript estiver desativado, não terá validação do lado do cliente, mas o servidor irá detetar os valores publicados que não são válidos, e os valores do formulário serão reapresentados com mensagens de erro. Mais adiante no tutorial, analisamos a Validação de Modelos com mais detalhe. O Assistente de Etiqueta de Validação no Views/Movies/Edit.cshtml modelo de visualização trata de mostrar as mensagens de erro apropriadas.

Edit: Uma exceção para um valor incorreto de preço de abc indica que o campo Preço deve ser um número. Uma exceção para um valor incorreto de Data de Lançamento de xyz indica Por favor, introduza uma data válida.

Todos os HttpGet métodos no comando de filmes seguem um padrão semelhante. Eles recebem um objeto de filme (ou lista de objetos, no caso de Index), e passam o objeto (modelo) para a vista. O Create método passa um objeto de filme vazio para a Create vista. Todos os métodos que criam, editam, eliminam ou de outra forma modificam dados fazem-no na [HttpPost] sobrecarga do método. Modificar dados num HTTP GET método é um risco de segurança. Modificar dados num HTTP GET método também viola as melhores práticas HTTP e o padrão arquitetónico REST , que especifica que os pedidos GET não devem alterar o estado da sua aplicação. Ou seja, realizar uma operação GET deve ser segura, sem efeitos secundários e sem modificar os seus dados persistentes.

Recursos adicionais