Pertenencia y administración

Por Erik Reitan

En esta serie de tutoriales se le enseñarán los conceptos básicos de la creación de una aplicación ASP.NET Web Forms mediante ASP.NET 4.5 y Microsoft Visual Studio Express 2013 para Web. Como acompañamiento a este serie de tutoriales, hay disponible un proyecto con código fuente de C# de Visual Studio 2013.

En este tutorial se muestra cómo actualizar la aplicación de ejemplo Wingtip Toys para agregar un rol personalizado y usar ASP.NET Identity. También se muestra cómo implementar una página de administración desde la que el usuario con un rol personalizado puede agregar y quitar productos del sitio web.

ASP.NET Identity es el sistema de pertenencia que se usa para compilar aplicaciones web de ASP.NET y está disponible en ASP.NET 4.5. ASP.NET Identity se usa en la plantilla de proyecto Web Forms de Visual Studio 2013, así como en las plantillas para ASP.NET MVC, ASP.NET Web API y Aplicación de página única de ASP.NET. También puede instalar específicamente el sistema ASP.NET Identity mediante NuGet al empezar con una aplicación web vacía. Pero en esta serie de tutoriales se usa la plantilla de proyecto Web Forms, que incluye el sistema ASP.NET Identity. ASP.NET Identity facilita la integración de los datos de perfil específicos del usuario con los datos de aplicación. Además, ASP.NET Identity permite elegir el modelo de persistencia para perfiles de usuario en la aplicación. Puede almacenar los datos en una base de datos de SQL Server u otro almacén de datos, incluidos almacenes de datos NoSQL como tablas de almacenamiento de Windows Azure.

Este tutorial se basa en el tutorial anterior titulado "Finalización de la compra y pago con PayPal" de la serie de tutoriales sobre Wingtip Toys.

Temas que se abordarán:

  • Procedimiento para usar código para agregar un rol personalizado y un usuario a la aplicación.
  • Procedimiento para restringir el acceso a la carpeta y la página de administración.
  • Procedimiento para proporcionar navegación al usuario que pertenece al rol personalizado.
  • Procedimiento para usar el enlace de modelos para rellenar un control DropDownList con categorías de productos.
  • Procedimiento para un archivo en la aplicación web mediante el control FileUpload.
  • Procedimiento para usar controles de validación para implementar la validación de entrada.
  • Procedimiento para agregar y quitar productos de la aplicación.

Estas características se incluyen en el tutorial:

  • ASP.NET Identity
  • Configuración y autorización
  • Enlace de modelos
  • Validación discreta

ASP.NET Web Forms proporciona funcionalidades de pertenencia. Mediante el uso de la plantilla predeterminada, tiene la funcionalidad de pertenencia integrada que puede usar inmediatamente cuando se ejecuta la aplicación. En este tutorial se muestra cómo usar ASP.NET Identity para agregar un rol personalizado y asignar un usuario a ese rol. Obtendrá información sobre cómo restringir el acceso a la carpeta y la página de administración. Agregará una página a la carpeta de administración que permite a un usuario con un rol personalizado agregar y quitar productos, y obtener una vista previa de un producto después de agregarlo.

Adición de un rol personalizado

Con ASP.NET Identity, puede agregar un rol personalizado y asignar un usuario a ese rol mediante código.

  1. En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Logic y cree una clase.

  2. Asigne el nombre RoleActions.cs a la nueva clase.

  3. Modifique el código para que sea similar al siguiente:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    namespace WingtipToys.Logic
    {
        internal class RoleActions
        {
        }
    }
    
  4. En el Explorador de soluciones, abra el archivo Global.asax.cs.

  5. Para modificar el archivo Global.asax.cs, agregue el código resaltado en amarillo para que sea similar al siguiente:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Optimization;
    using System.Web.Routing;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Data.Entity;
    using WingtipToys.Models;
    using WingtipToys.Logic;
    
    namespace WingtipToys
    {
        public class Global : HttpApplication
        {
            void Application_Start(object sender, EventArgs e)
            {
              // Code that runs on application startup
              RouteConfig.RegisterRoutes(RouteTable.Routes);
              BundleConfig.RegisterBundles(BundleTable.Bundles);
    
              // Initialize the product database.
              Database.SetInitializer(new ProductDatabaseInitializer());
    
              // Create the custom role and user.
              RoleActions roleActions = new RoleActions();
              roleActions.AddUserAndRole();
            }
        }
    }
    
  6. Observe que AddUserAndRole está subrayado en rojo. Haga doble clic en el código AddUserAndRole.
    Se subrayará la letra "A" al principio del método resaltado.

  7. Mantenga el puntero sobre la letra "A" y haga clic en la interfaz de usuario que le permite generar un código auxiliar de método para el método AddUserAndRole.

    Membership and Administration - Generate Method Stub

  8. Haga clic en la opción titulada:
    Generate method stub for "AddUserAndRole" in "WingtipToys.Logic.RoleActions"

  9. Abra el archivo RoleActions.cs desde la carpeta Logic.
    El método AddUserAndRole se ha agregado al archivo de clase.

  10. Para modificar el archivo RoleActions.cs, quite NotImplementedException y agregue el código resaltado en amarillo, de modo que sea similar al siguiente:

    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    using Microsoft.AspNet.Identity;
    using Microsoft.AspNet.Identity.EntityFramework;
    
    namespace WingtipToys.Logic
    {
      internal class RoleActions
      {
        internal void AddUserAndRole()
        {
          // Access the application context and create result variables.
          Models.ApplicationDbContext context = new ApplicationDbContext();
          IdentityResult IdRoleResult;
          IdentityResult IdUserResult;
    
          // Create a RoleStore object by using the ApplicationDbContext object. 
          // The RoleStore is only allowed to contain IdentityRole objects.
          var roleStore = new RoleStore<IdentityRole>(context);
    
          // Create a RoleManager object that is only allowed to contain IdentityRole objects.
          // When creating the RoleManager object, you pass in (as a parameter) a new RoleStore object. 
          var roleMgr = new RoleManager<IdentityRole>(roleStore);
    
          // Then, you create the "canEdit" role if it doesn't already exist.
          if (!roleMgr.RoleExists("canEdit"))
          {
            IdRoleResult = roleMgr.Create(new IdentityRole { Name = "canEdit" });
          }
    
          // Create a UserManager object based on the UserStore object and the ApplicationDbContext  
          // object. Note that you can create new objects and use them as parameters in
          // a single line of code, rather than using multiple lines of code, as you did
          // for the RoleManager object.
          var userMgr = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
          var appUser = new ApplicationUser
          {
            UserName = "canEditUser@wingtiptoys.com",
            Email = "canEditUser@wingtiptoys.com"
          };
          IdUserResult = userMgr.Create(appUser, ConfigurationManager.AppSettings["AppUserPasswordKey"]);
    
          // If the new "canEdit" user was successfully created, 
          // add the "canEdit" user to the "canEdit" role. 
          if (!userMgr.IsInRole(userMgr.FindByEmail("canEditUser@wingtiptoys.com").Id, "canEdit"))
          {
            IdUserResult = userMgr.AddToRole(userMgr.FindByEmail("canEditUser@wingtiptoys.com").Id, "canEdit");
          }
        }
      }
    }
    

En el código anterior, primero se establece un contexto de base de datos para la base de datos de pertenencia. La base de datos de pertenencia también se almacena como un archivo .mdf en la carpeta App_Data. Podrá ver esta base de datos una vez que el primer usuario haya iniciado sesión en esta aplicación web.

Nota:

Si quiere almacenar los datos de pertenencia junto con los datos del producto, puede considerar la posibilidad de usar el mismo objeto DbContext que ha utilizado para almacenar los datos del producto en el código anterior.

La palabra clave internal es un modificador de acceso para tipos (como clases) y miembros de tipo (como métodos o propiedades). Solo se puede acceder a los tipos internos o los miembros desde los archivos del mismo ensamblado (archivo .dll). Al compilar la aplicación, se crea un archivo de ensamblado (.dll) que contiene el código que se ejecuta al ejecutar la aplicación.

Se crea un objeto RoleStore, que proporciona administración de roles, en función del contexto de la base de datos.

Nota:

Observe que cuando se crea el objeto RoleStore, usa un tipo IdentityRole genérico. Esto significa que solo se permite que RoleStore contenga objetos IdentityRole. Además, con los genéricos, mejora el control de los recursos en memoria.

A continuación, se crea el objeto RoleManager en función del objeto RoleStore que acaba de crear. El objeto RoleManager expone la API relacionada con el rol que se puede usar para guardar automáticamente los cambios en RoleStore. Solo se permite que contenga RoleManager objetos IdentityRole porque el código usa el tipo Genérico <IdentityRole>.

Llame al método RoleExists para determinar si el rol "canEdit" está presente en la base de datos de pertenencia. Si no es así, debe crear el rol.

La creación del objeto UserManager parece ser más complicada que la del control RoleManager, pero es muy similar. Solo se programa en una línea en lugar de varias. Aquí, el parámetro que va a pasar se crea como una instancia de un nuevo objeto contenido en los paréntesis.

A continuación, cree el usuario "canEditUser" mediante la creación de un objeto ApplicationUser. Después, si ha creado correctamente el usuario, agréguelo al nuevo rol.

Nota:

El control de errores se actualizará durante el tutorial "Control de errores de ASP.NET" más adelante en esta serie de tutoriales.

La próxima vez que se inicie la aplicación, el usuario denominado "canEditUser" se agregará como el rol denominado "canEdit" de la aplicación. Más adelante en este tutorial, iniciará sesión como el usuario "canEditUser" para mostrar funcionalidades adicionales que agregará durante este tutorial. Para obtener detalles de API sobre ASP.NET Identity, vea el espacio de nombres Microsoft.AspNet.Identity. Para más información sobre cómo inicializar el sistema de ASP.NET Identity, vea AspnetIdentitySample.

Restricción del acceso a la página Administración

La aplicación de ejemplo Wingtip Toys permite a los usuarios anónimos y a los usuarios que han iniciado sesión ver y comprar productos. Pero el usuario que ha iniciado sesión que tiene el rol personalizado "canEdit" puede acceder a una página restringida para agregar y quitar productos.

Adición de una carpeta y una página de administración

A continuación, creará una carpeta denominada Administración para el usuario "canEditUser" que pertenece al rol personalizado de la aplicación de ejemplo Wingtip Toys.

  1. Haga clic con el botón derecho en el nombre del proyecto (Wingtip Toys) en el Explorador de soluciones y seleccione Agregar ->Nueva carpeta.
  2. Asigne el nombre Admin a la nueva carpeta.
  3. Haga clic con el botón derecho en la carpeta Admin y, luego, seleccione Agregar ->Nuevo elemento.
    Se abrirá el cuadro de diálogo Agregar nuevo elemento.
  4. Seleccione el grupo de plantillas Visual C# ->Web de la izquierda. En la lista central, seleccione Formulario web con página maestra, asígnele el nombre AdminPage.aspx, y, después, seleccione Agregar.
  5. Seleccione el archivo Site.Master como página maestra y, después, elija Aceptar.

Adición de un archivo Web.config

Al agregar un archivo Web.config a la carpeta Admin, puede restringir el acceso a la página contenida en la carpeta.

  1. Haga clic con el botón derecho en la carpeta Admin y seleccione Agregar ->Nuevo elemento.
    Se abrirá el cuadro de diálogo Agregar nuevo elemento.

  2. En la lista de plantillas web de Visual C#, seleccione Archivo de configuración web en la lista central, acepte el nombre predeterminado de Web.config, y, después, seleccione Agregar.

  3. Reemplace el contenido XML del archivo Web.config por el siguiente:

    <?xml version="1.0"?>
    <configuration>
      <system.web>
        <authorization>
          <allow roles="canEdit"/>
          <deny users="*"/>
        </authorization>
      </system.web>
    </configuration>
    

Guarde el archivo Web.config . El archivo Web.config especifica que solo el usuario que pertenece al rol "canEdit" de la aplicación puede acceder a la página contenida en la carpeta Admin.

Inclusión de navegación de rol personalizado

Para permitir que el usuario del rol personalizado "canEdit" vaya a la sección de administración de la aplicación, debe agregar un vínculo a la página Site.Master. Solo los usuarios que pertenecen al rol "canEdit" podrán ver el vínculo Admin y acceder a la sección de administración.

  1. En el Explorador de soluciones, abra la página Site.Master.

  2. A fin de crear un vínculo para el usuario del rol "canEdit", agregue el marcado resaltado en amarillo al siguiente elemento de lista sin ordenar <ul> para que la lista sea similar a la siguiente:

    <ul class="nav navbar-nav">
        <li><a runat="server" id="adminLink" visible="false" 
          href="~/Admin/AdminPage">Admin</a></li>
        <li><a runat="server" href="~/">Home</a></li>
        <li><a runat="server" href="~/About">About</a></li>
        <li><a runat="server" href="~/Contact">Contact</a></li>
        <li><a runat="server" href="~/ProductList">Products</a></li>
        <li><a runat="server" href="~/ShoppingCart" 
          ID="cartCount">&nbsp;</a></li>
    </ul>
    
  3. Abra el archivo Site.Master.cs. Haga que el vínculo Admin solo sea visible para el usuario "canEditUser"; para ello, agregue el código resaltado en amarillo al controlador Page_Load. El controlador Page_Load será similar al siguiente:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (HttpContext.Current.User.IsInRole("canEdit"))
        {
            adminLink.Visible = true;
        }
    }
    

Cuando se carga la página, el código comprueba si el usuario que ha iniciado sesión tiene el rol "canEdit". Si el usuario pertenece al rol "canEdit", el elemento span que contiene el vínculo a la página AdminPage.aspx (y, por tanto, el vínculo dentro de span) se hace visible.

Habilitación de la administración de productos

Hasta ahora, ha creado el rol "canEdit" y ha agregado un usuario "canEditUser", una carpeta de administración y una página de administración. Ha establecido derechos de acceso para la carpeta y la página de administración, y ha agregado un vínculo de navegación para el usuario del rol "canEdit" a la aplicación. A continuación, agregará marcado a la página AdminPage.aspx y código al archivo de código subyacente AdminPage.aspx.cs que permitirá al usuario con el rol "canEdit" agregar y quitar productos.

  1. En el Explorador de soluciones, abra el archivo AdminPage.aspx desde la carpeta Admin.

  2. Reemplace el marcado existente por el siguiente:

    <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="AdminPage.aspx.cs" Inherits="WingtipToys.Admin.AdminPage" %>
    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
        <h1>Administration</h1>
        <hr />
        <h3>Add Product:</h3>
        <table>
            <tr>
                <td><asp:Label ID="LabelAddCategory" runat="server">Category:</asp:Label></td>
                <td>
                    <asp:DropDownList ID="DropDownAddCategory" runat="server" 
                        ItemType="WingtipToys.Models.Category" 
                        SelectMethod="GetCategories" DataTextField="CategoryName" 
                        DataValueField="CategoryID" >
                    </asp:DropDownList>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddName" runat="server">Name:</asp:Label></td>
                <td>
                    <asp:TextBox ID="AddProductName" runat="server"></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" Text="* Product name required." ControlToValidate="AddProductName" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddDescription" runat="server">Description:</asp:Label></td>
                <td>
                    <asp:TextBox ID="AddProductDescription" runat="server"></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" Text="* Description required." ControlToValidate="AddProductDescription" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddPrice" runat="server">Price:</asp:Label></td>
                <td>
                    <asp:TextBox ID="AddProductPrice" runat="server"></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" Text="* Price required." ControlToValidate="AddProductPrice" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                    <asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server" Text="* Must be a valid price without $." ControlToValidate="AddProductPrice" SetFocusOnError="True" Display="Dynamic" ValidationExpression="^[0-9]*(\.)?[0-9]?[0-9]?$"></asp:RegularExpressionValidator>
                </td>
            </tr>
            <tr>
                <td><asp:Label ID="LabelAddImageFile" runat="server">Image File:</asp:Label></td>
                <td>
                    <asp:FileUpload ID="ProductImage" runat="server" />
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" Text="* Image path required." ControlToValidate="ProductImage" SetFocusOnError="true" Display="Dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
        </table>
        <p></p>
        <p></p>
        <asp:Button ID="AddProductButton" runat="server" Text="Add Product" OnClick="AddProductButton_Click"  CausesValidation="true"/>
        <asp:Label ID="LabelAddStatus" runat="server" Text=""></asp:Label>
        <p></p>
        <h3>Remove Product:</h3>
        <table>
            <tr>
                <td><asp:Label ID="LabelRemoveProduct" runat="server">Product:</asp:Label></td>
                <td><asp:DropDownList ID="DropDownRemoveProduct" runat="server" ItemType="WingtipToys.Models.Product" 
                        SelectMethod="GetProducts" AppendDataBoundItems="true" 
                        DataTextField="ProductName" DataValueField="ProductID" >
                    </asp:DropDownList>
                </td>
            </tr>
        </table>
        <p></p>
        <asp:Button ID="RemoveProductButton" runat="server" Text="Remove Product" OnClick="RemoveProductButton_Click" CausesValidation="false"/>
        <asp:Label ID="LabelRemoveStatus" runat="server" Text=""></asp:Label>
    </asp:Content>
    
  3. A continuación, abra el archivo de código subyacente AdminPage.aspx.cs; para ello, haga clic con el botón derecho en AdminPage.aspx y haga clic en Ver código.

  4. Reemplace el código existente en el archivo de código subyacente AdminPage.aspx.cs por el código siguiente:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using WingtipToys.Models;
    using WingtipToys.Logic;
    
    namespace WingtipToys.Admin
    {
      public partial class AdminPage : System.Web.UI.Page
      {
        protected void Page_Load(object sender, EventArgs e)
        {
          string productAction = Request.QueryString["ProductAction"];
          if (productAction == "add")
          {
            LabelAddStatus.Text = "Product added!";
          }
    
          if (productAction == "remove")
          {
            LabelRemoveStatus.Text = "Product removed!";
          }
        }
    
        protected void AddProductButton_Click(object sender, EventArgs e)
        {
          Boolean fileOK = false;
          String path = Server.MapPath("~/Catalog/Images/");
          if (ProductImage.HasFile)
          {
            String fileExtension = System.IO.Path.GetExtension(ProductImage.FileName).ToLower();
            String[] allowedExtensions = { ".gif", ".png", ".jpeg", ".jpg" };
            for (int i = 0; i < allowedExtensions.Length; i++)
            {
              if (fileExtension == allowedExtensions[i])
              {
                fileOK = true;
              }
            }
          }
    
          if (fileOK)
          {
            try
            {
              // Save to Images folder.
              ProductImage.PostedFile.SaveAs(path + ProductImage.FileName);
              // Save to Images/Thumbs folder.
              ProductImage.PostedFile.SaveAs(path + "Thumbs/" + ProductImage.FileName);
            }
            catch (Exception ex)
            {
              LabelAddStatus.Text = ex.Message;
            }
    
            // Add product data to DB.
            AddProducts products = new AddProducts();
            bool addSuccess = products.AddProduct(AddProductName.Text, AddProductDescription.Text,
                AddProductPrice.Text, DropDownAddCategory.SelectedValue, ProductImage.FileName);
            if (addSuccess)
            {
              // Reload the page.
              string pageUrl = Request.Url.AbsoluteUri.Substring(0, Request.Url.AbsoluteUri.Count() - Request.Url.Query.Count());
              Response.Redirect(pageUrl + "?ProductAction=add");
            }
            else
            {
              LabelAddStatus.Text = "Unable to add new product to database.";
            }
          }
          else
          {
            LabelAddStatus.Text = "Unable to accept file type.";
          }
        }
    
        public IQueryable GetCategories()
        {
          var _db = new WingtipToys.Models.ProductContext();
          IQueryable query = _db.Categories;
          return query;
        }
    
        public IQueryable GetProducts()
        {
          var _db = new WingtipToys.Models.ProductContext();
          IQueryable query = _db.Products;
          return query;
        }
    
        protected void RemoveProductButton_Click(object sender, EventArgs e)
        {
          using (var _db = new WingtipToys.Models.ProductContext())
          {
            int productId = Convert.ToInt16(DropDownRemoveProduct.SelectedValue);
            var myItem = (from c in _db.Products where c.ProductID == productId select c).FirstOrDefault();
            if (myItem != null)
            {
              _db.Products.Remove(myItem);
              _db.SaveChanges();
    
              // Reload the page.
              string pageUrl = Request.Url.AbsoluteUri.Substring(0, Request.Url.AbsoluteUri.Count() - Request.Url.Query.Count());
              Response.Redirect(pageUrl + "?ProductAction=remove");
            }
            else
            {
              LabelRemoveStatus.Text = "Unable to locate product.";
            }
          }
        }
      }
    }
    

En el código que ha escrito para el archivo de código subyacente AdminPage.aspx.cs, una clase denominada AddProducts realiza el trabajo real de agregar productos a la base de datos. Esta clase aún no existe, por lo que la creará ahora.

  1. En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Logic y seleccione Agregar ->Nuevo elemento.
    Se abrirá el cuadro de diálogo Agregar nuevo elemento.

  2. Seleccione el grupo de plantillas Visual C# ->Código de la izquierda. Después, seleccione Claseen la lista central y asígnele el nombre AddProducts.cs.
    Se muestra el nuevo archivo de clase.

  3. Reemplace el código existente por el siguiente:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    
    namespace WingtipToys.Logic
    {
        public class AddProducts
        {
            public bool AddProduct(string ProductName, string ProductDesc, string ProductPrice, string ProductCategory, string ProductImagePath)
            {
                var myProduct = new Product();
                myProduct.ProductName = ProductName;
                myProduct.Description = ProductDesc;
                myProduct.UnitPrice = Convert.ToDouble(ProductPrice);
                myProduct.ImagePath = ProductImagePath;
                myProduct.CategoryID = Convert.ToInt32(ProductCategory);
    
                using (ProductContext _db = new ProductContext())
                {
                    // Add product to DB.
                    _db.Products.Add(myProduct);
                    _db.SaveChanges();
                }
                // Success.
                return true;
            }
        }
    }
    

La página AdminPage.aspx permite al usuario que pertenece al rol "canEdit" agregar y quitar productos. Cuando se agrega un nuevo producto, se validan los detalles sobre el producto y, después, se escriben en la base de datos. El nuevo producto está disponible inmediatamente para todos los usuarios de la aplicación web.

Validación discreta

Los detalles del producto que proporciona el usuario en la página AdminPage.aspx se validan mediante controles de validación (RequiredFieldValidator y RegularExpressionValidator). Estos controles usan automáticamente la validación discreta. La validación discreta permite que los controles de validación usen JavaScript para la lógica de validación del lado cliente, lo que significa que la página no necesita un acceso al servidor para validarse. De manera predeterminada, la validación discreta se incluye en el archivo Web.config en función del siguiente valor de configuración:

<add key="ValidationSettings:UnobtrusiveValidationMode" value="WebForms" />

Expresiones regulares

El precio del producto en la página AdminPage.aspx se valida mediante un control RegularExpressionValidator. Este control valida si el valor del control de entrada asociado (el cuadro de texto "AddProductPrice") coincide con el patrón especificado por la expresión regular. Una expresión regular es una notación de coincidencia de patrones que permite buscar y comparar rápidamente patrones de caracteres específicos. El control RegularExpressionValidator incluye una propiedad denominada ValidationExpression que contiene la expresión regular utilizada para validar la entrada del precio, como se muestra a continuación:

<asp:RegularExpressionValidator 
    ID="RegularExpressionValidator1" runat="server"
    Text="* Must be a valid price without $." ControlToValidate="AddProductPrice" 
    SetFocusOnError="True" Display="Dynamic" 
    ValidationExpression="^[0-9]*(\.)?[0-9]?[0-9]?$">
</asp:RegularExpressionValidator>

Control FileUpload

Además de los controles de entrada y validación, ha agregado el control FileUpload a la página AdminPage.aspx. Este control proporciona la capacidad de cargar archivos. En este caso, solo permite cargar archivos de imagen. En el archivo de código subyacente (AdminPage.aspx.cs), cuando se hace clic en AddProductButton, el código comprueba la propiedad HasFile del control FileUpload. Si el control tiene un archivo y si se permite el tipo de archivo (en función de la extensión de archivo), la imagen se guarda en la carpeta Images y en la carpeta Images/Thumbs de la aplicación.

Enlace de modelos

Anteriormente en esta serie de tutoriales ha usado el enlace de modelos para rellenar controles ListView, FormsView, GridView y DetailView. En este tutorial, usará el enlace de modelos para rellenar un control DropDownList con una lista de categorías de productos.

El marcado que ha agregado al archivo AdminPage.aspx contiene un control DropDownList denominado DropDownAddCategory:

<asp:DropDownList ID="DropDownAddCategory" runat="server" 
        ItemType="WingtipToys.Models.Category" 
        SelectMethod="GetCategories" DataTextField="CategoryName" 
        DataValueField="CategoryID" >
    </asp:DropDownList>

Use el enlace de modelos para rellenar este control DropDownList mediante el establecimiento de los atributos ItemType y SelectMethod. El atributo ItemType especifica que se usa el tipo WingtipToys.Models.Category al rellenar el control. Ha definido este tipo al principio de esta serie de tutoriales mediante la creación de la clase Category (que se muestra a continuación). La clase Category se encuentra en la carpeta Models dentro del archivo Category.cs.

public class Category
{
    [ScaffoldColumn(false)]
    public int CategoryID { get; set; }

    [Required, StringLength(100), Display(Name = "Name")]
    public string CategoryName { get; set; }

    [Display(Name = "Product Description")]
    public string Description { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

El atributo SelectMethod del control DropDownList especifica que se usa el método GetCategories (que se muestra a continuación) que se incluye en el archivo de código subyacente (AdminPage.aspx.cs).

public IQueryable GetCategories()
{
  var _db = new WingtipToys.Models.ProductContext();
  IQueryable query = _db.Categories;
  return query;
}

Este método especifica que se usa una interfaz IQueryable para evaluar una consulta con un tipo Category. El valor devuelto se usa para rellenar el control DropDownList en el marcado de la página (AdminPage.aspx).

El texto que se muestra para cada elemento de la lista se especifica al establecer el atributo DataTextField. El atributo DataTextField usa el valor CategoryName de la clase Category (mostrado anteriormente) para mostrar cada categoría en el control DropDownList. El valor real que se pasa cuando se selecciona un elemento en el control DropDownList se basa en el atributo DataValueField. El atributo DataValueField se establece en CategoryID como se define en la clase Category (mostrada antes).

Cómo funcionará la aplicación

Cuando el usuario que pertenece al rol "canEdit" navega a la página por primera vez, el control DropDownAddCategoryDropDownList se rellena como se ha descrito antes. El control DropDownRemoveProduct DropDownList también se rellena con productos mediante el mismo enfoque. El usuario que pertenece al rol "canEdit" selecciona el tipo de categoría y agrega detalles del producto (Nombre, Descripción, Precio y Archivo de imagen). Cuando el usuario que pertenece al rol "canEdit" hace clic en el botón Agregar producto, se desencadena el controlador de eventos AddProductButton_Click. El controlador de eventos AddProductButton_Click ubicado en el archivo de código subyacente (AdminPage.aspx.cs) comprueba el archivo de imagen para asegurarse de que coincide con los tipos de archivo permitidos (.gif, .png, .jpeg o .jpg). Después, el archivo de imagen se guarda en una carpeta de la aplicación de ejemplo Wingtip Toys. A continuación, el nuevo producto se agrega a la base de datos. Para lograr la adición de un nuevo producto, se crea una instancia de la clase AddProducts y los productos con nombre. La clase AddProducts tiene un método denominado AddProducty el objeto products llama a este método para agregar productos a la base de datos.

// Add product data to DB.
AddProducts products = new AddProducts();
bool addSuccess = products.AddProduct(AddProductName.Text, AddProductDescription.Text,
    AddProductPrice.Text, DropDownAddCategory.SelectedValue, ProductImage.FileName);

Si el código agrega correctamente el nuevo producto a la base de datos, la página se vuelve a cargar con el valor de cadena de consulta ProductAction=add.

Response.Redirect(pageUrl + "?ProductAction=add");

Cuando se vuelve a cargar la página, la cadena de consulta se incluye en la dirección URL. Al volver a cargar la página, el usuario que pertenece al rol "canEdit" puede ver inmediatamente las actualizaciones en los controles DropDownList de la página AdminPage.aspx. Además, al incluir la cadena de consulta con la dirección URL, la página puede mostrar un mensaje correcto al usuario que pertenece al rol "canEdit".

Cuando se vuelve a cargar la página AdminPage.aspx, se llama al evento Page_Load.

protected void Page_Load(object sender, EventArgs e)
{
    string productAction = Request.QueryString["ProductAction"];
    if (productAction == "add")
    {
        LabelAddStatus.Text = "Product added!";
    }

    if (productAction == "remove")
    {
        LabelRemoveStatus.Text = "Product removed!";
    }
}

El controlador de eventos Page_Load comprueba el valor de la cadena de consulta y determina si se debe mostrar un mensaje de operación correcta.

Ejecutar la aplicación

Ahora puede ejecutar la aplicación para ver cómo agregar, eliminar y actualizar elementos en el carro de la compra. El total del carro de la compra reflejará el costo total de todos los artículos del carro de la compra.

  1. En el Explorador de soluciones, presione F5 para ejecutar la aplicación de ejemplo Wingtip Toys.
    El explorador se abre y muestra la página Default.aspx.

  2. Haga clic en la pestaña Iniciar sesión en la parte superior de la página.

    Membership and Administration - Log In Link

    Se muestra la página Login.aspx.

  3. Escriba el nombre de usuario y la contraseña.

    Membership and Administration - Log In Page

  4. Haga clic en el botón Iniciar sesión cerca de la parte inferior de la página.

  5. En la parte superior de la página siguiente, seleccione el vínculo Administración para ir a la página AdminPage.aspx.

    Membership and Administration - Admin Link

  6. Para probar la validación de entrada, haga clic en el botón Agregar producto sin agregar detalles del producto.

    Membership and Administration - Admin Page

    Observe que se muestran los mensajes de campo necesarios.

  7. Agregue los detalles de un nuevo producto y, después, haga clic en el botón Agregar producto.

    Membership and Administration - Add Product

  8. Seleccione Productos en el menú de navegación superior para ver el nuevo producto que ha agregado.

    Membership and Administration - Show New Product

  9. Haga clic en el vínculo Administración para volver a la página de administración.

  10. En la sección Quitar producto de la página, seleccione el nuevo producto que ha agregado en DropDownListBox.

  11. Haga clic en el botón Quitar producto para quitar el nuevo producto de la aplicación.

    Membership and Administration - Remove Product

  12. Seleccione Productos en el menú de navegación superior para confirmar que el producto se ha quitado.

  13. Haga clic en Cerrar sesión para salir del modo de administración.
    Observe que en el panel de navegación superior ya no se muestra el elemento de menú Administración.

Resumen

En este tutorial, ha agregado un rol personalizado y un usuario que pertenece al rol personalizado, ha restringido el acceso a la carpeta y la página de administración, y ha proporcionado navegación para el usuario que pertenece al rol personalizado. Ha usado el enlace de modelos para rellenar un control DropDownList con datos. Ha implementado el control FileUpload y controles de validación. Además, ha aprendido a agregar y quitar productos de una base de datos. En el siguiente tutorial, aprenderá a implementar el enrutamiento de ASP.NET.

Recursos adicionales

Web.config: elemento authorization
ASP.NET Identity
Implementación de una aplicación segura de ASP.NET Web Forms con pertenencia, OAuth y SQL Database en un sitio web de Azure
Microsoft Azure: Prueba gratuita