Examinar los métodos y la vista Edit

por Rick Anderson

Note

Hay disponible una versión actualizada de este tutorial aquí con la versión más reciente de Visual Studio. El nuevo tutorial usa ASP.NET Core MVC,que proporciona muchas mejoras en este tutorial.

En este tutorial se muestra ASP.NET Core MVC con controladores y vistas. Razor Pages es una nueva alternativa en ASP.NET Core, un modelo de programación basado en páginas que hace que la creación de la interfaz de usuario web sea más fácil y productiva. Se recomienda probar el tutorial de las páginas de Razor antes que la versión MVC. El tutorial de las páginas de Razor:

  • Es más fácil de seguir.
  • Abarca más características.
  • Es el enfoque preferido para el desarrollo de nuevas aplicaciones.

En esta sección, examinará las Edit vistas y los métodos de acción generados para el controlador de película. Pero primero vamos a tomar una breve versión para que la fecha de lanzamiento tenga un aspecto mejor. Abra el archivo Models\Movie.CS y agregue las líneas resaltadas que se muestran a continuación:

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

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

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

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

También puede hacer que la referencia cultural de fecha sea específica de la siguiente manera:

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

En el próximo tutorial hablaremos de DataAnnotations. El atributo Display especifica qué se muestra como nombre de un campo (en este caso, "Release Date" en lugar de "ReleaseDate"). El atributo DataType especifica el tipo de los datos, en este caso es una fecha, por lo que no se muestra la información de hora almacenada en el campo. El atributo DisplayFormat es necesario para un error en el explorador Chrome que presenta los formatos de fecha incorrectamente.

Ejecute la aplicación y vaya al Movies controlador. Mantenga el puntero del mouse sobre un vínculo de edición para ver la dirección URL a la que está vinculado.

EditLink_sm

El Edit método genera el vínculo de edición Html.ActionLink en la vista Views\Movies\Index.cshtml :

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

HTML. ActionLink

El Html objeto es una aplicación auxiliar que se expone mediante una propiedad en la clase base System. Web. Mvc. WebViewPage . El ActionLink método del ayudante facilita la generación dinámica de hipervínculos HTML que se vinculan a los métodos de acción en los controladores. El primer argumento del ActionLink método es el texto del vínculo que se va a representar (por ejemplo, <a>Edit Me</a> ). El segundo argumento es el nombre del método de acción que se va a invocar (en este caso, la Edit acción). El último argumento es un objeto anónimo que genera los datos de la ruta (en este caso, el identificador 4).

El vínculo generado que se muestra en la imagen anterior es http://localhost:1234/Movies/Edit/4 . La ruta predeterminada (establecida en App _ Start\RouteConfig.CS) toma el patrón de la dirección URL {controller}/{action}/{id} . Por lo tanto, ASP.NET http://localhost:1234/Movies/Edit/4 se traduce en una solicitud al Edit método de acción del Movies controlador con el parámetro ID igual a 4. Examine el siguiente código del archivo * _ Start\RouteConfig.CS* de la aplicación. El método MapRoute se usa para enrutar las solicitudes HTTP al controlador y el método de acción correctos y proporcionar el parámetro de identificador opcional. El método MapRoute también lo usa el HtmlHelpers como ActionLink para generar direcciones URL según el controlador, el método de acción y los datos de ruta.

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

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

También puede pasar parámetros de método de acción mediante una cadena de consulta. Por ejemplo, la dirección URL http://localhost:1234/Movies/Edit?ID=3 también pasa el parámetro ID de 3 al Edit método de acción del Movies controlador.

EditQueryString

Abra el Movies controlador. EditA continuación se muestran los dos métodos de acción.

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

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

Observe que el segundo método de acción Edit va precedido del atributo HttpPost. Este atributo especifica que la sobrecarga del Edit método solo se puede invocar para las solicitudes post. Podría aplicar el HttpGet atributo al primer método de edición, pero no es necesario porque es el valor predeterminado. (Haremos referencia a los métodos de acción a los que se asignan implícitamente el HttpGet atributo como HttpGet métodos). El atributo de enlace es otro mecanismo de seguridad importante que evita que los hackers envíen datos por encima del modelo. Solo debe incluir propiedades en el atributo de enlace que desee cambiar. Puede leer acerca de la publicación en la publicación y el atributo de enlace en la nota de seguridad que se está sobreexpuesto. En el modelo simple que se usa en este tutorial, se enlazarán todos los datos del modelo. El atributo ValidateAntiForgeryToken se usa para impedir la falsificación de una solicitud y se empareja con @Html.AntiForgeryToken() en el archivo de vista de edición (Views\Movies\Edit.cshtml); a continuación, se muestra una parte:

@model MvcMovie.Models.Movie

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

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

@Html.AntiForgeryToken() genera un token antifalsificación de formulario oculto que debe coincidir en el Edit método del Movies controlador. Puede leer más sobre la falsificación de solicitudes entre sitios (también conocido como XSRF o CSRF) en mi tutorial prevención de XSRF/CSRF en MVC.

El HttpGet Edit método toma el parámetro de ID. de película, busca la película con el Find método Entity Framework y devuelve la película seleccionada a la vista de edición. Si no se encuentra una película, se devuelve HttpNotFound . Cuando el sistema de scaffolding creó la vista de edición, examinó la clase Movie y creó código para representar los elementos <label> y <input> para cada propiedad de la clase. En el ejemplo siguiente se muestra la vista de edición generada por el sistema de scaffolding de Visual Studio:

@model MvcMovie.Models.Movie

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

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

Observe cómo la plantilla de vista tiene una @model MvcMovie.Models.Movie instrucción en la parte superior del archivo; esto especifica que la vista espera que el modelo de la plantilla de vista sea de tipo Movie .

El código con scaffolding usa varios métodos auxiliares para optimizar el marcado HTML. El Html.LabelFor ayudante muestra el nombre del campo ( " title " , " ReleaseDate " , " Genre " o " Price " ). El Html.EditorFor ayudante representa un <input> elemento HTML. El Html.ValidationMessageFor ayudante muestra cualquier mensaje de validación asociado a esa propiedad.

Ejecute la aplicación y vaya a la dirección URL de /Movies . Haga clic en un vínculo Edit (Editar). En el explorador, vea el código fuente de la página. A continuación se muestra el código HTML del elemento Form.

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

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

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

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

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

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

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

Los <input> elementos están en un <form> elemento HTML cuyo action atributo está establecido en post en la dirección URL de /Movies/Edit . Los datos del formulario se publicarán en el servidor cuando se haga clic en el botón Guardar . La segunda línea muestra el token XSRF oculto generado por la @Html.AntiForgeryToken() llamada.

Procesamiento de la solicitud POST

En la siguiente lista se muestra la versión HttpPost del método de acción Edit.

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

El atributo ValidateAntiForgeryToken valida el token XSRF generado por la @Html.AntiForgeryToken() llamada en la vista.

El enlazador de modelos MVC de ASP.net toma los valores del formulario expuesto y crea un Movie objeto que se pasa como movie parámetro. ModelState.IsValidComprueba que los datos enviados en el formulario se pueden usar para modificar (editar o actualizar) un Movie objeto. Si los datos son válidos, los datos de la película se guardan en la Movies colección de db ( MovieDBContext instancia). Los nuevos datos de la película se guardan en la base de datos llamando al SaveChanges método de MovieDBContext . Después de guardar los datos, el código redirige al usuario al método de acción Index de la clase MoviesController, que muestra la colección de películas, incluidos los cambios que se acaban de hacer.

En cuanto la validación del lado cliente determina que el valor de un campo no es válido, se muestra un mensaje de error. Si JavaScript está deshabilitado, la validación del lado cliente está deshabilitada. Sin embargo, el servidor detecta que los valores expuestos no son válidos y los valores del formulario se vuelven a mostrar con mensajes de error.

La validación se examina con más detalle más adelante en el tutorial.

Las Html.ValidationMessageFor aplicaciones auxiliares de la plantilla de vista Edit. cshtml se encargan de mostrar los mensajes de error correspondientes.

abcNotValid

Todos los HttpGet métodos siguen un patrón similar. Obtienen un objeto de película (o una lista de objetos, en el caso de Index ) y pasan el modelo a la vista. El Create método pasa un objeto de película vacío a la vista de creación. Todos los métodos que crean, editan, eliminan o modifican los datos lo hacen en la sobrecarga HttpPost del método. La modificación de los datos en un método HTTP GET es un riesgo para la seguridad, como se describe en la entrada de blog de ASP.NET MVC Tip #46 – no usar los vínculos de eliminación porque crean carencias de seguridad. La modificación de los datos en un método GET también infringe los procedimientos recomendados de HTTP y el patrón Rest de la arquitectura, que especifica que las solicitudes GET no deben cambiar el estado de la aplicación. En otras palabras, realizar una operación GET debería ser una operación segura sin efectos secundarios, que no modifica los datos persistentes.

validación de jQuery para configuraciones regionales que no estén en inglés

Si usa un equipo Inglés de EE. UU., puede omitir esta sección y pasar al siguiente tutorial. Puede descargar la versión de globalización de este tutorial aquí. Para obtener un tutorial de dos partes excelente sobre internacionalización, consulte internacionalización de MVC 5 de Nadeem de ASP.net.

Note

para admitir la validación de jQuery para configuraciones regionales distintas del inglés que usan una coma ( " , " ) para un separador decimal y formatos de fecha que no son de Inglés de EE. UU., debe incluir globalize.js y el archivo de *globalize.cultures.jso referencia cultural * específico (de https://github.com/jquery/globalize ) y JavaScript que se va a usar Globalize.parseFloat . Puede obtener la validación no en Inglés de jQuery desde NuGet. (No instale el globalizado si usa una configuración regional en inglés).

  1. En el menú herramientas , haga clic en Administrador de paquetes Nugety, a continuación, haga clic en administrar paquetes NuGet para la solución.

  2. En el panel izquierdo, seleccione examinar *. * (Consulte la imagen siguiente).

  3. En el cuadro de entrada, escriba * Globalization * *.

    Elija jQuery.Validation.Globalize , elija MvcMovie y haga clic en instalar. El archivo Scripts\jquery.globalize\globalize.js se agregará al proyecto. La carpeta * Scripts\jquery.globalize\cultures contendrá * muchos archivos JavaScript de referencia cultural. Tenga en cuenta que este paquete puede tardar cinco minutos en instalarse.

    En el código siguiente se muestran las modificaciones en el archivo Views\Movies\Edit.cshtml:

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

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

Para evitar repetir este código en cada vista de edición, puede moverlo al archivo de diseño. Para optimizar la descarga del script, vea mi tutorial de Unión y minificación.

Para obtener más información, consulte ASP.NET MVC 3 internacionalización y ASP.NET MVC 3 Internacionalization, parte 2 (NerdDinner).

Como solución temporal, si no consigue que la validación funcione en su configuración regional, puede forzar que el equipo use el Inglés de EE. UU. o puede deshabilitar JavaScript en el explorador. Para obligar al equipo a utilizar el Inglés de EE. UU., puede Agregar el elemento de globalización al archivo de web.config raíz del proyecto. En el código siguiente se muestra el elemento Globalization con la referencia cultural establecida en Estados Unidos English.

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

En el siguiente tutorial, se implementará la funcionalidad de búsqueda.