Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Por Rick Anderson, Dave Brock y Kirk Larkin
Note
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 10 de este artículo.
Warning
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la Política de soporte de .NET y .NET Core. Para la versión actual, consulte la versión .NET 10 de este artículo.
Las páginas pueden facilitar la codificación de escenarios centrados en páginas y hacer que sea más productivo que el uso de controladores y vistas.
Si busca un tutorial que use el enfoque Model-View-Controller, consulte Get started with ASP.NET Core MVC.
En este artículo se tratan la arquitectura, los conceptos y los patrones que hacen que Pages sea eficaz para crear aplicaciones web centradas en páginas. Explica cómo funcionan las páginas, sus componentes clave y los procedimientos recomendados para la implementación. Si prefiere el aprendizaje práctico con instrucciones paso a paso, consulte Tutorial: Creación de una aplicación web de Razor Pages con ASP.NET Core. Para obtener información general sobre ASP.NET Core, consulte la Introducción a ASP.NET Core.
Prerequisites
- Visual Studio 2022 con la carga de trabajo ASP.NET y desarrollo web.
- SDK de .NET 6
Crear un Razor Pages project
Consulte Comience con Razor Pages para obtener instrucciones detalladas sobre cómo crear un Razor proyecto de Pages.
Páginas
Pages está habilitado en :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
En el código anterior:
- agrega servicios para Pages a la aplicación.
- agrega puntos de conexión para las páginas al sistema.
Considere la posibilidad de una página básica:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
El código anterior se parece mucho a un archivo de vista Razor que se usa en una aplicación de ASP.NET Core con controladores y vistas. La directiva lo hace diferente. transforma el archivo en una acción de MVC, lo que significa que administra las solicitudes directamente, sin tener que pasar a través de un controlador. debe ser la primera directiva en una página. afecta al comportamiento de otros constructos. Los nombres de archivo de Pages tienen un sufijo.
Una página similar, con una clase , se muestra en los dos archivos siguientes. El archivo :
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Modelo de página :
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
Por convención, el archivo de clase tiene el mismo nombre que el archivo de Página con adjunto. Por ejemplo, la página anterior es. El archivo que contiene la clase se denomina .
Las asociaciones de rutas de dirección URL a páginas se determinan según la ubicación de la página en el sistema de archivos. En la tabla siguiente, se muestra la ruta de página y la URL correspondiente.
| Ruta de acceso y nombre de archivo | dirección URL coincidente |
|---|---|
/Pages/Index.cshtml |
o |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
o |
Notes:
- El entorno de ejecución busca archivos de Pages en la carpeta Pages de forma predeterminada.
- es la página predeterminada cuando una URL no incluye una página.
Escritura de un formulario básico
Pages está diseñado para facilitar la implementación de patrones comunes que se usan con exploradores web al compilar una aplicación. La vinculación de modelos, los ayudantes de etiquetas y los ayudantes de HTML funcionan con las propiedades definidas en una clase de página. Considere la posibilidad de una página que implementa un formulario básico del estilo "Póngase en contacto con nosotros" para el modelo :
Para los ejemplos de este documento, el DbContext se inicializa en el archivo Program.cs.
La base de datos en memoria requiere el paquete NuGet .
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
El modelo de datos:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
El contexto de la base de datos:
using Microsoft.EntityFrameworkCore;
namespace RazorPagesContacts.Data
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext (DbContextOptions<CustomerDbContext> options)
: base(options)
{
}
public DbSet<RazorPagesContacts.Models.Customer> Customer => Set<RazorPagesContacts.Models.Customer>();
}
}
Archivo de visualización
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
Modelo de página :
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Por convención, la clase se denomina y se encuentra en el mismo espacio de nombres que la página.
La clase permite la separación de la lógica de una página de su presentación. Define los controladores de página para solicitudes que se envían a la página y los datos que usan para representar la página. Esta separación permite lo siguiente:
- Administración de dependencias de página mediante la inyección de dependencias.
- Pruebas unitarias
La página tiene un método controlador, que se ejecuta en las solicitudes (cuando un usuario envía el formulario). Se pueden agregar métodos de controlador para cualquier verbo HTTP. Los controladores más comunes son:
- para inicializar el estado necesario para la página. En el código anterior, el método muestra la Página.
- para gestionar los envíos de formularios.
El sufijo de nombre es opcional, pero se usa a menudo por convención para funciones asincrónicas. El código anterior es típico de Pages.
Si está familiarizado con asp.NET aplicaciones que usan controladores y vistas:
- El código del ejemplo anterior es similar al típico código de controlador.
- La mayoría de los primitivos de MVC, como el enlace de modelos, la validación y los resultados de acciones, funcionan del mismo modo con los controladores y Pages.
El método anterior:
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
El flujo básico de :
Compruebe los errores de validación.
- Si no hay ningún error, guarde los datos y redirija.
- Si hay errores, muestre la página de nuevo con mensajes de validación. En muchos casos, los errores de validación se detectan en el cliente y nunca se envían al servidor.
Archivo de visualización
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
El HTML renderizado de :
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
En el código anterior, el envío del formulario:
Con datos válidos:
El método del controlador llama al método auxiliar. devuelve una instancia de . :
- Es el resultado de una acción.
- Es similar a o (se usa en controladores y vistas).
- Se ha personalizado para las páginas. En el ejemplo anterior, redirige a la página de índice raíz (). se detalla en la sección Generación de direcciones URL para las páginas.
Con errores de validación que se pasan al servidor:
- El método del controlador llama al método auxiliar. devuelve una instancia de . El retorno es similar a cómo las acciones en los controladores realizan devoluciones. es el tipo de retorno predeterminado para un método de controlador. Un método de controlador que devuelve y representa la página.
- En el ejemplo anterior, la publicación del formulario sin valores hace que se devuelva ModelState.IsValid como false. En este ejemplo, no se muestra ningún error de validación en el cliente. El manejo de errores de validación se aborda más adelante en este documento.
[BindProperty] public Customer? Customer { get; set; } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } if (Customer != null) _context.Customer.Add(Customer); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }Con errores de validación detectados mediante la validación del lado cliente:
- Los datos no se publican en el servidor.
- La validación del lado cliente se explica más adelante en este documento.
La propiedad utiliza el atributo para habilitar el enlace de modelos.
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
no debe usarse en modelos que contengan propiedades que el cliente no debe cambiar. Para más detalles, consulte Overposting.
De forma predeterminada, Pages enlaza propiedades solo con elementos que no son verbos. El enlace a propiedades elimina la necesidad de escribir código para convertir los datos HTTP en el tipo de modelo. La vinculación reduce el código al usar la misma propiedad para renderizar los campos del formulario y aceptar la entrada de datos.
Warning
Por razones de seguridad, debe habilitar la vinculación de datos de solicitud a las propiedades del modelo de página. Compruebe las entradas de los usuarios antes de asignarlas a las propiedades. Optar por la vinculación puede ser útil al abordar escenarios que dependen de la cadena de consulta o de los valores de la ruta.
Para vincular una propiedad en solicitudes, establezca la propiedad del atributo en:
[BindProperty(SupportsGet = true)]
Para obtener más información, consulte ASP.NET Core Community Standup: Bind on GET discussion (YouTube).
Revisión del archivo de visualización
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
- En el código anterior, el asistente de etiquetas de entrada enlaza el elemento HTML con la expresión del modelo .
- hace que los asistentes de etiquetas estén disponibles.
Página principal
es la página principal:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@if (Model.Customers != null)
{
foreach (var contact in Model.Customers)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
Clase asociada ():
public class IndexModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public IndexModel(Data.CustomerDbContext context)
{
_context = context;
}
public IList<Customer>? Customers { get; set; }
public async Task OnGetAsync()
{
Customers = await _context.Customer.ToListAsync();
}
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
}
El archivo contiene el siguiente marcado:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
El asistente de etiquetas delimitadoras ha usado el atributo para generar un vínculo a la página de edición. El vínculo contiene datos de ruta con el identificador del contacto. Por ejemplo: . Las aplicaciones auxiliares de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de .
El archivo contiene el marcado para crear un botón de eliminar para cada uno de los contactos de cliente.
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
El código HTML representado:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Al representar el botón de eliminar en HTML, el elemento formaction incluye parámetros para los siguientes elementos:
- Id. de contacto de cliente especificado mediante el atributo .
- especificado mediante el atributo .
Al seleccionar el botón, se envía una solicitud de formulario al servidor. De forma predeterminada, el nombre del método de control se selecciona de acuerdo con el valor del parámetro y según el esquema .
Como el término está presente en este ejemplo, se utiliza el método de controlador para procesar la solicitud . Si el valor se establece en uno diferente, como , se seleccionará un método de controlador con el nombre .
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
El método:
- Obtiene el elemento de la cadena de consulta.
- Realiza una consulta a la base de datos del contacto de cliente con .
- Si se encuentra el contacto del cliente, este se quita y se actualiza la base de datos.
- Instrucciones para redirigir a la página índice principal ().
El archivo Edit.cshtml
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Customer</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Customer!.Id" />
<div class="form-group">
<label asp-for="Customer!.Name" class="control-label"></label>
<input asp-for="Customer!.Name" class="form-control" />
<span asp-validation-for="Customer!.Name" 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-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
La primera línea contiene la directiva . La restricción de ruteo indica a la página que acepte solicitudes a la misma que contengan datos de ruta. Si una solicitud a la página no contiene datos de ruta que se puedan convertir en , el tiempo de ejecución devuelve un error HTTP 404 (no encontrado). Para que el identificador sea opcional, anexe a la restricción de ruta:
@page "{id:int?}"
El archivo :
public class EditModel : PageModel
{
private readonly RazorPagesContacts.Data.CustomerDbContext _context;
public EditModel(RazorPagesContacts.Data.CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Customer = await _context.Customer.FirstOrDefaultAsync(m => m.Id == id);
if (Customer == null)
{
return NotFound();
}
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null)
{
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(Customer.Id))
{
return NotFound();
}
else
{
throw;
}
}
}
return RedirectToPage("./Index");
}
private bool CustomerExists(int id)
{
return _context.Customer.Any(e => e.Id == id);
}
}
Validation
Reglas de validación:
- Se especifican mediante declaración en la clase de modelo.
- Se aplican en toda la aplicación.
El espacio de nombres proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad. DataAnnotations también contiene atributos de formato como el caso de los atributos que ayudan a aplicar formato y no proporcionan validación alguna.
Considere el modelo :
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
Con el siguiente archivo de visualización:
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
El código anterior:
Incluye jQuery y scripts de validación de jQuery.
Usa los ayudantes de etiquetas y Tag Helpers para facilitar lo siguiente:
- Validación del lado cliente.
- Representación del error de validación.
Se genera el siguiente código HTML:
<p>Enter a customer name:</p> <form method="post"> Name: <input type="text" data-val="true" data-val-length="The field Name must be a string with a maximum length of 10." data-val-length-max="10" data-val-required="The Name field is required." id="Customer_Name" maxlength="10" name="Customer.Name" value="" /> <input type="submit" /> <input name="__RequestVerificationToken" type="hidden" value="<Antiforgery token here>" /> </form> <script src="/lib/jquery/dist/jquery.js"></script> <script src="/lib/jquery-validation/dist/jquery.validate.js"></script> <script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Al publicar el formulario de creación sin un valor de nombre, se muestra el mensaje de error "El campo Nombre es obligatorio" en el formulario. Si JavaScript está habilitado en el cliente, el explorador muestra el error sin realizar la publicación en el servidor.
El atributo genera en el HTML renderizado. impide que los navegadores sobrepasen la longitud máxima especificada. Si se usa una herramienta como Fiddler para editar y reproducir la publicación:
- Con el nombre que tiene más de 10 caracteres.
- Se devuelve el mensaje de error "El nombre del campo debe ser una cadena con una longitud máxima de 10 caracteres".
Considere el modelo siguiente:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
Los atributos de validación especifican el comportamiento que se exigirá a las propiedades del modelo al que se aplican:
Los atributos y indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.
El atributo se usa para limitar los caracteres que se pueden escribir. En el código anterior, "Género":
- Solo debe usar letras.
- La primera letra debe estar en mayúsculas. No se permiten espacios en blanco, números ni caracteres especiales.
La "Clasificación":
- Requiere que el primer carácter sea una letra mayúscula.
- Permite caracteres especiales y números en los espacios posteriores. "PG-13" es válido para una "Clasificación", pero falla para un "Género".
El atributo restringe un valor a un intervalo determinado.
El atributo establece la longitud máxima de una propiedad de cadena y, opcionalmente, su longitud mínima.
Los tipos de valor (como , , , ) son intrínsecamente necesarios y no necesitan el atributo .
La página de creación para el modelo muestra errores relacionados con valores no válidos.
Formulario de vista de película con varios errores de validación del lado del cliente con jQuery
Para obtener más información, consulte:
- Agregue validación a la aplicación de películas
- Validación del modelo en ASP.NET Core.
Aislamiento de CSS
Aísle los estilos CSS a páginas, vistas y componentes individuales para reducir o evitar lo siguiente:
- Dependencias de estilos globales que resultan difíciles de mantener.
- Conflictos de estilo en el contenido anidado.
Para agregar un archivo CSS con ámbito para una página o vista, coloque los estilos CSS en un archivo complementario que coincida con el nombre del archivo . En el ejemplo siguiente, un archivo proporciona estilos CSS que solo se aplican a la página o vista especificada.
(Páginas) o (MVC):
h1 {
color: red;
}
El aislamiento de CSS se produce en tiempo de compilación. El marco reescribe selectores CSS para que coincidan con el marcado representado por las páginas o vistas de la aplicación. Estos estilos de CSS reescritos se unen y se generan como un recurso estático, . El marcador de posición {APP ASSEMBLY} es el nombre del ensamblado del proyecto. Se coloca un vínculo a los estilos CSS agrupados en el diseño de la aplicación.
En el contenido de la aplicación (Páginas) o (MVC), agregue o confirme la presencia del vínculo a los estilos CSS agrupados:
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />
En el ejemplo siguiente, el nombre del ensamblado de la aplicación es :
<link rel="stylesheet" href="WebApp.styles.css" />
Los estilos definidos en un archivo CSS con ámbito solo se aplican a la salida representada del archivo coincidente. En el ejemplo anterior, cualquier declaración CSS definida en otra parte de la aplicación no entra en conflicto con el estilo de encabezado de . Las reglas de cascada y herencia de los estilos CSS permanecen en vigor para los archivos CSS de ámbito. Por ejemplo, los estilos aplicados directamente a un elemento del archivo invalidan los estilos del archivo CSS con ámbito en .
Note
Para garantizar el aislamiento de estilos CSS en el momento de la unión, no se admite la importación de CSS en bloques de código .
El aislamiento de CSS solo se aplica a los elementos HTML. El aislamiento de CSS no se admite para los asistentes de etiquetas.
Dentro del archivo CSS agrupado, cada página, vista o componente está asociado a un identificador de ámbito en el formato , donde el marcador de posición es una cadena de diez caracteres generada por el marco. En el ejemplo siguiente se proporciona el estilo del elemento anterior en la página de una aplicación de Pages:
/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
color: red;
}
En la página donde se aplica el estilo CSS desde el archivo agrupado, el identificador de ámbito se anexa como un atributo HTML:
<h1 b-3xxtam6d07>
El identificador es único para una aplicación. En tiempo de compilación, se crea una agrupación de proyecto con la convención {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css, donde el marcador de posición {STATIC WEB ASSETS BASE PATH} es la ruta base de activos web estáticos.
Si se utilizan otros proyectos, como paquetes NuGet o bibliotecas de clases, el archivo combinado:
- Hace referencia a los estilos mediante importaciones de CSS.
- No se publica como recurso web estático de la aplicación que consume los estilos.
Soporte para preprocesador CSS
Los preprocesadores de CSS son útiles para mejorar el desarrollo de CSS mediante el uso de características como variables, anidamiento, módulos, mixins y herencia. Aunque el aislamiento de CSS no admite de forma nativa preprocesadores de CSS como Sass o Less, la integración de preprocesadores de CSS se realiza sin problemas siempre que se produzca la compilación del preprocesador antes de que el marco reescriba los selectores de CSS durante el proceso de compilación. Con Visual Studio por ejemplo, configure la compilación de preprocesador existente como una tarea Before Build en el Explorador de ejecutores de tareas de Visual Studio.
Muchos paquetes NuGet de terceros, como , pueden compilar archivos SASS/SCSS al principio del proceso de compilación antes de que se produzca el aislamiento de CSS y no requieren ninguna configuración adicional.
Configuración del aislamiento de CSS
El aislamiento de CSS permite la configuración de algunos escenarios avanzados, como cuando hay dependencias en herramientas o flujos de trabajo existentes.
Personalización del formato del identificador de ámbito
En esta sección, el marcador de posición es para las aplicaciones de Pages o para las aplicaciones de MVC.
De forma predeterminada, los identificadores de ámbito usan el formato , donde el marcador de posición es una cadena de diez caracteres generada por el marco. Para personalizar el formato de identificador de ámbito, actualice el archivo project a un patrón deseado:
<ItemGroup>
<None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
En el ejemplo anterior, el CSS generado para cambia su identificador de ámbito de a .
Use identificadores de ámbito para lograr la herencia con archivos CSS de ámbito. En el siguiente ejemplo de archivo de proyecto, un archivo BaseView.cshtml.css contiene estilos comunes a través de las vistas. Un archivo hereda estos estilos.
<ItemGroup>
<None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
<None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Usa el operador comodín (*) para compartir los identificadores de ámbito en varios archivos:
<ItemGroup>
<None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Cambio de la ruta de acceso base de recursos web estáticos
El archivo CSS con ámbito se genera en la raíz de la aplicación. En el archivo project, use la propiedad StaticWebAssetBasePath para cambiar la ruta de acceso predeterminada. En el ejemplo siguiente se coloca el archivo CSS con ámbito y el resto de los recursos de la aplicación, en la ruta de acceso:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
Deshabilitación de la unión automática
Para no usar el modo en que el marco publica y carga archivos con ámbito en tiempo de ejecución, use la propiedad . Al usar esta propiedad, otras herramientas o procesos son responsables de tomar los archivos CSS aislados del directorio y de publicarlos y cargarlos en tiempo de ejecución:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Compatibilidad con la biblioteca de clases (RCL)
Cuando una biblioteca de clases (RCL) proporciona estilos aislados, el atributo de la etiqueta apunta a , donde los marcadores de posición son los siguientes:
- : ruta de acceso base del recurso web estático.
- : el identificador del paquete de la biblioteca. El identificador de paquete tiene como valor predeterminado el nombre del ensamblado del project si no se especifica el identificador del paquete en el archivo project.
En el ejemplo siguiente:
- La ruta de acceso base del recurso web estático es .
- El nombre del ensamblado de la biblioteca de clases es .
(Páginas) o (MVC):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
Para obtener más información sobre RCL, vea los siguientes artículos:
- Interfaz de usuario reutilizable Razor en bibliotecas de clases con ASP.NET Core
- Consumir componentes ASP.NET Core Razor de una biblioteca de clases (RCL)Razor
Para obtener información sobre el aislamiento CSS de Blazor, vea ASP.NET Core Blazor aislamiento css.
Controlar las solicitudes HEAD con un manejador OnGet de respaldo
Las solicitudes permiten recuperar los encabezados de un recurso específico. A diferencia de las solicitudes , las solicitudes no devuelven un cuerpo de respuesta.
Normalmente, se crea un controlador al que se llama para las solicitudes :
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Pages recurre a una llamada al controlador si no se define ningún controlador .
XSRF/CSRF y Pages
Las páginas están protegidas mediante validación antifalsificación. El elemento FormTagHelper inserta tokens antifalsificación en los elementos de formulario HTML.
Usar diseños, parciales, plantillas y asistentes de etiquetas con Pages
Las páginas funcionan con todas las capacidades del motor de vista. Los diseños, parciales, plantillas, asistentes de etiquetas y otros elementos funcionan de la misma manera que en las vistas convencionales.
Para simplificar esta página, aprovecharemos algunas de esas características.
Agregue una página de diseño a :
<!DOCTYPE html>
<html>
<head>
<title>RP Sample</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
<a asp-page="/Index">Home</a>
<a asp-page="/Customers/Create">Create</a>
<a asp-page="/Customers/Index">Customers</a> <br />
@RenderBody()
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>
El diseño:
- Controla el diseño de cada página (a no ser que la página no tenga diseño).
- Importa las estructuras HTML como JavaScript y hojas de estilos.
- El contenido de la página se representa donde <...> es llamado.
Para más información, consulte la página de diseño.
La propiedad Layout se establece en :
@{
Layout = "_Layout";
}
El diseño está en la carpeta Pages/Shared. Las páginas buscan otras vistas (diseños, plantillas, parciales) de forma jerárquica, a partir de la misma carpeta que la página actual. Un diseño en la carpeta Pages/Shared se puede usar desde cualquier página dentro de la carpeta Pages.
El archivo de diseño debería ir en la carpeta Pages/Shared.
Le recomendamos que no coloque el archivo de diseño en la carpeta Views/Shared. Views/Shared es un patrón de vistas de MVC. Pages está diseñado para basarse en la jerarquía de carpetas, no en las convenciones de ruta de acceso.
Visualizar búsqueda desde una Página incluye la carpeta Páginas. Los diseños, plantillas y vistas parciales que se usan con los controladores de MVC y las vistas convencionales funcionan sin problemas.
Agregue un archivo :
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
se explica más adelante en el tutorial. La directiva incorpora los Tag Helpers integrados en todas las páginas de la carpeta Pages.
La directiva establecida en una página:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
La directiva establece el espacio de nombres de la página. La directiva no necesita incluir el espacio de nombres.
Cuando la directiva se encuentra en , el espacio de nombres especificado proporciona el prefijo del espacio de nombres generado en la página que importa la directiva . El resto del espacio de nombres generado (la parte del sufijo) es la ruta de acceso relativa separada por puntos entre la carpeta que contiene y la carpeta que contiene la página.
Por ejemplo, la clase establece explícitamente el espacio de nombres:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
El archivo establece el siguiente espacio de nombres:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
El espacio de nombres generado para la Página es el mismo que el de la clase.
también funciona con vistas convencionales.
Considere el archivo de vista :
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer!.Name"></span>
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Archivo de vista actualizado con y el archivo de diseño anterior:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
En el código anterior, se importó el espacio de nombres y los Tag Helpers. El archivo de distribución importó los archivos JavaScript.
El Razor Pages starter project contiene el Pages/_ValidationScriptsPartial.cshtml, que enlaza la validación del lado cliente.
Para obtener más información sobre las vistas parciales, vea Vistas particionales en ASP.NET Core.
Generación de direcciones URL para las páginas
La página , mostrada anteriormente, usa :
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
La aplicación tiene la siguiente estructura de archivos o carpetas:
/Pages
Index.cshtmlPrivacy.cshtml/Customers
Create.cshtmlEdit.cshtmlIndex.cshtml
Las páginas y redirigen a después de una operación exitosa. La cadena ./Index es un nombre de página relativo que se usa para acceder a la página precedente. Se usa para generar direcciones URL a la página . Por ejemplo:
Url.Page("./Index", ...)<a asp-page="./Index">Customers Index Page</a>RedirectToPage("./Index")
El nombre de página absoluto se usa para generar direcciones URL en la página . Por ejemplo:
Url.Page("/Index", ...)<a asp-page="/Index">Home Index Page</a>RedirectToPage("/Index")
El nombre de la página es la ruta de acceso a la página desde la carpeta raíz /Pages, incluyendo un carácter inicial (por ejemplo, ). Los ejemplos anteriores de generación de URL ofrecen opciones mejoradas y capacidades funcionales en comparación con la codificación manual de un URL. La generación de direcciones URL usa el enrutamiento y puede generar y codificar parámetros según cómo se defina la ruta en la ruta de acceso de destino.
La generación de direcciones URL para las páginas admite nombres relativos. En la siguiente tabla, se muestra qué página de índice está seleccionada usando distintos parámetros en .
| RedirectToPage(x) | Page |
|---|---|
| RedirectToPage("/Index") | Pages/Index |
| RedirectToPage("./Index"); | Pages/Customers/Index |
| RedirectToPage("../Index") | Pages/Index |
| RedirectToPage("Index") | Pages/Customers/Index |
, , y son nombres relativos. El parámetro se combina con la ruta de acceso de la página actual para calcular el nombre de la página de destino.
La vinculación mediante nombres relativos es útil cuando se crean sitios con estructuras complejas. Cuando se usan nombres relativos para el vínculo entre las páginas de una carpeta:
- Al cambiar el nombre de una carpeta, no se rompen los vínculos relativos.
- Los vínculos no se rompen porque no incluyen el nombre de la carpeta.
Para redirigir a una página en otra área, especifique el área:
RedirectToPage("/Index", new { area = "Services" });
Para obtener más información, consulte Áreas en ASP.NET Core y Razor Convenciones de enrutamiento de páginas y aplicaciones en ASP.NET Core.
Atributo ViewData
Se pueden pasar datos a una página con . Las propiedades con el atributo tienen sus valores almacenados y cargados desde el elemento .
En el ejemplo siguiente, el elemento aplica el atributo a la propiedad :
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
En la página Acerca de, accede a la propiedad Title como una propiedad de modelo.
<h1>@Model.Title</h1>
En el diseño, el título se lee desde el diccionario ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
ASP.NET Core expone el TempData. Esta propiedad almacena datos hasta que son leídos. Los métodos y se pueden usar para examinar los datos sin que se eliminen. es útil para el redireccionamiento cuando se necesitan los datos de más de una única solicitud.
El siguiente código establece el valor de mediante :
public class CreateDotModel : PageModel
{
private readonly AppDbContext _db;
public CreateDotModel(AppDbContext db)
{
_db = db;
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./Index");
}
}
El siguiente marcado en el archivo muestra el valor de mediante .
<h3>Msg: @Model.Message</h3>
El modelo de página aplica el atributo a la propiedad .
[TempData]
public string Message { get; set; }
Para más información, consulte TempData.
Varios controladores por página
En la página siguiente se usa el asistente de etiquetas para generar marcado para dos controladores:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</form>
</body>
</html>
El formulario del ejemplo anterior tiene dos botones de envío, y cada uno de ellos usa para enviar a una dirección URL diferente. El atributo es un complemento de . genera direcciones URL que envían a cada uno de los métodos de controlador definidos por una página. no se especifica porque el ejemplo se vincula a la página actual.
Modelo de página:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateFATHModel : PageModel
{
private readonly AppDbContext _db;
public CreateFATHModel(AppDbContext db)
{
_db = db;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostJoinListAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
public async Task<IActionResult> OnPostJoinListUCAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Customer.Name = Customer.Name?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
El código anterior usa métodos de controlador nombrados. Los métodos de controlador con nombre se crean tomando el texto en el nombre después de y antes de (si existe). En el ejemplo anterior, los métodos de página son OnPostJoinListAsync y OnPostJoinListUCAsync. Quitando OnPost y Async, los nombres de controlador son y .
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Al usar el código anterior, la ruta de dirección URL que envía a es . La ruta de dirección URL que envía a es .
Rutas personalizadas
Use la directiva para:
- Especificar una ruta personalizada a una página. Por ejemplo, la ruta a la página Acerca de se puede establecer en con .
- Anexar segmentos a la ruta predeterminada de una página. Por ejemplo, se puede agregar un segmento "item" a la ruta predeterminada de una página con .
- Anexar parámetros a la ruta predeterminada de una página. Por ejemplo, un parámetro de identificador, , puede ser necesario para una página con .
Se admite una ruta relativa a la raíz, designada por el símbolo tilde (~) al principio de la ruta. Por ejemplo, es lo mismo que .
Si no le gusta la cadena de consulta en la dirección URL, puede cambiar la ruta para poner el nombre del controlador en la parte de la ruta de la dirección URL. Para personalizar la ruta, se puede agregar una plantilla de ruta entre comillas dobles después de la directiva .
@page "{handler?}"
@model CreateRouteModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
Al usar el código anterior, la ruta de dirección URL que envía a es . La ruta de dirección URL que envía a es .
El signo que sigue a significa que el parámetro de ruta es opcional.
Combinación de archivos JavaScript ()
La intercalación de archivos de JavaScript () para páginas y vistas es una manera cómoda de organizar scripts en una aplicación.
Coloque los archivos usando las siguientes convenciones de extensión de nombre de archivo:
- Páginas de aplicaciones Pages y vistas de aplicaciones MVC: . Examples:
- para la página de una aplicación Pages en .
- para la vista de una aplicación MVC en .
Los archivos JS colocados pueden ser direccionados públicamente a través de la ruta al archivo en el proyecto:
Páginas y vistas de un archivo de scripts colocados en la aplicación:
{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js- El marcador de posición es la ruta a la página, vista o componente.
- El marcador de posición es la página, vista o componente.
- El marcador de posición coincide con la extensión de la página, vista, o componente, ya sea .html o .json.
Ejemplo de Pages:
Un archivo para la página se coloca en la carpeta () junto a la página (). En la página, se hace referencia al script en la ruta de la carpeta:
@section Scripts { <script src="~/Pages/Index.cshtml.js"></script> }
El diseño predeterminado se puede configurar para incluir archivos colocados , lo que elimina la necesidad de configurar cada página individualmente:
<script asp-src-include="@(ViewContext.View.Path).js"></script>
La descarga de muestra utiliza el fragmento de código anterior para incluir archivos colocados en el diseño predeterminado.
Cuando se publica la aplicación, el marco mueve automáticamente el script a la raíz web. En el ejemplo anterior, el script se mueve a , donde el marcador de posición es el moniker de la plataforma de destino (TFM). No se necesita ningún cambio en la dirección URL relativa del script en la página .
Cuando se publica la aplicación, el marco mueve automáticamente el script a la raíz web. En el ejemplo anterior, el script se mueve a , donde el marcador de posición es el moniker de la plataforma de destino (TFM). No se necesita ningún cambio en la dirección URL relativa del script en el componente .
Para los scripts proporcionados por una biblioteca de clases (RCL):
_content/{PACKAGE ID}/{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js- El marcador de posición es el identificador de paquete de la RCL (o el nombre de la biblioteca para una biblioteca de clases a la que hace referencia la aplicación).
- El marcador de posición es la ruta a la página, vista o componente. Si un componente se encuentra en la raíz de la RCL, no se incluye el segmento de ruta.
- El marcador de posición es la página, la vista o el componente.
- El marcador de posición coincide con la extensión de la página, vista o componente, ya sea o .
Configuración y ajustes avanzados
La mayoría de las aplicaciones no requieren la configuración de las siguientes secciones.
Para configurar las opciones avanzadas, use la sobrecarga que configura :
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Use el elemento para establecer el directorio raíz de páginas o agregar convenciones de modelo de aplicación para las páginas. Para obtener más información sobre las convenciones, vea Convenciones de autorización de Pages.
Para precompilar vistas, consulte "compilación de vistas".
Especifique que Pages está en la raíz del contenido
De forma predeterminada, Pages se encuentra en la raíz del directorio /Pages. Agregue para especificar que las páginas están en la raíz de contenido () de la aplicación.
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Especifica que las páginas están en un directorio raíz personalizado
Agregue para especificar que Pages se encuentra en un directorio raíz personalizado en la aplicación (proporcione una ruta de acceso relativa):
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Recursos adicionales
- Consulte Empezar con la aplicación Razor Pages, que se basa en esta introducción.
- atributo en aplicaciones de Pages
- Descargar o ver código de ejemplo
- Información general de ASP.NET Core
- Razor referencia de sintaxis para ASP.NET Core
- Areas en ASP.NET Core
- Tutorial: Comienza con Razor Páginas en ASP.NET Core
- Razor Convenciones de autorización de Pages en ASP.NET Core
- Razor Convenciones de enrutamiento de páginas y aplicaciones en ASP.NET Core
- Razor Pruebas unitarias de Pages en ASP.NET Core
- Vistas parciales en ASP.NET Core
- Visual Studio 2019 16.4 o posterior con la carga de trabajo ASP.NET y desarrollo web
- SDK de .NET Core 3.1
- Visual Studio 2019 16.8 o posterior con la carga de trabajo ASP.NET y desarrollo web
- SDK de .NET 5
Crear un Razor Pages project
Consulte Comience con Razor Pages para obtener instrucciones detalladas sobre cómo crear un Razor proyecto de Pages.
Páginas
Pages está habilitado en :
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
Considere la posibilidad de una página básica:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
El código anterior se parece mucho a un archivo de vista Razor que se usa en una aplicación de ASP.NET Core con controladores y vistas. La directiva lo hace diferente. transforma el archivo en una acción de MVC, lo que significa que administra las solicitudes directamente, sin tener que pasar a través de un controlador. debe ser la primera directiva en una página. afecta al comportamiento de otros constructos. Los nombres de archivo de Pages tienen un sufijo.
Una página similar, con una clase , se muestra en los dos archivos siguientes. El archivo :
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
Modelo de página :
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
Por convención, el archivo de clase tiene el mismo nombre que el archivo de Página con adjunto. Por ejemplo, la página anterior es. El archivo que contiene la clase se denomina .
Las asociaciones de rutas de dirección URL a páginas se determinan según la ubicación de la página en el sistema de archivos. En la tabla siguiente, se muestra la ruta de página y la URL correspondiente.
| Ruta de acceso y nombre de archivo | dirección URL coincidente |
|---|---|
/Pages/Index.cshtml |
o |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
o |
Notes:
- El entorno de ejecución busca archivos de Pages en la carpeta Pages de forma predeterminada.
- es la página predeterminada cuando una URL no incluye una página.
Escritura de un formulario básico
Pages está diseñado para facilitar la implementación de patrones comunes que se usan con exploradores web al compilar una aplicación. Los enlaces de modelos, los asistentes de etiquetas y los asistentes de HTML simplemente funcionan con las propiedades definidas en una clase de Pages. Considere la posibilidad de una página que implementa un formulario básico del estilo "Póngase en contacto con nosotros" para el modelo :
Para los ejemplos de este documento, el DbContext se inicializa en el archivo Startup.cs.
La base de datos en memoria requiere el paquete NuGet .
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
services.AddRazorPages();
}
El modelo de datos:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
El contexto de la base de datos:
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Models;
namespace RazorPagesContacts.Data
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext(DbContextOptions options)
: base(options)
{
}
public DbSet<Customer> Customers { get; set; }
}
}
Archivo de visualización
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
Modelo de página :
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using RazorPagesContacts.Models;
using System.Threading.Tasks;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
Por convención, la clase se denomina y se encuentra en el mismo espacio de nombres que la página.
La clase permite la separación de la lógica de una página de su presentación. Define los controladores de página para solicitudes que se envían a la página y los datos que usan para representar la página. Esta separación permite lo siguiente:
- Administración de dependencias de página mediante la inyección de dependencias.
- Pruebas unitarias
La página tiene un método controlador, que se ejecuta en las solicitudes (cuando un usuario envía el formulario). Se pueden agregar métodos de controlador para cualquier verbo HTTP. Los controladores más comunes son:
- para inicializar el estado necesario para la página. En el código anterior, el método muestra la Página.
- para gestionar los envíos de formularios.
El sufijo de nombre es opcional, pero se usa a menudo por convención para funciones asincrónicas. El código anterior es típico de Pages.
Si está familiarizado con asp.NET aplicaciones que usan controladores y vistas:
- El código del ejemplo anterior es similar al típico código de controlador.
- La mayoría de los primitivos de MVC, como el enlace de modelos, la validación y los resultados de acciones, funcionan del mismo modo con los controladores y Pages.
El método anterior:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
El flujo básico de :
Compruebe los errores de validación.
- Si no hay ningún error, guarde los datos y redirija.
- Si hay errores, muestre la página de nuevo con mensajes de validación. En muchos casos, los errores de validación se detectan en el cliente y nunca se envían al servidor.
Archivo de visualización
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
El HTML renderizado de :
<p>Enter a customer name:</p>
<form method="post">
Name:
<input type="text" data-val="true"
data-val-length="The field Name must be a string with a maximum length of 10."
data-val-length-max="10" data-val-required="The Name field is required."
id="Customer_Name" maxlength="10" name="Customer.Name" value="" />
<input type="submit" />
<input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token here>" />
</form>
En el código anterior, el envío del formulario:
Con datos válidos:
El método del controlador llama al método auxiliar. devuelve una instancia de . :
- Es el resultado de una acción.
- Es similar a o (se usa en controladores y vistas).
- Se ha personalizado para las páginas. En el ejemplo anterior, redirige a la página de índice raíz (). se detalla en la sección Generación de direcciones URL para las páginas.
Con errores de validación que se pasan al servidor:
- El método del controlador llama al método auxiliar. devuelve una instancia de . El retorno es similar a cómo las acciones en los controladores realizan devoluciones. es el tipo de retorno predeterminado para un método de controlador. Un método de controlador que devuelve y representa la página.
- En el ejemplo anterior, la publicación del formulario sin valores hace que se devuelva ModelState.IsValid como false. En este ejemplo, no se muestra ningún error de validación en el cliente. El manejo de errores de validación se aborda más adelante en este documento.
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _context.Customers.Add(Customer); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }Con errores de validación detectados mediante la validación del lado cliente:
- Los datos no se publican en el servidor.
- La validación del lado cliente se explica más adelante en este documento.
La propiedad utiliza el atributo para habilitar el enlace de modelos.
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
no debe usarse en modelos que contengan propiedades que el cliente no debe cambiar. Para más detalles, consulte Overposting.
De forma predeterminada, Pages enlaza propiedades solo con elementos que no son verbos. El enlace a propiedades elimina la necesidad de escribir código para convertir los datos HTTP en el tipo de modelo. La vinculación reduce el código al usar la misma propiedad para renderizar los campos del formulario y aceptar la entrada de datos.
Warning
Por razones de seguridad, debe habilitar la vinculación de datos de solicitud a las propiedades del modelo de página. Compruebe las entradas de los usuarios antes de asignarlas a las propiedades. Optar por la vinculación puede ser útil al abordar escenarios que dependen de la cadena de consulta o de los valores de la ruta.
Para vincular una propiedad en solicitudes, establezca la propiedad del atributo en:
[BindProperty(SupportsGet = true)]
Para obtener más información, consulte ASP.NET Core Community Standup: Bind on GET discussion (YouTube).
Revisión del archivo de visualización
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
- En el código anterior, el asistente de etiquetas de entrada enlaza el elemento HTML con la expresión del modelo .
- hace que los asistentes de etiquetas estén disponibles.
Página principal
es la página principal:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var contact in Model.Customer)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
Clase asociada ():
public class IndexModel : PageModel
{
private readonly CustomerDbContext _context;
public IndexModel(CustomerDbContext context)
{
_context = context;
}
public IList<Customer> Customer { get; set; }
public async Task OnGetAsync()
{
Customer = await _context.Customers.ToListAsync();
}
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customers.FindAsync(id);
if (contact != null)
{
_context.Customers.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
}
El archivo contiene el siguiente marcado:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
El asistente de etiquetas delimitadoras ha usado el atributo para generar un vínculo a la página de edición. El vínculo contiene datos de ruta con el identificador del contacto. Por ejemplo: . Las aplicaciones auxiliares de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de .
El archivo contiene el marcado para crear un botón de eliminar para cada uno de los contactos de cliente.
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
El código HTML representado:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Al representar el botón de eliminar en HTML, el elemento formaction incluye parámetros para los siguientes elementos:
- Id. de contacto de cliente especificado mediante el atributo .
- especificado mediante el atributo .
Al seleccionar el botón, se envía una solicitud de formulario al servidor. De forma predeterminada, el nombre del método de control se selecciona de acuerdo con el valor del parámetro y según el esquema .
Como el término está presente en este ejemplo, se utiliza el método de controlador para procesar la solicitud . Si el valor se establece en uno diferente, como , se seleccionará un método de controlador con el nombre .
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customers.FindAsync(id);
if (contact != null)
{
_context.Customers.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
El método:
- Obtiene el elemento de la cadena de consulta.
- Realiza una consulta a la base de datos del contacto de cliente con .
- Si se encuentra el contacto del cliente, este se quita y se actualiza la base de datos.
- Instrucciones para redirigir a la página índice principal ().
El archivo Edit.cshtml
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Edit Customer - @Model.Customer.Id</h1>
<form method="post">
<div asp-validation-summary="All"></div>
<input asp-for="Customer.Id" type="hidden" />
<div>
<label asp-for="Customer.Name"></label>
<div>
<input asp-for="Customer.Name" />
<span asp-validation-for="Customer.Name"></span>
</div>
</div>
<div>
<button type="submit">Save</button>
</div>
</form>
La primera línea contiene la directiva . La restricción de ruteo indica a la página que acepte solicitudes a la misma que contengan datos de ruta. Si una solicitud a la página no contiene datos de ruta que se puedan convertir en , el tiempo de ejecución devuelve un error HTTP 404 (no encontrado). Para que el identificador sea opcional, anexe a la restricción de ruta:
@page "{id:int?}"
El archivo :
public class EditModel : PageModel
{
private readonly CustomerDbContext _context;
public EditModel(CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Customer = await _context.Customers.FindAsync(id);
if (Customer == null)
{
return RedirectToPage("./Index");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw new Exception($"Customer {Customer.Id} not found!");
}
return RedirectToPage("./Index");
}
}
Validation
Reglas de validación:
- Se especifican mediante declaración en la clase de modelo.
- Se aplican en toda la aplicación.
El espacio de nombres proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad. DataAnnotations también contiene atributos de formateo, como los que ayudan a aplicar formato y no proporcionan ninguna validación.
Considere el modelo :
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
Usando el siguiente archivo de visualización
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
El código anterior:
Incluye scripts de jQuery y validación de jQuery.
Usa los "Tag Helpers" para habilitar lo siguiente:
- Validación del lado cliente.
- Representación del error de validación.
Se genera el siguiente código HTML:
<p>Enter a customer name:</p> <form method="post"> Name: <input type="text" data-val="true" data-val-length="The field Name must be a string with a maximum length of 10." data-val-length-max="10" data-val-required="The Name field is required." id="Customer_Name" maxlength="10" name="Customer.Name" value="" /> <input type="submit" /> <input name="__RequestVerificationToken" type="hidden" value="<Antiforgery token here>" /> </form> <script src="/lib/jquery/dist/jquery.js"></script> <script src="/lib/jquery-validation/dist/jquery.validate.js"></script> <script src="/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Al publicar el formulario de creación sin un valor de nombre, se muestra el mensaje de error "El campo Nombre es obligatorio" en el formulario. Si JavaScript está habilitado en el cliente, el explorador muestra el error sin realizar la publicación en el servidor.
El atributo genera en el código HTML representado. impide que los navegadores superen la longitud máxima especificada. Si se usa una herramienta como Fiddler para editar y reproducir la publicación:
- Con el nombre de más de 10 caracteres.
- Se devuelve el mensaje de error "El nombre del campo debe ser una cadena con una longitud máxima de 10 caracteres".
Considere el modelo siguiente:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
Los atributos de validación especifican el comportamiento que se exigirá a las propiedades del modelo al que se aplican:
Los atributos y indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.
El atributo se usa para limitar los caracteres que se pueden escribir. En el código anterior, "Género":
- Debe usar únicamente letras.
- La primera letra debe estar en mayúsculas. No se permiten espacios en blanco, números ni caracteres especiales.
La "Clasificación"
- Requiere que el primer carácter sea una letra mayúscula.
- Permite caracteres especiales y números en los espacios posteriores. "PG-13" es válido para una "Clasificación", pero no es válido para un "Género".
El atributo restringe un valor a un intervalo determinado.
El atributo establece la longitud máxima de una propiedad de cadena y, opcionalmente, su longitud mínima.
Los tipos de valor (como , , , ) son intrínsecamente necesarios y no necesitan el atributo .
La página de creación del modelo muestra errores con valores no válidos.
Formulario de vista de película con varios errores de validación del lado del cliente con jQuery
Para obtener más información, consulte:
- Agregue validación a la aplicación de películas
- Validación del modelo en ASP.NET Core.
Controlar las solicitudes HEAD con un manejador OnGet de respaldo
Las solicitudes permiten recuperar los encabezados de un recurso específico. A diferencia de las solicitudes , las solicitudes no devuelven un cuerpo de respuesta.
Normalmente, se crea un controlador al que se llama para las solicitudes :
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Pages recurre a una llamada al controlador si no se define ningún controlador .
XSRF/CSRF y Pages
Las páginas están protegidas mediante validación antifalsificación. El elemento FormTagHelper inserta tokens antifalsificación en los elementos de formulario HTML.
Usar diseños, parciales, plantillas y asistentes de etiquetas con Pages
Las páginas funcionan con todas las capacidades del motor de vista. Los diseños, parciales, plantillas, asistentes de etiquetas y otros elementos funcionan de la misma manera que en las vistas convencionales.
Para simplificar esta página, aprovecharemos algunas de esas características.
Agregue una página de diseño a :
<!DOCTYPE html>
<html>
<head>
<title>RP Sample</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</head>
<body>
<a asp-page="/Index">Home</a>
<a asp-page="/Customers/Create">Create</a>
<a asp-page="/Customers/Index">Customers</a> <br />
@RenderBody()
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</body>
</html>
El diseño:
- Controla el diseño de cada página (a no ser que la página no tenga diseño).
- Importa las estructuras HTML como JavaScript y hojas de estilos.
- El contenido de la página se representa donde <...> es llamado.
Para más información, consulte la página de diseño.
La propiedad Layout se establece en :
@{
Layout = "_Layout";
}
El diseño está en la carpeta Pages/Shared. Las páginas buscan otras vistas (diseños, plantillas, parciales) de forma jerárquica, a partir de la misma carpeta que la página actual. Un diseño en la carpeta Pages/Shared se puede usar desde cualquier página dentro de la carpeta Pages.
El archivo de diseño debería ir en la carpeta Pages/Shared.
Le recomendamos que no coloque el archivo de diseño en la carpeta Views/Shared. Views/Shared es un patrón de vistas de MVC. Pages está diseñado para basarse en la jerarquía de carpetas, no en las convenciones de ruta de acceso.
Visualizar búsqueda desde una Página incluye la carpeta Páginas. Los diseños, plantillas y vistas parciales que se usan con los controladores de MVC y las vistas convencionales funcionan sin problemas.
Agregue un archivo :
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
se explica más adelante en el tutorial. La directiva incorpora los Tag Helpers integrados en todas las páginas de la carpeta Pages.
La directiva establecida en una página:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
La directiva establece el espacio de nombres de la página. La directiva no necesita incluir el espacio de nombres.
Cuando la directiva se encuentra en , el espacio de nombres especificado proporciona el prefijo del espacio de nombres generado en la página que importa la directiva . El resto del espacio de nombres generado (la parte del sufijo) es la ruta de acceso relativa separada por puntos entre la carpeta que contiene y la carpeta que contiene la página.
Por ejemplo, la clase establece explícitamente el espacio de nombres:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
El archivo establece el siguiente espacio de nombres:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
El espacio de nombres generado para la Página es el mismo que el de la clase.
también funciona con vistas convencionales.
Considere el archivo de vista :
@page
@model RazorPagesContacts.Pages.Customers.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<p>Validation: customer name:</p>
<form method="post">
<div asp-validation-summary="ModelOnly"></div>
<span asp-validation-for="Customer.Name"></span>
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
Archivo de vista actualizado con y el archivo de diseño anterior:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
En el código anterior, se importó el espacio de nombres y los Tag Helpers. El archivo de distribución importó los archivos JavaScript.
El Razor Pages starter project contiene el Pages/_ValidationScriptsPartial.cshtml, que enlaza la validación del lado cliente.
Para obtener más información sobre las vistas parciales, vea Vistas particionales en ASP.NET Core.
Generación de direcciones URL para las páginas
La página , mostrada anteriormente, usa :
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
La aplicación tiene la siguiente estructura de archivos o carpetas:
/Pages
Index.cshtmlPrivacy.cshtml/Customers
Create.cshtmlEdit.cshtmlIndex.cshtml
Las páginas y redirigen a después de una operación exitosa. La cadena ./Index es un nombre de página relativo que se usa para acceder a la página precedente. Se usa para generar direcciones URL a la página . Por ejemplo:
Url.Page("./Index", ...)<a asp-page="./Index">Customers Index Page</a>RedirectToPage("./Index")
El nombre de página absoluto se usa para generar direcciones URL en la página . Por ejemplo:
Url.Page("/Index", ...)<a asp-page="/Index">Home Index Page</a>RedirectToPage("/Index")
El nombre de la página es la ruta de acceso a la página desde la carpeta raíz /Pages, incluyendo un carácter inicial (por ejemplo, ). Los ejemplos anteriores de generación de URL ofrecen opciones mejoradas y capacidades funcionales en comparación con la codificación manual de un URL. La generación de direcciones URL usa el enrutamiento y puede generar y codificar parámetros según cómo se defina la ruta en la ruta de acceso de destino.
La generación de direcciones URL para las páginas admite nombres relativos. En la siguiente tabla, se muestra qué página de índice está seleccionada usando distintos parámetros en .
| RedirectToPage(x) | Page |
|---|---|
| RedirectToPage("/Index") | Pages/Index |
| RedirectToPage("./Index"); | Pages/Customers/Index |
| RedirectToPage("../Index") | Pages/Index |
| RedirectToPage("Index") | Pages/Customers/Index |
, , y son nombres relativos. El parámetro se combina con la ruta de acceso de la página actual para calcular el nombre de la página de destino.
La vinculación mediante nombres relativos es útil cuando se crean sitios con estructuras complejas. Cuando se usan nombres relativos para el vínculo entre las páginas de una carpeta:
- Al cambiar el nombre de una carpeta, no se rompen los vínculos relativos.
- Los vínculos no se rompen porque no incluyen el nombre de la carpeta.
Para redirigir a una página en otra área, especifique el área:
RedirectToPage("/Index", new { area = "Services" });
Para obtener más información, consulte Áreas en ASP.NET Core y Razor Convenciones de enrutamiento de páginas y aplicaciones en ASP.NET Core.
Atributo ViewData
Se pueden pasar datos a una página con . Las propiedades con el atributo tienen sus valores almacenados y cargados desde el elemento .
En el ejemplo siguiente, el elemento aplica el atributo a la propiedad :
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
En la página Acerca de, accede a la propiedad Title como una propiedad de modelo.
<h1>@Model.Title</h1>
En el diseño, el título se lee desde el diccionario ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
ASP.NET Core expone el TempData. Esta propiedad almacena datos hasta que son leídos. Los métodos y se pueden usar para examinar los datos sin que se eliminen. es útil para el redireccionamiento cuando se necesitan los datos de más de una única solicitud.
El siguiente código establece el valor de mediante :
public class CreateDotModel : PageModel
{
private readonly AppDbContext _db;
public CreateDotModel(AppDbContext db)
{
_db = db;
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./Index");
}
}
El siguiente marcado en el archivo muestra el valor de mediante .
<h3>Msg: @Model.Message</h3>
El modelo de página aplica el atributo a la propiedad .
[TempData]
public string Message { get; set; }
Para más información, consulte TempData.
Varios controladores por página
En la página siguiente se usa el asistente de etiquetas para generar marcado para dos controladores:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</form>
</body>
</html>
El formulario del ejemplo anterior tiene dos botones de envío, y cada uno de ellos usa para enviar a una dirección URL diferente. El atributo es un complemento de . genera direcciones URL que envían a cada uno de los métodos de controlador definidos por una página. no se especifica porque el ejemplo se vincula a la página actual.
Modelo de página:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateFATHModel : PageModel
{
private readonly AppDbContext _db;
public CreateFATHModel(AppDbContext db)
{
_db = db;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostJoinListAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
public async Task<IActionResult> OnPostJoinListUCAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Customer.Name = Customer.Name?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
El código anterior usa métodos de controlador nombrados. Los métodos de controlador con nombre se crean tomando el texto en el nombre después de y antes de (si existe). En el ejemplo anterior, los métodos de página son OnPostJoinListAsync y OnPostJoinListUCAsync. Quitando OnPost y Async, los nombres de controlador son y .
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Al usar el código anterior, la ruta de dirección URL que envía a es . La ruta de dirección URL que envía a es .
Rutas personalizadas
Use la directiva para:
- Especificar una ruta personalizada a una página. Por ejemplo, la ruta a la página Acerca de se puede establecer en con .
- Anexar segmentos a la ruta predeterminada de una página. Por ejemplo, se puede agregar un segmento "item" a la ruta predeterminada de una página con .
- Anexar parámetros a la ruta predeterminada de una página. Por ejemplo, un parámetro de identificador, , puede ser necesario para una página con .
Se admite una ruta relativa a la raíz designada por una tilde () al inicio de la ruta. Por ejemplo, es lo mismo que .
Si no le gusta la cadena de consulta en la dirección URL, puede cambiar la ruta para poner el nombre del controlador en la parte de la ruta de la dirección URL. Para personalizar la ruta, se puede agregar una plantilla de ruta entre comillas dobles después de la directiva .
@page "{handler?}"
@model CreateRouteModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
Al usar el código anterior, la ruta de dirección URL que envía a es . La ruta de dirección URL que envía a es .
Lo siguiente indica que el parámetro de ruta es opcional.
Configuración y ajustes avanzados
La mayoría de las aplicaciones no requieren la configuración de las siguientes secciones.
Para configurar las opciones avanzadas, use la sobrecarga que configura :
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
}
Use el elemento para establecer el directorio raíz de páginas o agregar convenciones de modelo de aplicación para las páginas. Para obtener más información sobre las convenciones, vea Convenciones de autorización de Pages.
Para precompilar vistas, consulte la sección sobre la compilación de vistas.
Especificar que Pages está en la raíz del contenido
De forma predeterminada, Pages se encuentra en la raíz del directorio /Pages. Agregue para especificar que sus elementos de Pages se encuentran en la raíz del contenido () de la aplicación:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
}
Especificar que las Páginas están en un directorio raíz personalizado
Agregue para especificar que Pages se encuentra en un directorio raíz personalizado en la aplicación (proporcione una ruta de acceso relativa):
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
}
Recursos adicionales
- Consulte Introducción a Razor Pages, que se basa en esta introducción.
- atributo en aplicaciones de Pages
- Descargar o ver código de ejemplo
- Información general de ASP.NET Core
- Razor referencia de sintaxis para ASP.NET Core
- Areas en ASP.NET Core
- Tutorial: Comienza con Razor Páginas en ASP.NET Core
- Razor Convenciones de autorización de Pages en ASP.NET Core
- Razor Convenciones de rutas de páginas y de aplicaciones en ASP.NET Core
- Razor Pruebas unitarias de páginas en ASP.NET Core
- Vistas parciales en ASP.NET Core