Introducción a las páginas de Razor en ASP.NET CoreIntroduction to Razor Pages in ASP.NET Core

Por Rick Anderson y Ryan NowakBy Rick Anderson and Ryan Nowak

Razor Pages facilita la programación de escenarios centrados en páginas y hace que resulte más productiva que con controladores y vistas.Razor Pages can make coding page-focused scenarios easier and more productive than using controllers and views.

Si busca un tutorial que use el enfoque Model-View-Controller, consulte Introducción a ASP.NET Core MVC.If you're looking for a tutorial that uses the Model-View-Controller approach, see Get started with ASP.NET Core MVC.

En este documento se proporciona una introducción a las páginas de Razor.This document provides an introduction to Razor Pages. No es un tutorial paso a paso.It's not a step by step tutorial. Si encuentra que alguna sección es demasiado avanzada, consulte Introducción a las páginas de Razor.If you find some of the sections too advanced, see Get started with Razor Pages. Para obtener información general de ASP.NET Core, vea Introducción a ASP.NET Core.For an overview of ASP.NET Core, see the Introduction to ASP.NET Core.

Requisitos previosPrerequisites

Creación de un proyecto de Razor PagesCreate a Razor Pages project

Vea Introducción a Razor Pages para obtener instrucciones detalladas sobre cómo crear un proyecto Razor Pages.See Get started with Razor Pages for detailed instructions on how to create a Razor Pages project.

Páginas de RazorRazor Pages

Páginas de Razor está habilitada en Startup.cs:Razor Pages is enabled in Startup.cs:

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: Consider a basic page:

@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 de Razor que se utiliza en una aplicación ASP.NET Core con controladores y vistas.The preceding code looks a lot like a Razor view file used in an ASP.NET Core app with controllers and views. La directiva @page lo hace diferente.What makes it different is the @page directive. @page 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.@page makes the file into an MVC action - which means that it handles requests directly, without going through a controller. @page debe ser la primera directiva de Razor de una página.@page must be the first Razor directive on a page. @page afecta al comportamiento de otras construcciones de Razor.@page affects the behavior of other Razor constructs. Los nombres de archivo de Razor Pages tienen el sufijo .cshtml.Razor Pages file names have a .cshtml suffix.

Una página similar, con una clase PageModel, se muestra en los dos archivos siguientes.A similar page, using a PageModel class, is shown in the following two files. El archivo Pages/Index2.cshtml:The Pages/Index2.cshtml file:

@page
@using RazorPagesIntro.Pages
@model Index2Model

<h2>Separate page model</h2>
<p>
    @Model.Message
</p>

Modelo de página Pages/Index2.cshtml.cs:The Pages/Index2.cshtml.cs page model:

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 PageModel tiene el mismo nombre que el archivo de páginas de Razor con .cs anexado.By convention, the PageModel class file has the same name as the Razor Page file with .cs appended. Por ejemplo, la página de Razor anterior es Pages/Index2.cshtml.For example, the previous Razor Page is Pages/Index2.cshtml. El archivo que contiene la clase PageModel se denomina Pages/Index2.cshtml.cs.The file containing the PageModel class is named Pages/Index2.cshtml.cs.

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.The associations of URL paths to pages are determined by the page's location in the file system. En la tabla siguiente, se muestra una ruta de acceso de página de Razor y la dirección URL correspondiente:The following table shows a Razor Page path and the matching URL:

Ruta de acceso y nombre de archivoFile name and path URL correspondientematching URL
/Pages/Index.cshtml/Pages/Index.cshtml / o /Index/ or /Index
/Pages/Contact.cshtml/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml/Pages/Store/Index.cshtml /Store o /Store/Index/Store or /Store/Index

Notas:Notes:

  • El runtime busca archivos de páginas de Razor en la carpeta Páginas de forma predeterminada.The runtime looks for Razor Pages files in the Pages folder by default.
  • Index es la página predeterminada cuando una URL no incluye una página.Index is the default page when a URL doesn't include a page.

Escritura de un formulario básicoWrite a basic form

Las páginas de Razor están diseñadas para facilitar la implementación de patrones comunes que se usan con exploradores web al compilar una aplicación.Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app. Los enlaces de modelos, los asistentes de etiquetas y los asistentes de HTML simplemente funcionan con las propiedades definidas en una clase de página de Razor.Model binding, Tag Helpers, and HTML helpers all just work with the properties defined in a Razor Page class. Considere la posibilidad de una página que implementa un formulario básico del estilo "Póngase en contacto con nosotros" para el modelo Contact:Consider a page that implements a basic "contact us" form for the Contact model:

Para los ejemplos de este documento, DbContext se inicializa en el archivo Startup.cs.For the samples in this document, the DbContext is initialized in the Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<CustomerDbContext>(options =>
                      options.UseInMemoryDatabase("name"));
    services.AddRazorPages();
}

El modelo de datos:The data model:

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:The db context:

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

El archivo de vista Pages/Create.cshtml:The Pages/Create.cshtml view file:

@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 Pages/Create.cshtml.cs:The Pages/Create.cshtml.cs page model:

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 PageModel se denomina <PageName>Model y se encuentra en el mismo espacio de nombres que la página.By convention, the PageModel class is called <PageName>Model and is in the same namespace as the page.

La clase PageModel permite la separación de la lógica de una página de su presentación.The PageModel class allows separation of the logic of a page from its presentation. 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.It defines page handlers for requests sent to the page and the data used to render the page. Esta separación permite lo siguiente:This separation allows:

La página tiene un método de controlador OnPostAsync, que se ejecuta en solicitudes POST (cuando un usuario envía el formulario).The page has an OnPostAsync handler method, which runs on POST requests (when a user posts the form). Se pueden agregar métodos de controlador para cualquier verbo HTTP.Handler methods for any HTTP verb can be added. Los controladores más comunes son:The most common handlers are:

  • OnGet para inicializar el estado necesario para la página.OnGet to initialize state needed for the page. En el código anterior, el método OnGet muestra la página de Razor CreateModel.cshtml.In the preceding code, the OnGet method displays the CreateModel.cshtml Razor Page.
  • OnPost para controlar los envíos del formulario.OnPost to handle form submissions.

El sufijo de nombre Async es opcional, pero se usa a menudo por convención para funciones asincrónicas.The Async naming suffix is optional but is often used by convention for asynchronous functions. El código anterior es típico de las páginas de Razor.The preceding code is typical for Razor Pages.

Si está familiarizado con las aplicaciones de ASP.NET con controladores y vistas:If you're familiar with ASP.NET apps using controllers and views:

  • El código OnPostAsync del ejemplo anterior es similar al típico código de controlador.The OnPostAsync code in the preceding example looks similar to typical controller code.
  • 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 Razor Pages.Most of the MVC primitives like model binding, validation, and action results work the same with Controllers and Razor Pages.

El método OnPostAsync anterior:The previous OnPostAsync method:

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 OnPostAsync:The basic flow of OnPostAsync:

Compruebe los errores de validación.Check for validation errors.

  • Si no hay ningún error, guarde los datos y redirija.If there are no errors, save the data and redirect.
  • Si hay errores, muestre la página de nuevo con mensajes de validación.If there are errors, show the page again with validation messages. En muchos casos, los errores de validación se detectan en el cliente y nunca se envían al servidor.In many cases, validation errors would be detected on the client, and never submitted to the server.

El archivo de vista Pages/Create.cshtml:The Pages/Create.cshtml view file:

@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 código HTML representado de Pages/Create.cshtml:The rendered HTML from Pages/Create.cshtml:

<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, la publicación del formulario:In the previous code, posting the form:

  • Con datos válidos:With valid data:

    • El método del controlador OnPostAsync llama al método auxiliar RedirectToPage.The OnPostAsync handler method calls the RedirectToPage helper method. RedirectToPage devuelve una instancia de RedirectToPageResult.RedirectToPage returns an instance of RedirectToPageResult. RedirectToPage:RedirectToPage:

      • Es el resultado de una acción.Is an action result.
      • Es similar a RedirectToAction o RedirectToRoute (se usa en controladores y vistas).Is similar to RedirectToAction or RedirectToRoute (used in controllers and views).
      • Se ha personalizado para las páginas.Is customized for pages. En el ejemplo anterior, redirige a la página de índice raíz (/Index).In the preceding sample, it redirects to the root Index page (/Index). RedirectToPage se detalla en la sección Generación de direcciones URL para las páginas.RedirectToPage is detailed in the URL generation for Pages section.
  • Con errores de validación que se pasan al servidor:With validation errors that are passed to the server:

    • El método del controlador OnPostAsync llama al método auxiliar Page.The OnPostAsync handler method calls the Page helper method. Page devuelve una instancia de PageResult.Page returns an instance of PageResult. Devolver Page es similar a cómo las acciones en los controladores devuelven View.Returning Page is similar to how actions in controllers return View. PageResult es el tipo de valor devuelto predeterminado para un método de controlador.PageResult is the default return type for a handler method. Un método de controlador que devuelve void representa la página.A handler method that returns void renders the page.
    • En el ejemplo anterior, la publicación del formulario sin valores hace que se devuelva ModelState.IsValid como false.In the preceding example, posting the form with no value results in ModelState.IsValid returning false. En este ejemplo, no se muestra ningún error de validación en el cliente.In this sample, no validation errors are displayed on the client. La entrega de errores de validación se trata más adelante en este documento.Validation error handing is covered later in this document.
    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:With validation errors detected by client side validation:

    • Los datos no se publican en el servidor.Data is not posted to the server.
    • La validación del lado cliente se explica más adelante en este documento.Client-side validation is explained later in this document.

La propiedad Customer usa el atributo [BindProperty] para participar en el enlace de modelos:The Customer property uses [BindProperty] attribute to opt in to model binding:

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");
    }
}

[BindProperty] no debe usarse en modelos que contengan propiedades que el cliente no debe cambiar.[BindProperty] should not be used on models containing properties that should not be changed by the client. Para más información, consulte Publicación excesiva.For more information, see Overposting.

De forma predeterminada, Razor Pages enlaza propiedades solo con verbos que no sean GET.Razor Pages, by default, bind properties only with non-GET verbs. El enlace a propiedades elimina la necesidad de escribir código para convertir los datos HTTP en el tipo de modelo.Binding to properties removes the need to writing code to convert HTTP data to the model type. Enlazar reduce el código al usar la misma propiedad para representar los campos de formulario (<input asp-for="Customer.Name">) y aceptar la entrada.Binding reduces code by using the same property to render form fields (<input asp-for="Customer.Name">) and accept the input.

Advertencia

Por motivos de seguridad, debe participar en el enlace de datos de solicitud GET con las propiedades del modelo de página.For security reasons, you must opt in to binding GET request data to page model properties. Compruebe las entradas de los usuarios antes de asignarlas a las propiedades.Verify user input before mapping it to properties. Si participa en el enlace de GET, le puede ser útil al trabajar con escenarios que dependan de cadenas de consultas o valores de rutas.Opting into GET binding is useful when addressing scenarios that rely on query string or route values.

Para enlazar una propiedad en solicitudes GET, establezca la propiedad SupportsGet del atributo [BindProperty] en true:To bind a property on GET requests, set the [BindProperty] attribute's SupportsGet property to true:

[BindProperty(SupportsGet = true)]

Para obtener más información, consulte Reunión de la comunidad de ASP.NET: debate sobre enlazar en GET (YouTube).For more information, see ASP.NET Core Community Standup: Bind on GET discussion (YouTube).

Revisión del archivo de vista Pages/Create.cshtml:Reviewing the Pages/Create.cshtml view file:

@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 <input asp-for="Customer.Name" /> enlaza el elemento <input> HTML con la expresión del modelo Customer.Name.In the preceding code, the input tag helper <input asp-for="Customer.Name" /> binds the HTML <input> element to the Customer.Name model expression.
  • @addTagHelper hace que los asistentes de etiquetas estén disponibles.@addTagHelper makes Tag Helpers available.

La página principalThe home page

Index.cshtml es la página principal:Index.cshtml is the home page:

@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>
            </tr>
        </thead>
        <tbody>
            @foreach (var contact in Model.Customer)
            {
                <tr>
                    <td> @contact.Id  </td>
                    <td>@contact.Name</td>
                    <td>
                        <a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
                        <button type="submit" asp-page-handler="delete"
                                asp-route-id="@contact.Id">delete
                        </button>
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <a asp-page="Create">Create New</a>
</form>

La clase PageModel asociada (Index.cshtml.cs):The associated PageModel class (Index.cshtml.cs):

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 Index.cshtml contiene el siguiente marcado:The Index.cshtml file contains the following markup:

<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |

El <a /a> asistente de etiquetas delimitadoras ha usado el atributo asp-route-{value} para generar un vínculo a la página de edición.The <a /a> Anchor Tag Helper used the asp-route-{value} attribute to generate a link to the Edit page. El vínculo contiene datos de ruta con el identificador del contacto.The link contains route data with the contact ID. Por ejemplo: https://localhost:5001/Edit/1.For example, https://localhost:5001/Edit/1. Los asistentes de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de Razor.Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files.

El archivo index.cshtml contiene marcado para crear un botón de eliminar para cada contacto de cliente:The Index.cshtml file contains markup to create a delete button for each customer contact:

<button type="submit" asp-page-handler="delete"
        asp-route-id="@contact.Id">delete

El código HTML representado:The rendered HTML:

<button type="submit" formaction="/Customers?id=1&amp;handler=delete">delete</button>

Al representar el botón de eliminar en HTML, el elemento formaction incluye parámetros para los siguientes elementos:When the delete button is rendered in HTML, its formaction includes parameters for:

  • Id. de contacto de cliente especificado mediante el atributo asp-route-id.The customer contact ID, specified by the asp-route-id attribute.
  • handler especificado mediante el atributo asp-page-handler.The handler, specified by the asp-page-handler attribute.

Al seleccionar el botón, se envía una solicitud de formulario POST al servidor.When the button is selected, a form POST request is sent to the server. De forma predeterminada, el nombre del método de control se selecciona de acuerdo con el valor del parámetro handler y según el esquema OnPost[handler]Async.By convention, the name of the handler method is selected based on the value of the handler parameter according to the scheme OnPost[handler]Async.

Como en este ejemplo handler es delete, el método de control OnPostDeleteAsync se usa para procesar la solicitud POST.Because the handler is delete in this example, the OnPostDeleteAsync handler method is used to process the POST request. Si asp-page-handler se establece en otro valor, como remove, se seleccionará un método de controlador llamado OnPostRemoveAsync.If the asp-page-handler is set to a different value, such as remove, a handler method with the name OnPostRemoveAsync is selected.

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 OnPostDeleteAsync realiza las acciones siguientes:The OnPostDeleteAsync method:

  • Obtiene el elemento id de la cadena de consulta.Gets the id from the query string.
  • Realiza una consulta a la base de datos del contacto de cliente con FindAsync.Queries the database for the customer contact with FindAsync.
  • Si se encuentra el contacto del cliente, este se quita y se actualiza la base de datos.If the customer contact is found, it's removed and the database is updated.
  • Llama a RedirectToPage para redirigir la página Index raíz (/Index).Calls RedirectToPage to redirect to the root Index page (/Index).

El archivo Edit.cshtmlThe Edit.cshtml file

@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 @page "{id:int}".The first line contains the @page "{id:int}" directive. La restricción de enrutamiento "{id:int}" indica a la página que acepte las solicitudes a la página que contienen datos de ruta int.The routing constraint"{id:int}" tells the page to accept requests to the page that contain int route data. Si una solicitud a la página no contiene datos de ruta que se puedan convertir en int, el tiempo de ejecución devuelve un error HTTP 404 (no encontrado).If a request to the page doesn't contain route data that can be converted to an int, the runtime returns an HTTP 404 (not found) error. Para que el identificador sea opcional, anexe ? a la restricción de ruta:To make the ID optional, append ? to the route constraint:

@page "{id:int?}"

El archivo Edit.cshtml.cs:The Edit.cshtml.cs file:

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");
    }

}

ValidaciónValidation

Reglas de validación:Validation rules:

  • Se especifican mediante declaración en la clase de modelo.Are declaratively specified in the model class.
  • Se aplican en toda la aplicación.Are enforced everywhere in the app.

El espacio de nombres System.ComponentModel.DataAnnotations proporciona un conjunto de atributos de validación integrados que se aplican mediante declaración a una clase o propiedad.The System.ComponentModel.DataAnnotations namespace provides a set of built-in validation attributes that are applied declaratively to a class or property. DataAnnotations también contiene atributos de formato como [DataType], que ayudan a aplicar formato y no proporcionan ninguna validación.DataAnnotations also contains formatting attributes like [DataType] that help with formatting and don't provide any validation.

Considere el modelo Customer:Consider the Customer model:

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 vista Create.cshtml:Using the following Create.cshtml view file:

@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:The preceding code:

  • Incluye scripts de validación de jQuery y jQuery.Includes jQuery and jQuery validation scripts.

  • Usa los asistentes de etiquetas <div /> y <span /> para habilitar lo siguiente:Uses the <div /> and <span /> Tag Helpers to enable:

    • Validación del lado cliente.Client-side validation.
    • Representación del error de validación.Validation error rendering.
  • Se genera el siguiente código HTML:Generates the following 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".Posting the Create form without a name value displays the error message "The Name field is required." en el formulario.on the form. Si JavaScript está habilitado en el cliente, el explorador muestra el error sin realizar la publicación en el servidor.If JavaScript is enabled on the client, the browser displays the error without posting to the server.

El atributo [StringLength(10)] genera data-val-length-max="10" en el código HTML representado.The [StringLength(10)] attribute generates data-val-length-max="10" on the rendered HTML. data-val-length-max impide que los exploradores superen la longitud máxima especificada al escribir.data-val-length-max prevents browsers from entering more than the maximum length specified. Si se usa una herramienta como Fiddler para editar y reproducir la publicación:If a tool such as Fiddler is used to edit and replay the post:

  • Con el nombre de más de 10 caracteres.With the name longer than 10.
  • Se devolverá el mensaje de error "El nombre del campo debe ser una cadena con una longitud máxima de 10 caracteres".The error message "The field Name must be a string with a maximum length of 10." .is returned.

Considere el modelo Movie siguiente:Consider the following Movie model:

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:The validation attributes specify behavior to enforce on the model properties they're applied to:

  • Los atributos Required y MinimumLength indican que una propiedad debe tener un valor, pero nada impide al usuario escribir espacios en blanco para satisfacer esta validación.The Required and MinimumLength attributes indicate that a property must have a value, but nothing prevents a user from entering white space to satisfy this validation.

  • El atributo RegularExpression se usa para limitar los caracteres que se pueden escribir.The RegularExpression attribute is used to limit what characters can be input. En el código anterior, "Género":In the preceding code, "Genre":

    • Solo debe usar letras.Must only use letters.
    • La primera letra debe estar en mayúsculas.The first letter is required to be uppercase. No se permiten espacios en blanco, números ni caracteres especiales.White space, numbers, and special characters are not allowed.
  • La "Clasificación" de RegularExpression:The RegularExpression "Rating":

    • Requiere que el primer carácter sea una letra mayúscula.Requires that the first character be an uppercase letter.
    • Permite caracteres especiales y números en los espacios posteriores.Allows special characters and numbers in subsequent spaces. "PG-13" es válido para una "Clasificación", pero se produce un error en un "Género"."PG-13" is valid for a rating, but fails for a "Genre".
  • El atributo Range restringe un valor a un intervalo determinado.The Range attribute constrains a value to within a specified range.

  • El atributo StringLength establece la longitud máxima de una propiedad de cadena y, opcionalmente, su longitud mínima.The StringLength attribute sets the maximum length of a string property, and optionally its minimum length.

  • Los tipos de valor (como decimal, int, float, DateTime) son intrínsecamente necesarios y no necesitan el atributo [Required].Value types (such as decimal, int, float, DateTime) are inherently required and don't need the [Required] attribute.

La página de creación del modelo Movie muestra errores de visualización con valores no válidos:The Create page for the Movie model shows displays errors with invalid values:

Formulario de vista de película con varios errores de validación de cliente de jQuery

Para obtener más información, consulte:For more information, see:

Control de solicitudes HEAD con un controlador OnGet de reservaHandle HEAD requests with an OnGet handler fallback

Las solicitudes HEAD permiten recuperar los encabezados de un recurso específico.HEAD requests allow retrieving the headers for a specific resource. A diferencia de las solicitudes GET, las solicitudes HEAD no devuelven un cuerpo de respuesta.Unlike GET requests, HEAD requests don't return a response body.

Normalmente, se crea un controlador OnHead al que se llama para las solicitudes HEAD:Ordinarily, an OnHead handler is created and called for HEAD requests:

public void OnHead()
{
    HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}

Razor Pages recurre a una llamada al controlador OnGet si no se define ningún controlador OnHead.Razor Pages falls back to calling the OnGet handler if no OnHead handler is defined.

XSRF/CSRF y páginas de RazorXSRF/CSRF and Razor Pages

Razor Pages está protegido mediante validación antifalsificación.Razor Pages are protected by Antiforgery validation. El elemento FormTagHelper inserta tokens antifalsificación en los elementos de formulario HTML.The FormTagHelper injects antiforgery tokens into HTML form elements.

Usar diseños, parciales, plantillas y asistentes de etiquetas con las páginas de RazorUsing Layouts, partials, templates, and Tag Helpers with Razor Pages

Las páginas funcionan con todas las características del motor de vista de Razor.Pages work with all the capabilities of the Razor view engine. Los diseños, parciales, plantillas, asistentes de etiquetas, _ViewStart.cshtml y _ViewImports.cshtml funcionan de la misma manera que lo hacen con las vistas de Razor convencionales.Layouts, partials, templates, Tag Helpers, _ViewStart.cshtml, and _ViewImports.cshtml work in the same way they do for conventional Razor views.

Para simplificar esta página, aprovecharemos algunas de esas características.Let's declutter this page by taking advantage of some of those capabilities.

Agregue una página de diseño a Pages/Shared/_Layout.cshtml:Add a layout page to Pages/Shared/_Layout.cshtml:

<!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:The Layout:

  • Controla el diseño de cada página (a no ser que la página no tenga diseño).Controls the layout of each page (unless the page opts out of layout).
  • Importa las estructuras HTML como JavaScript y hojas de estilos.Imports HTML structures such as JavaScript and stylesheets.
  • El contenido de la página de Razor se representa donde se llama a @RenderBody().The contents of the Razor page are rendered where @RenderBody() is called.

Para más información, consulte la página de diseño.For more information, see layout page.

La propiedad Layout se establece en Pages/_ViewStart.cshtml:The Layout property is set in Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

El diseño está en la carpeta Pages/Shared.The layout is in the Pages/Shared folder. 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.Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. Un diseño en la carpeta Pages/Shared se puede usar desde cualquier página de Razor en la carpeta Pages.A layout in the Pages/Shared folder can be used from any Razor page under the Pages folder.

El archivo de diseño debería ir en la carpeta Pages/Shared.The layout file should go in the Pages/Shared folder.

Le recomendamos que no coloque el archivo de diseño en la carpeta Views/Shared.We recommend you not put the layout file in the Views/Shared folder. Views/Shared es un patrón de vistas de MVC.Views/Shared is an MVC views pattern. Las páginas de Razor están diseñadas para basarse en la jerarquía de carpetas, no en las convenciones de ruta de acceso.Razor Pages are meant to rely on folder hierarchy, not path conventions.

La búsqueda de vistas de una página de Razor incluye la carpeta Pages.View search from a Razor Page includes the Pages folder. Los diseños, plantillas y parciales que se usan con los controladores de MVC y las vistas de Razor convencionales simplemente funcionan.The layouts, templates, and partials used with MVC controllers and conventional Razor views just work.

Agregue un archivo Pages/_ViewImports.cshtml:Add a Pages/_ViewImports.cshtml file:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@namespace se explica más adelante en el tutorial.@namespace is explained later in the tutorial. La directiva @addTagHelper pone los asistentes de etiquetas integradas en todas las páginas de la carpeta Pages.The @addTagHelper directive brings in the built-in Tag Helpers to all the pages in the Pages folder.

La directiva @namespace establecida en una página:The @namespace directive set on a page:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

<h2>Name space</h2>
<p>
    @Model.Message
</p>

La directiva @namespace establece el espacio de nombres de la página.The @namespace directive sets the namespace for the page. La directiva @model no necesita incluir el espacio de nombres.The @model directive doesn't need to include the namespace.

Cuando la directiva @namespace se encuentra en _ViewImports.cshtml, el espacio de nombres especificado proporciona el prefijo del espacio de nombres generado en la página que importa la directiva @namespace.When the @namespace directive is contained in _ViewImports.cshtml, the specified namespace supplies the prefix for the generated namespace in the Page that imports the @namespace directive. 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 _ViewImports.cshtml y la carpeta que contiene la página.The rest of the generated namespace (the suffix portion) is the dot-separated relative path between the folder containing _ViewImports.cshtml and the folder containing the page.

Por ejemplo, la clase PageModel Pages/Customers/Edit.cshtml.cs establece explícitamente el espacio de nombres:For example, the PageModel class Pages/Customers/Edit.cshtml.cs explicitly sets the namespace:

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.

El archivo Pages/_ViewImports.cshtml establece el espacio de nombres siguiente:The Pages/_ViewImports.cshtml file sets the following namespace:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

El espacio de nombres generado para la página de Razor Pages/Customers/Edit.cshtml es el mismo que la clase PageModel.The generated namespace for the Pages/Customers/Edit.cshtml Razor Page is the same as the PageModel class.

@namespace también funciona con las vistas de Razor convencionales.@namespace also works with conventional Razor views.

Considere el archivo de vista Pages/Create.cshtml:Consider the Pages/Create.cshtml view file:

@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 Pages/Create.cshtml con _ViewImports.cshtml y el archivo de distribución anterior:The updated Pages/Create.cshtml view file with _ViewImports.cshtml and the preceding layout file:

@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, el elemento _ViewImports. cshtml importó el espacio de nombres y los asistentes de etiquetas.In the preceding code, the _ViewImports.cshtml imported the namespace and Tag Helpers. El archivo de distribución importó los archivos JavaScript.The layout file imported the JavaScript files.

El proyecto de inicio de las páginas de Razor contiene Pages/_ValidationScriptsPartial.cshtml, que enlaza la validación del lado cliente.The Razor Pages starter project contains the Pages/_ValidationScriptsPartial.cshtml, which hooks up client-side validation.

Para más información sobre las vistas parciales, vea Vistas parciales en ASP.NET Core.For more information on partial views, see Vistas parciales en ASP.NET Core.

Generación de direcciones URL para las páginasURL generation for Pages

La página Create, mostrada anteriormente, usa RedirectToPage:The Create page, shown previously, uses RedirectToPage:

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:The app has the following file/folder structure:

  • /Pages/Pages

    • Index.cshtmlIndex.cshtml

    • Privacy.cshtmlPrivacy.cshtml

    • /Customers/Customers

      • Create.cshtmlCreate.cshtml
      • Edit.cshtmlEdit.cshtml
      • Index.cshtmlIndex.cshtml

Las páginas Pages/Customers/Create.cshtml y Pages/Customers/Edit.cshtml redirigen a Pages/Customers/Index.cshtml si la operación se realiza correctamente.The Pages/Customers/Create.cshtml and Pages/Customers/Edit.cshtml pages redirect to Pages/Customers/Index.cshtml after success. La cadena ./Index es un nombre de página relativo que se usa para acceder a la página anterior.The string ./Index is a relative page name used to access the preceding page. Se usa para generar direcciones URL a la página Pages/Customers/Index.cshtml.It is used to generate URLs to the Pages/Customers/Index.cshtml page. Por ejemplo:For example:

  • Url.Page("./Index", ...)
  • <a asp-page="./Index">Customers Index Page</a>
  • RedirectToPage("./Index")

El nombre de página absoluto /Index se usa para generar direcciones URL a la página Pages/Index.cshtml.The absolute page name /Index is used to generate URLs to the Pages/Index.cshtml page. Por ejemplo:For example:

  • Url.Page("/Index", ...)
  • <a asp-page="/Index">Home Index Page</a>
  • RedirectToPage("/Index")

El nombre de página es la ruta de acceso a la página de la carpeta raíz /Pages, incluido un / inicial, por ejemplo /Index.The page name is the path to the page from the root /Pages folder including a leading / (for example, /Index). Los ejemplos anteriores de generación de URL ofrecen opciones mejoradas y capacidades funcionales en comparación con la escritura a mano de estas.The preceding URL generation samples offer enhanced options and functional capabilities over hard-coding a 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.URL generation uses routing and can generate and encode parameters according to how the route is defined in the destination path.

La generación de direcciones URL para las páginas admite nombres relativos.URL generation for pages supports relative names. En la siguiente tabla, se muestra qué página de índice está seleccionada con diferentes parámetros RedirectToPage en Pages/Customers/Create.cshtml.The following table shows which Index page is selected using different RedirectToPage parameters in Pages/Customers/Create.cshtml.

RedirectToPage(x)RedirectToPage(x) PáginaPage
RedirectToPage("/Index")RedirectToPage("/Index") Pages/IndexPages/Index
RedirectToPage("./Index");RedirectToPage("./Index"); Pages/Customers/IndexPages/Customers/Index
RedirectToPage("../Index")RedirectToPage("../Index") Pages/IndexPages/Index
RedirectToPage("Index")RedirectToPage("Index") Pages/Customers/IndexPages/Customers/Index

RedirectToPage("Index"), RedirectToPage("./Index") y RedirectToPage("../Index") son nombres relativos.RedirectToPage("Index"), RedirectToPage("./Index"), and RedirectToPage("../Index") are relative names. El parámetro RedirectToPage se combina con la ruta de acceso de la página actual para calcular el nombre de la página de destino.The RedirectToPage parameter is combined with the path of the current page to compute the name of the destination page.

Vincular el nombre relativo es útil al crear sitios con una estructura compleja.Relative name linking is useful when building sites with a complex structure. Cuando se usan nombres relativos para el vínculo entre las páginas de una carpeta:When relative names are used to link between pages in a folder:

  • Al cambiar el nombre de una carpeta, no se rompen los vínculos relativos.Renaming a folder doesn't break the relative links.
  • Los vínculos no se rompen porque no incluyen el nombre de la carpeta.Links are not broken because they don't include the folder name.

Para redirigir a una página en otra área, especifique el área:To redirect to a page in a different Area, specify the area:

RedirectToPage("/Index", new { area = "Services" });

Para obtener más información, vea Áreas de ASP.NET Core y Convenciones de aplicación y de ruta de páginas de Razor en ASP.NET Core.For more information, see Áreas de ASP.NET Core and Convenciones de aplicación y de ruta de páginas de Razor en ASP.NET Core.

Atributo ViewDataViewData attribute

Se pueden pasar datos a una página con ViewDataAttribute.Data can be passed to a page with ViewDataAttribute. Las propiedades con el atributo [ViewData] tienen sus valores almacenados y cargados desde el elemento ViewDataDictionary.Properties with the [ViewData] attribute have their values stored and loaded from the ViewDataDictionary.

En el ejemplo siguiente, el elemento AboutModel aplica el atributo [ViewData] a la propiedad Title:In the following example, the AboutModel applies the [ViewData] attribute to the Title property:

public class AboutModel : PageModel
{
    [ViewData]
    public string Title { get; } = "About";

    public void OnGet()
    {
    }
}

En la página Acerca de, acceda a la propiedad Title como propiedad de modelo:In the About page, access the Title property as a model property:

<h1>@Model.Title</h1>

En el diseño, el título se lee desde el diccionario ViewData:In the layout, the title is read from the ViewData dictionary:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

TempDataTempData

ASP.NET Core expone el elemento TempData.ASP.NET Core exposes the TempData. Esta propiedad almacena datos hasta que se leen.This property stores data until it's read. Los métodos Keep y Peek se pueden usar para examinar los datos sin que se eliminen.The Keep and Peek methods can be used to examine the data without deletion. TempData es útil para el redireccionamiento cuando se necesitan los datos de más de una única solicitud.TempData is useful for redirection, when data is needed for more than a single request.

El siguiente código establece el valor de Message mediante TempData:The following code sets the value of Message using TempData:

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 Pages/Customers/Index.cshtml muestra el valor de Message mediante TempData.The following markup in the Pages/Customers/Index.cshtml file displays the value of Message using TempData.

<h3>Msg: @Model.Message</h3>

El modelo de página Pages/Customers/Index.cshtml.cs aplica el atributo [TempData] a la propiedad Message.The Pages/Customers/Index.cshtml.cs page model applies the [TempData] attribute to the Message property.

[TempData]
public string Message { get; set; }

Para más información, consulte TempData.For more information, see TempData.

Varios controladores por páginaMultiple handlers per page

En la página siguiente se usa el asistente de etiquetas asp-page-handler para generar marcado para dos controladores:The following page generates markup for two handlers using the asp-page-handler Tag Helper:

@page
@model CreateFATHModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

El formulario del ejemplo anterior tiene dos botones de envío, y cada uno de ellos usa FormActionTagHelper para enviar a una dirección URL diferente.The form in the preceding example has two submit buttons, each using the FormActionTagHelper to submit to a different URL. El atributo asp-page-handler es un complemento de asp-page.The asp-page-handler attribute is a companion to asp-page. asp-page-handler genera direcciones URL que envían a cada uno de los métodos de controlador definidos por una página.asp-page-handler generates URLs that submit to each of the handler methods defined by a page. asp-page no se especifica porque el ejemplo se vincula a la página actual.asp-page isn't specified because the sample is linking to the current page.

Modelo de página:The page model:

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?.ToUpper();
            return await OnPostJoinListAsync();
        }
    }
}

El código anterior usa métodos de controlador con nombre.The preceding code uses named handler methods. Los métodos de controlador con nombre se crean tomando el texto en el nombre después de On<HTTP Verb> y antes de Async (si existe).Named handler methods are created by taking the text in the name after On<HTTP Verb> and before Async (if present). En el ejemplo anterior, los métodos de página son OnPostJoinListAsync y OnPostJoinListUCAsync.In the preceding example, the page methods are OnPostJoinListAsync and OnPostJoinListUCAsync. Quitando OnPost y Async, los nombres de controlador son JoinList y JoinListUC.With OnPost and Async removed, the handler names are JoinList and JoinListUC.

<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 OnPostJoinListAsync es https://localhost:5001/Customers/CreateFATH?handler=JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinList. La ruta de dirección URL que envía a OnPostJoinListUCAsync es https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.

Rutas personalizadasCustom routes

Use la directiva @page para:Use the @page directive to:

  • Especificar una ruta personalizada a una página.Specify a custom route to a page. Por ejemplo, la ruta a la página Acerca de se puede establecer en /Some/Other/Path con @page "/Some/Other/Path".For example, the route to the About page can be set to /Some/Other/Path with @page "/Some/Other/Path".
  • Anexar segmentos a la ruta predeterminada de una página.Append segments to a page's default route. Por ejemplo, se puede agregar un segmento "item" a la ruta predeterminada de una página con @page "item".For example, an "item" segment can be added to a page's default route with @page "item".
  • Anexar parámetros a la ruta predeterminada de una página.Append parameters to a page's default route. Por ejemplo, un parámetro de identificador, id, puede ser necesario para una página con @page "{id}".For example, an ID parameter, id, can be required for a page with @page "{id}".

Se admite una ruta de acceso relativa raíz designada por una tilde (~) al principio de la ruta de acceso.A root-relative path designated by a tilde (~) at the beginning of the path is supported. Por ejemplo, @page "~/Some/Other/Path" es lo mismo que @page "/Some/Other/Path".For example, @page "~/Some/Other/Path" is the same as @page "/Some/Other/Path".

La cadena de consulta ?handler=JoinList de la dirección URL se puede cambiar por un segmento de ruta /JoinList, para lo cual hay que especificar la plantilla de ruta @page "{handler?}".You can change the query string ?handler=JoinList in the URL to a route segment /JoinList by specifying the route template @page "{handler?}".

Si no le gusta la cadena de consulta ?handler=JoinList 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.If you don't like the query string ?handler=JoinList in the URL, you can change the route to put the handler name in the path portion of the URL. Para personalizar la ruta, se puede agregar una plantilla de ruta entre comillas dobles después de la directiva @page.You can customize the route by adding a route template enclosed in double quotes after the @page directive.

@page "{handler?}"
@model CreateRouteModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></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 OnPostJoinListAsync es https://localhost:5001/Customers/CreateFATH/JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH/JoinList. La ruta de dirección URL que envía a OnPostJoinListUCAsync es https://localhost:5001/Customers/CreateFATH/JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH/JoinListUC.

El signo ? que sigue a handler significa que el parámetro de ruta es opcional.The ? following handler means the route parameter is optional.

Valores de configuración avanzadosAdvanced configuration and settings

La mayoría de las aplicaciones no requieren la configuración de las siguientes secciones.The configuration and settings in following sections is not required by most apps.

Para configurar opciones avanzadas, use el método de extensión AddRazorPagesOptions:To configure advanced options, use the extension method AddRazorPagesOptions:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages()
        .AddRazorPagesOptions(options =>
        {
            options.RootDirectory = "/MyPages";
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        });
}

Use el elemento RazorPagesOptions para establecer el directorio raíz de páginas o agregar convenciones de modelo de aplicación para las páginas.Use the RazorPagesOptions to set the root directory for pages, or add application model conventions for pages. Para obtener más información sobre las convenciones, vea Convenciones de autorización de Razor Pages.For more information on conventions, see Razor Pages authorization conventions.

Para precompilar vistas, consulte la sección sobre la compilación de vistas de Razor.To precompile views, see Razor view compilation.

Especificar que las páginas de Razor se encuentran en la raíz de contenidoSpecify that Razor Pages are at the content root

De forma predeterminada, la ruta raíz de las páginas de Razor es el directorio /Pages.By default, Razor Pages are rooted in the /Pages directory. Agregue WithRazorPagesAtContentRoot para especificar que sus Razor Pages se encuentran en la raíz de contenido (ContentRootPath) de la aplicación:Add WithRazorPagesAtContentRoot to specify that your Razor Pages are at the content root (ContentRootPath) of the app:

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages()
        .AddRazorPagesOptions(options =>
        {
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        })
        .WithRazorPagesAtContentRoot();
}

Especificar que las páginas de Razor se encuentran en un directorio raíz personalizadoSpecify that Razor Pages are at a custom root directory

Agregue WithRazorPagesRoot para especificar que Razor Pages se encuentra en un directorio raíz personalizado en la aplicación (proporcione una ruta de acceso relativa):Add WithRazorPagesRoot to specify that Razor Pages are at a custom root directory in the app (provide a relative path):

public void ConfigureServices(IServiceCollection services)
{            
    services.AddRazorPages()
        .AddRazorPagesOptions(options =>
        {
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        })
        .WithRazorPagesRoot("/path/to/razor/pages");
}

Recursos adicionalesAdditional resources

Por Rick Anderson y Ryan NowakBy Rick Anderson and Ryan Nowak

Las páginas de Razor son una nueva característica de ASP.NET Core MVC que facilita la codificación de escenarios centrados en páginas y hace que sea más productiva.Razor Pages is a new aspect of ASP.NET Core MVC that makes coding page-focused scenarios easier and more productive.

Si busca un tutorial que use el enfoque Model-View-Controller, consulte Introducción a ASP.NET Core MVC.If you're looking for a tutorial that uses the Model-View-Controller approach, see Get started with ASP.NET Core MVC.

En este documento se proporciona una introducción a las páginas de Razor.This document provides an introduction to Razor Pages. No es un tutorial paso a paso.It's not a step by step tutorial. Si encuentra que alguna sección es demasiado avanzada, consulte Introducción a las páginas de Razor.If you find some of the sections too advanced, see Get started with Razor Pages. Para obtener información general de ASP.NET Core, vea Introducción a ASP.NET Core.For an overview of ASP.NET Core, see the Introduction to ASP.NET Core.

Requisitos previosPrerequisites

Advertencia

Si usa Visual Studio 2017, consulte dotnet/sdk problema #3124 para información sobre las versiones del SDK de .NET Core que no funcionan con Visual Studio.If you use Visual Studio 2017, see dotnet/sdk issue #3124 for information about .NET Core SDK versions that don't work with Visual Studio.

Creación de un proyecto de Razor PagesCreate a Razor Pages project

Vea Introducción a Razor Pages para obtener instrucciones detalladas sobre cómo crear un proyecto Razor Pages.See Get started with Razor Pages for detailed instructions on how to create a Razor Pages project.

Páginas de RazorRazor Pages

Páginas de Razor está habilitada en Startup.cs:Razor Pages is enabled in Startup.cs:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Includes support for Razor Pages and controllers.
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc();
    }
}

Considere la posibilidad de una página básica: Consider a basic page:

@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 de Razor que se utiliza en una aplicación ASP.NET Core con controladores y vistas.The preceding code looks a lot like a Razor view file used in an ASP.NET Core app with controllers and views. La directiva @page lo hace diferente.What makes it different is the @page directive. @page 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.@page makes the file into an MVC action - which means that it handles requests directly, without going through a controller. @page debe ser la primera directiva de Razor de una página.@page must be the first Razor directive on a page. @page afecta al comportamiento de otras construcciones de Razor.@page affects the behavior of other Razor constructs.

Una página similar, con una clase PageModel, se muestra en los dos archivos siguientes.A similar page, using a PageModel class, is shown in the following two files. El archivo Pages/Index2.cshtml:The Pages/Index2.cshtml file:

@page
@using RazorPagesIntro.Pages
@model IndexModel2

<h2>Separate page model</h2>
<p>
    @Model.Message
</p>

Modelo de página Pages/Index2.cshtml.cs:The Pages/Index2.cshtml.cs page model:

using Microsoft.AspNetCore.Mvc.RazorPages;
using System;

namespace RazorPagesIntro.Pages
{
    public class IndexModel2 : 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 PageModel tiene el mismo nombre que el archivo de páginas de Razor con .cs anexado.By convention, the PageModel class file has the same name as the Razor Page file with .cs appended. Por ejemplo, la página de Razor anterior es Pages/Index2.cshtml.For example, the previous Razor Page is Pages/Index2.cshtml. El archivo que contiene la clase PageModel se denomina Pages/Index2.cshtml.cs.The file containing the PageModel class is named Pages/Index2.cshtml.cs.

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.The associations of URL paths to pages are determined by the page's location in the file system. En la tabla siguiente, se muestra una ruta de acceso de página de Razor y la dirección URL correspondiente:The following table shows a Razor Page path and the matching URL:

Ruta de acceso y nombre de archivoFile name and path URL correspondientematching URL
/Pages/Index.cshtml/Pages/Index.cshtml / o /Index/ or /Index
/Pages/Contact.cshtml/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml/Pages/Store/Index.cshtml /Store o /Store/Index/Store or /Store/Index

Notas:Notes:

  • El runtime busca archivos de páginas de Razor en la carpeta Páginas de forma predeterminada.The runtime looks for Razor Pages files in the Pages folder by default.
  • Index es la página predeterminada cuando una URL no incluye una página.Index is the default page when a URL doesn't include a page.

Escritura de un formulario básicoWrite a basic form

Las páginas de Razor están diseñadas para facilitar la implementación de patrones comunes que se usan con exploradores web al compilar una aplicación.Razor Pages is designed to make common patterns used with web browsers easy to implement when building an app. Los enlaces de modelos, los asistentes de etiquetas y los asistentes de HTML simplemente funcionan con las propiedades definidas en una clase de página de Razor.Model binding, Tag Helpers, and HTML helpers all just work with the properties defined in a Razor Page class. Considere la posibilidad de una página que implementa un formulario básico del estilo "Póngase en contacto con nosotros" para el modelo Contact:Consider a page that implements a basic "contact us" form for the Contact model:

Para los ejemplos de este documento, DbContext se inicializa en el archivo Startup.cs.For the samples in this document, the DbContext is initialized in the Startup.cs file.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RazorPagesContacts.Data;

namespace RazorPagesContacts
{
    public class Startup
    {
        public IHostingEnvironment HostingEnvironment { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<AppDbContext>(options =>
                              options.UseInMemoryDatabase("name"));
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc();
        }
    }
}

El modelo de datos:The data model:

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Data
{
    public class Customer
    {
        public int Id { get; set; }

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

El contexto de la base de datos:The db context:

using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions options)
            : base(options)
        {
        }

        public DbSet<Customer> Customers { get; set; }
    }
}

El archivo de vista Pages/Create.cshtml:The Pages/Create.cshtml view file:

@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

Modelo de página Pages/Create.cshtml.cs:The Pages/Create.cshtml.cs page model:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages
{
    public class CreateModel : PageModel
    {
        private readonly AppDbContext _db;

        public CreateModel(AppDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage("/Index");
        }
    }
}

Por convención, la clase PageModel se denomina <PageName>Model y se encuentra en el mismo espacio de nombres que la página.By convention, the PageModel class is called <PageName>Model and is in the same namespace as the page.

La clase PageModel permite la separación de la lógica de una página de su presentación.The PageModel class allows separation of the logic of a page from its presentation. 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.It defines page handlers for requests sent to the page and the data used to render the page. Esta separación permite lo siguiente:This separation allows:

La página tiene un método de controlador OnPostAsync, que se ejecuta en solicitudes POST (cuando un usuario envía el formulario).The page has an OnPostAsync handler method, which runs on POST requests (when a user posts the form). Puede agregar métodos de controlador para cualquier verbo HTTP.You can add handler methods for any HTTP verb. Los controladores más comunes son:The most common handlers are:

  • OnGet para inicializar el estado necesario para la página.OnGet to initialize state needed for the page. Ejemplo OnGet.OnGet sample.
  • OnPost para controlar los envíos del formulario.OnPost to handle form submissions.

El sufijo de nombre Async es opcional, pero se usa a menudo por convención para funciones asincrónicas.The Async naming suffix is optional but is often used by convention for asynchronous functions. El código anterior es típico de las páginas de Razor.The preceding code is typical for Razor Pages.

Si está familiarizado con las aplicaciones de ASP.NET con controladores y vistas:If you're familiar with ASP.NET apps using controllers and views:

  • El código OnPostAsync del ejemplo anterior es similar al típico código de controlador.The OnPostAsync code in the preceding example looks similar to typical controller code.
  • La mayoría de los primitivos MVC como el enlace de modelos, la validación, la validación y los resultados de acciones se comparten.Most of the MVC primitives like model binding, validation, Validation, and action results are shared.

El método OnPostAsync anterior:The previous OnPostAsync method:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _db.Customers.Add(Customer);
    await _db.SaveChangesAsync();
    return RedirectToPage("/Index");
}

El flujo básico de OnPostAsync:The basic flow of OnPostAsync:

Compruebe los errores de validación.Check for validation errors.

  • Si no hay ningún error, guarde los datos y redirija.If there are no errors, save the data and redirect.
  • Si hay errores, muestre la página de nuevo con mensajes de validación.If there are errors, show the page again with validation messages. La validación del lado cliente es idéntica a las aplicaciones de ASP.NET Core MVC tradicionales.Client-side validation is identical to traditional ASP.NET Core MVC applications. En muchos casos, los errores de validación se detectan en el cliente y nunca se envían al servidor.In many cases, validation errors would be detected on the client, and never submitted to the server.

Cuando los datos se escriben correctamente, el método del controlador OnPostAsync llama al método del asistente RedirectToPage para devolver una instancia de RedirectToPageResult.When the data is entered successfully, the OnPostAsync handler method calls the RedirectToPage helper method to return an instance of RedirectToPageResult. RedirectToPage es un resultado de acción nueva, similar a RedirectToAction o RedirectToRoute, pero personalizada para las páginas.RedirectToPage is a new action result, similar to RedirectToAction or RedirectToRoute, but customized for pages. En el ejemplo anterior, redirige a la página de índice raíz (/Index).In the preceding sample, it redirects to the root Index page (/Index). RedirectToPage se detalla en la sección Generación de direcciones URL para las páginas.RedirectToPage is detailed in the URL generation for Pages section.

Cuando el formulario enviado tiene errores de validación (que se pasan al servidor), el método del controlador OnPostAsync llama al método del asistente Page.When the submitted form has validation errors (that are passed to the server), theOnPostAsync handler method calls the Page helper method. Page devuelve una instancia de PageResult.Page returns an instance of PageResult. Devolver Page es similar a cómo las acciones en los controladores devuelven View.Returning Page is similar to how actions in controllers return View. PageResult es el tipo de valor devuelto predeterminado para un método de controlador.PageResult is the default return type for a handler method. Un método de controlador que devuelve void representa la página.A handler method that returns void renders the page.

La propiedad Customer usa el atributo [BindProperty] para participar en el enlace de modelos.The Customer property uses [BindProperty] attribute to opt in to model binding.

public class CreateModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateModel(AppDbContext db)
    {
        _db = db;
    }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        return RedirectToPage("/Index");
    }
}

De forma predeterminada, Razor Pages enlaza propiedades solo con verbos que no sean GET.Razor Pages, by default, bind properties only with non-GET verbs. Enlazar a propiedades puede reducir la cantidad de código que se debe escribir.Binding to properties can reduce the amount of code you have to write. Enlazar reduce el código al usar la misma propiedad para representar los campos de formulario (<input asp-for="Customer.Name">) y aceptar la entrada.Binding reduces code by using the same property to render form fields (<input asp-for="Customer.Name">) and accept the input.

Advertencia

Por motivos de seguridad, debe participar en el enlace de datos de solicitud GET con las propiedades del modelo de página.For security reasons, you must opt in to binding GET request data to page model properties. Compruebe las entradas de los usuarios antes de asignarlas a las propiedades.Verify user input before mapping it to properties. Si participa en el enlace de GET, le puede ser útil al trabajar con escenarios que dependan de cadenas de consultas o valores de rutas.Opting into GET binding is useful when addressing scenarios that rely on query string or route values.

Para enlazar una propiedad en solicitudes GET, establezca la propiedad SupportsGet del atributo [BindProperty] en true:To bind a property on GET requests, set the [BindProperty] attribute's SupportsGet property to true:

[BindProperty(SupportsGet = true)]

Para obtener más información, consulte Reunión de la comunidad de ASP.NET: debate sobre enlazar en GET (YouTube).For more information, see ASP.NET Core Community Standup: Bind on GET discussion (YouTube).

La página principal (Index.cshtml):The home page (Index.cshtml):

@page
@model RazorPagesContacts.Pages.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<h1>Contacts</h1>
<form method="post">
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var contact in Model.Customers)
            {
                <tr>
                    <td>@contact.Id</td>
                    <td>@contact.Name</td>
                    <td>
                        <a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>
                        <button type="submit" asp-page-handler="delete" 
                                asp-route-id="@contact.Id">delete</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>

    <a asp-page="./Create">Create</a>
</form>

La clase PageModel asociada (Index.cshtml.cs):The associated PageModel class (Index.cshtml.cs):

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Pages
{
    public class IndexModel : PageModel
    {
        private readonly AppDbContext _db;

        public IndexModel(AppDbContext db)
        {
            _db = db;
        }

        public IList<Customer> Customers { get; private set; }

        public async Task OnGetAsync()
        {
            Customers = await _db.Customers.AsNoTracking().ToListAsync();
        }

        public async Task<IActionResult> OnPostDeleteAsync(int id)
        {
            var contact = await _db.Customers.FindAsync(id);

            if (contact != null)
            {
                _db.Customers.Remove(contact);
                await _db.SaveChangesAsync();
            }

            return RedirectToPage();
        }
    }
}

El archivo Index.cshtml contiene el siguiente marcado para crear un vínculo de edición para cada contacto:The Index.cshtml file contains the following markup to create an edit link for each contact:

<a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>

El <a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> asistente de etiquetas delimitadoras ha usado el atributo asp-route-{value} para generar un vínculo a la página de edición.The <a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> Anchor Tag Helper used the asp-route-{value} attribute to generate a link to the Edit page. El vínculo contiene datos de ruta con el identificador del contacto.The link contains route data with the contact ID. Por ejemplo: https://localhost:5001/Edit/1.For example, https://localhost:5001/Edit/1. Los asistentes de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de Razor.Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files. Los asistentes de etiquetas se habilitan mediante @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpersTag Helpers are enabled by @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

El archivo Pages/Edit.cshtml:The Pages/Edit.cshtml file:

@page "{id:int}"
@model RazorPagesContacts.Pages.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

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

<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 @page "{id:int}".The first line contains the @page "{id:int}" directive. La restricción de enrutamiento "{id:int}" indica a la página que acepte las solicitudes a la página que contienen datos de ruta int.The routing constraint"{id:int}" tells the page to accept requests to the page that contain int route data. Si una solicitud a la página no contiene datos de ruta que se puedan convertir en int, el tiempo de ejecución devuelve un error HTTP 404 (no encontrado).If a request to the page doesn't contain route data that can be converted to an int, the runtime returns an HTTP 404 (not found) error. Para que el identificador sea opcional, anexe ? a la restricción de ruta:To make the ID optional, append ? to the route constraint:

@page "{id:int?}"

El archivo Pages/Edit.cshtml.cs:The Pages/Edit.cshtml.cs file:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnGetAsync(int id)
        {
            Customer = await _db.Customers.FindAsync(id);

            if (Customer == null)
            {
                return RedirectToPage("/Index");
            }

            return Page();
        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Attach(Customer).State = EntityState.Modified;

            try
            {
                await _db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                throw new Exception($"Customer {Customer.Id} not found!");
            }

            return RedirectToPage("/Index");
        }
    }
}

El archivo index.cshtml también contiene una marca para crear un botón de eliminar para cada contacto de cliente:The Index.cshtml file also contains markup to create a delete button for each customer contact:

<button type="submit" asp-page-handler="delete" 
        asp-route-id="@contact.Id">delete</button>

Al representar dicho botón de eliminar en HTML, formaction incluye parámetros para:When the delete button is rendered in HTML, its formaction includes parameters for:

  • Id. de contacto de cliente especificado mediante el atributo asp-route-id.The customer contact ID specified by the asp-route-id attribute.
  • handler especificado mediante el atributo asp-page-handler.The handler specified by the asp-page-handler attribute.

Aquí tiene un ejemplo de un botón de eliminar representado con un id. de contacto de cliente de 1:Here is an example of a rendered delete button with a customer contact ID of 1:

<button type="submit" formaction="/?id=1&amp;handler=delete">delete</button>

Al seleccionar el botón, se envía una solicitud de formulario POST al servidor.When the button is selected, a form POST request is sent to the server. De forma predeterminada, el nombre del método de control se selecciona de acuerdo con el valor del parámetro handler y según el esquema OnPost[handler]Async.By convention, the name of the handler method is selected based on the value of the handler parameter according to the scheme OnPost[handler]Async.

Como en este ejemplo handler es delete, el método de control OnPostDeleteAsync se usa para procesar la solicitud POST.Because the handler is delete in this example, the OnPostDeleteAsync handler method is used to process the POST request. Si asp-page-handler se establece en otro valor, como remove, se seleccionará un método de controlador llamado OnPostRemoveAsync.If the asp-page-handler is set to a different value, such as remove, a handler method with the name OnPostRemoveAsync is selected. En el código siguiente se muestra el controlador OnPostDeleteAsync:The following code shows the OnPostDeleteAsync handler:

public async Task<IActionResult> OnPostDeleteAsync(int id)
{
    var contact = await _db.Customers.FindAsync(id);

    if (contact != null)
    {
        _db.Customers.Remove(contact);
        await _db.SaveChangesAsync();
    }

    return RedirectToPage();
}

El método OnPostDeleteAsync realiza las acciones siguientes:The OnPostDeleteAsync method:

  • Acepta el elemento id de la cadena de consulta.Accepts the id from the query string. Si la directiva de página Index.cshtml contuviera la restricción de enrutamiento "{id:int?}", id provendría de los datos de la ruta.If the Index.cshtml page directive contained routing constraint "{id:int?}", id would come from route data. Los datos de la ruta de id se especifican en el URI. Por ejemplo, https://localhost:5001/Customers/2.The route data for id is specified in the URI such as https://localhost:5001/Customers/2.
  • Realiza una consulta a la base de datos del contacto de cliente con FindAsync.Queries the database for the customer contact with FindAsync.
  • Si encuentra dicho contacto de cliente, se quita de la lista correspondiente.If the customer contact is found, they're removed from the list of customer contacts. Luego, se actualiza la base de datos.The database is updated.
  • Llama a RedirectToPage para redirigir la página Index raíz (/Index).Calls RedirectToPage to redirect to the root Index page (/Index).

Marcado de las propiedades de página según sea necesarioMark page properties as required

Las propiedades de un valor PageModel se pueden decorar con el atributo Required:Properties on a PageModel can be decorated with the Required attribute:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel.DataAnnotations;

namespace RazorPagesMovie.Pages.Movies
{
    public class CreateModel : PageModel
    {
        public IActionResult OnGet()
        {
            return Page();
        }

        [BindProperty]
        [Required(ErrorMessage = "Color is required")]
        public string Color { get; set; }

        public IActionResult OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            // Process color.

            return RedirectToPage("./Index");
        }
    }
}

Para obtener más información, vea Validación de modelos.For more information, see Model validation.

Control de solicitudes HEAD con un controlador OnGet de reservaHandle HEAD requests with an OnGet handler fallback

Las solicitudes HEAD permiten recuperar los encabezados de un recurso específico.HEAD requests allow you to retrieve the headers for a specific resource. A diferencia de las solicitudes GET, las solicitudes HEAD no devuelven un cuerpo de respuesta.Unlike GET requests, HEAD requests don't return a response body.

Normalmente, se crea un controlador OnHead al que se llama para las solicitudes HEAD:Ordinarily, an OnHead handler is created and called for HEAD requests:

public void OnHead()
{
    HttpContext.Response.Headers.Add("HandledBy", "Handled by OnHead!");
}

En ASP.NET Core 2.1 o versiones posteriores, Razor Pages recurre a una llamada al controlador OnGet si no se define ningún controlador OnHead.In ASP.NET Core 2.1 or later, Razor Pages falls back to calling the OnGet handler if no OnHead handler is defined. Este comportamiento se habilita mediante la llamada a SetCompatibilityVersion en Startup.ConfigureServices:This behavior is enabled by the call to SetCompatibilityVersion in Startup.ConfigureServices:

services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Las plantillas predeterminadas generan la llamada SetCompatibilityVersion en ASP.NET Core 2.1 y 2.2.The default templates generate the SetCompatibilityVersion call in ASP.NET Core 2.1 and 2.2. SetCompatibilityVersion define con eficacia la opción de las páginas de Razor AllowMappingHeadRequestsToGetHandler como true.SetCompatibilityVersion effectively sets the Razor Pages option AllowMappingHeadRequestsToGetHandler to true.

En lugar de aceptar todos los comportamientos con SetCompatibilityVersion, puede aceptar explícitamente comportamientos específicos.Rather than opting in to all behaviors with SetCompatibilityVersion, you can explicitly opt in to specific behaviors. El código siguiente permite que las solicitudes HEAD se asignen al controlador OnGet:The following code opts in to allowing HEAD requests to be mapped to the OnGet handler:

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        options.AllowMappingHeadRequestsToGetHandler = true;
    });

XSRF/CSRF y páginas de RazorXSRF/CSRF and Razor Pages

No tiene que escribir ningún código para la validación antifalsificación.You don't have to write any code for antiforgery validation. La validación y generación de tokens antifalsificación se incluyen automáticamente en las páginas de Razor.Antiforgery token generation and validation are automatically included in Razor Pages.

Usar diseños, parciales, plantillas y asistentes de etiquetas con las páginas de RazorUsing Layouts, partials, templates, and Tag Helpers with Razor Pages

Las páginas funcionan con todas las características del motor de vista de Razor.Pages work with all the capabilities of the Razor view engine. Los diseños, parciales, plantillas, asistentes de etiquetas, _ViewStart.cshtml, _ViewImports.cshtml funcionan de la misma manera que lo hacen con las vistas de Razor convencionales.Layouts, partials, templates, Tag Helpers, _ViewStart.cshtml, _ViewImports.cshtml work in the same way they do for conventional Razor views.

Para simplificar esta página, aprovecharemos algunas de esas características.Let's declutter this page by taking advantage of some of those capabilities.

Agregue una página de diseño a Pages/Shared/_Layout.cshtml:Add a layout page to Pages/Shared/_Layout.cshtml:

<!DOCTYPE html>
<html>
<head> 
    <title>Razor Pages Sample</title>      
</head>
<body>    
   <a asp-page="/Index">Home</a>
    @RenderBody()  
    <a asp-page="/Customers/Create">Create</a> <br />
</body>
</html>

El diseño:The Layout:

  • Controla el diseño de cada página (a no ser que la página no tenga diseño).Controls the layout of each page (unless the page opts out of layout).
  • Importa las estructuras HTML como JavaScript y hojas de estilos.Imports HTML structures such as JavaScript and stylesheets.

Vea Layout page (Página de diseño) para obtener más información.See layout page for more information.

La propiedad Layout se establece en Pages/_ViewStart.cshtml:The Layout property is set in Pages/_ViewStart.cshtml:

@{
    Layout = "_Layout";
}

El diseño está en la carpeta Pages/Shared.The layout is in the Pages/Shared folder. 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.Pages look for other views (layouts, templates, partials) hierarchically, starting in the same folder as the current page. Un diseño en la carpeta Pages/Shared se puede usar desde cualquier página de Razor en la carpeta Pages.A layout in the Pages/Shared folder can be used from any Razor page under the Pages folder.

El archivo de diseño debería ir en la carpeta Pages/Shared.The layout file should go in the Pages/Shared folder.

Le recomendamos que no coloque el archivo de diseño en la carpeta Views/Shared.We recommend you not put the layout file in the Views/Shared folder. Views/Shared es un patrón de vistas de MVC.Views/Shared is an MVC views pattern. Las páginas de Razor están diseñadas para basarse en la jerarquía de carpetas, no en las convenciones de ruta de acceso.Razor Pages are meant to rely on folder hierarchy, not path conventions.

La búsqueda de vistas de una página de Razor incluye la carpeta Pages.View search from a Razor Page includes the Pages folder. Los diseños, plantillas y parciales que usa con los controladores de MVC y las vistas de Razor convencionales simplemente funcionan.The layouts, templates, and partials you're using with MVC controllers and conventional Razor views just work.

Agregue un archivo Pages/_ViewImports.cshtml:Add a Pages/_ViewImports.cshtml file:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@namespace se explica más adelante en el tutorial.@namespace is explained later in the tutorial. La directiva @addTagHelper pone los asistentes de etiquetas integradas en todas las páginas de la carpeta Pages.The @addTagHelper directive brings in the built-in Tag Helpers to all the pages in the Pages folder.

Cuando la directiva @namespace se usa explícitamente en una página:When the @namespace directive is used explicitly on a page:

@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.The directive sets the namespace for the page. La directiva @model no necesita incluir el espacio de nombres.The @model directive doesn't need to include the namespace.

Cuando la directiva @namespace se encuentra en _ViewImports.cshtml, el espacio de nombres especificado proporciona el prefijo del espacio de nombres generado en la página que importa la directiva @namespace.When the @namespace directive is contained in _ViewImports.cshtml, the specified namespace supplies the prefix for the generated namespace in the Page that imports the @namespace directive. 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 _ViewImports.cshtml y la carpeta que contiene la página.The rest of the generated namespace (the suffix portion) is the dot-separated relative path between the folder containing _ViewImports.cshtml and the folder containing the page.

Por ejemplo, la clase PageModel Pages/Customers/Edit.cshtml.cs establece explícitamente el espacio de nombres:For example, the PageModel class Pages/Customers/Edit.cshtml.cs explicitly sets the namespace:

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.

El archivo Pages/_ViewImports.cshtml establece el espacio de nombres siguiente:The Pages/_ViewImports.cshtml file sets the following namespace:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

El espacio de nombres generado para la página de Razor Pages/Customers/Edit.cshtml es el mismo que la clase PageModel.The generated namespace for the Pages/Customers/Edit.cshtml Razor Page is the same as the PageModel class.

@namespace también funciona con las vistas de Razor convencionales.@namespace also works with conventional Razor views.

El archivo de vista Pages/Create.cshtml original:The original Pages/Create.cshtml view file:

@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

El archivo de vista Pages/Create.cshtml actualizado:The updated Pages/Create.cshtml view file:

@page
@model CreateModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

El proyecto de inicio de las páginas de Razor contiene Pages/_ValidationScriptsPartial.cshtml, que enlaza la validación del lado cliente.The Razor Pages starter project contains the Pages/_ValidationScriptsPartial.cshtml, which hooks up client-side validation.

Para más información sobre las vistas parciales, vea Vistas parciales en ASP.NET Core.For more information on partial views, see Vistas parciales en ASP.NET Core.

Generación de direcciones URL para las páginasURL generation for Pages

La página Create, mostrada anteriormente, usa RedirectToPage:The Create page, shown previously, uses RedirectToPage:

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _db.Customers.Add(Customer);
    await _db.SaveChangesAsync();
    return RedirectToPage("/Index");
}

La aplicación tiene la siguiente estructura de archivos o carpetas:The app has the following file/folder structure:

  • /Pages/Pages

    • Index.cshtmlIndex.cshtml

    • /Customers/Customers

      • Create.cshtmlCreate.cshtml
      • Edit.cshtmlEdit.cshtml
      • Index.cshtmlIndex.cshtml

Las páginas Pages/Customers/Create.cshtml y Pages/Customers/Edit.cshtml redirigen a Pages/Index.cshtml si se realiza correctamente.The Pages/Customers/Create.cshtml and Pages/Customers/Edit.cshtml pages redirect to Pages/Index.cshtml after success. La cadena /Index forma parte del URI para tener acceso a la página anterior.The string /Index is part of the URI to access the preceding page. La cadena /Index puede usarse para generar los URI para la página Pages/Index.cshtml.The string /Index can be used to generate URIs to the Pages/Index.cshtml page. Por ejemplo:For example:

  • Url.Page("/Index", ...)
  • <a asp-page="/Index">My Index Page</a>
  • RedirectToPage("/Index")

El nombre de página es la ruta de acceso a la página de la carpeta raíz /Pages, incluido un / inicial, por ejemplo /Index.The page name is the path to the page from the root /Pages folder including a leading / (for example, /Index). Los ejemplos anteriores de generación de URL ofrecen opciones mejoradas y capacidades funcionales en comparación con la escritura a mano de estas.The preceding URL generation samples offer enhanced options and functional capabilities over hardcoding a 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.URL generation uses routing and can generate and encode parameters according to how the route is defined in the destination path.

La generación de direcciones URL para las páginas admite nombres relativos.URL generation for pages supports relative names. En la siguiente tabla, se muestra qué página de índice está seleccionada con diferentes parámetros RedirectToPage de Pages/Customers/Create.cshtml:The following table shows which Index page is selected with different RedirectToPage parameters from Pages/Customers/Create.cshtml:

RedirectToPage(x)RedirectToPage(x) PáginaPage
RedirectToPage("/Index")RedirectToPage("/Index") Pages/IndexPages/Index
RedirectToPage("./Index");RedirectToPage("./Index"); Pages/Customers/IndexPages/Customers/Index
RedirectToPage("../Index")RedirectToPage("../Index") Pages/IndexPages/Index
RedirectToPage("Index")RedirectToPage("Index") Pages/Customers/IndexPages/Customers/Index

RedirectToPage("Index"), RedirectToPage("./Index") y RedirectToPage("../Index") son nombres relativos.RedirectToPage("Index"), RedirectToPage("./Index"), and RedirectToPage("../Index") are relative names. El parámetro RedirectToPage se combina con la ruta de acceso de la página actual para calcular el nombre de la página de destino.The RedirectToPage parameter is combined with the path of the current page to compute the name of the destination page.

Vincular el nombre relativo es útil al crear sitios con una estructura compleja.Relative name linking is useful when building sites with a complex structure. Si usa nombres relativos para vincular entre páginas en una carpeta, puede cambiar el nombre de esa carpeta.If you use relative names to link between pages in a folder, you can rename that folder. Todos los vínculos seguirán funcionando (porque no incluyen el nombre de carpeta).All the links still work (because they didn't include the folder name).

Para redirigir a una página en otra área, especifique el área:To redirect to a page in a different Area, specify the area:

RedirectToPage("/Index", new { area = "Services" });

Para más información, consulte Áreas de ASP.NET Core.For more information, see Áreas de ASP.NET Core.

Atributo ViewDataViewData attribute

Se pueden pasar datos a una página con ViewDataAttribute.Data can be passed to a page with ViewDataAttribute. Los valores de las propiedades de controladores o modelos de página de Razor decoradas con [ViewData] se almacenan y cargan desde ViewDataDictionary.Properties on controllers or Razor Page models decorated with [ViewData] have their values stored and loaded from the ViewDataDictionary.

En el ejemplo siguiente, el valor AboutModel contiene una propiedad Title decorada con [ViewData].In the following example, the AboutModel contains a Title property decorated with [ViewData]. La propiedad Title se establece en el título de la página Acerca de:The Title property is set to the title of the About page:

public class AboutModel : PageModel
{
    [ViewData]
    public string Title { get; } = "About";

    public void OnGet()
    {
    }
}

En la página Acerca de, acceda a la propiedad Title como propiedad de modelo:In the About page, access the Title property as a model property:

<h1>@Model.Title</h1>

En el diseño, el título se lee desde el diccionario ViewData:In the layout, the title is read from the ViewData dictionary:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@ViewData["Title"] - WebApplication</title>
    ...

TempDataTempData

ASP.NET Core expone la propiedad TempData en un controlador.ASP.NET Core exposes the TempData property on a controller. Esta propiedad almacena datos hasta que se leen.This property stores data until it's read. Los métodos Keep y Peek se pueden usar para examinar los datos sin que se eliminen.The Keep and Peek methods can be used to examine the data without deletion. TempData es útil para el redireccionamiento cuando se necesitan los datos de más de una única solicitud.TempData is useful for redirection, when data is needed for more than a single request.

El siguiente código establece el valor de Message mediante TempData:The following code sets the value of Message using TempData:

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 Pages/Customers/Index.cshtml muestra el valor de Message mediante TempData.The following markup in the Pages/Customers/Index.cshtml file displays the value of Message using TempData.

<h3>Msg: @Model.Message</h3>

El modelo de página Pages/Customers/Index.cshtml.cs aplica el atributo [TempData] a la propiedad Message.The Pages/Customers/Index.cshtml.cs page model applies the [TempData] attribute to the Message property.

[TempData]
public string Message { get; set; }

Para obtener más información, vea TempData.For more information, see TempData .

Varios controladores por páginaMultiple handlers per page

En la página siguiente se usa el asistente de etiquetas asp-page-handler para generar marcado para dos controladores:The following page generates markup for two handlers using the asp-page-handler Tag Helper:

@page
@model CreateFATHModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

El formulario del ejemplo anterior tiene dos botones de envío, y cada uno de ellos usa FormActionTagHelper para enviar a una dirección URL diferente.The form in the preceding example has two submit buttons, each using the FormActionTagHelper to submit to a different URL. El atributo asp-page-handler es un complemento de asp-page.The asp-page-handler attribute is a companion to asp-page. asp-page-handler genera direcciones URL que envían a cada uno de los métodos de controlador definidos por una página.asp-page-handler generates URLs that submit to each of the handler methods defined by a page. asp-page no se especifica porque el ejemplo se vincula a la página actual.asp-page isn't specified because the sample is linking to the current page.

Modelo de página:The page model:

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?.ToUpper();
            return await OnPostJoinListAsync();
        }
    }
}

El código anterior usa métodos de controlador con nombre.The preceding code uses named handler methods. Los métodos de controlador con nombre se crean tomando el texto en el nombre después de On<HTTP Verb> y antes de Async (si existe).Named handler methods are created by taking the text in the name after On<HTTP Verb> and before Async (if present). En el ejemplo anterior, los métodos de página son OnPostJoinListAsync y OnPostJoinListUCAsync.In the preceding example, the page methods are OnPostJoinListAsync and OnPostJoinListUCAsync. Quitando OnPost y Async, los nombres de controlador son JoinList y JoinListUC.With OnPost and Async removed, the handler names are JoinList and JoinListUC.

<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 OnPostJoinListAsync es https://localhost:5001/Customers/CreateFATH?handler=JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinList. La ruta de dirección URL que envía a OnPostJoinListUCAsync es https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.

Rutas personalizadasCustom routes

Use la directiva @page para:Use the @page directive to:

  • Especificar una ruta personalizada a una página.Specify a custom route to a page. Por ejemplo, la ruta a la página Acerca de se puede establecer en /Some/Other/Path con @page "/Some/Other/Path".For example, the route to the About page can be set to /Some/Other/Path with @page "/Some/Other/Path".
  • Anexar segmentos a la ruta predeterminada de una página.Append segments to a page's default route. Por ejemplo, se puede agregar un segmento "item" a la ruta predeterminada de una página con @page "item".For example, an "item" segment can be added to a page's default route with @page "item".
  • Anexar parámetros a la ruta predeterminada de una página.Append parameters to a page's default route. Por ejemplo, un parámetro de identificador, id, puede ser necesario para una página con @page "{id}".For example, an ID parameter, id, can be required for a page with @page "{id}".

Se admite una ruta de acceso relativa raíz designada por una tilde (~) al principio de la ruta de acceso.A root-relative path designated by a tilde (~) at the beginning of the path is supported. Por ejemplo, @page "~/Some/Other/Path" es lo mismo que @page "/Some/Other/Path".For example, @page "~/Some/Other/Path" is the same as @page "/Some/Other/Path".

La cadena de consulta ?handler=JoinList de la dirección URL se puede cambiar por un segmento de ruta /JoinList, para lo cual hay que especificar la plantilla de ruta @page "{handler?}".You can change the query string ?handler=JoinList in the URL to a route segment /JoinList by specifying the route template @page "{handler?}".

Si no le gusta la cadena de consulta ?handler=JoinList 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.If you don't like the query string ?handler=JoinList in the URL, you can change the route to put the handler name in the path portion of the URL. Para personalizar la ruta, se puede agregar una plantilla de ruta entre comillas dobles después de la directiva @page.You can customize the route by adding a route template enclosed in double quotes after the @page directive.

@page "{handler?}"
@model CreateRouteModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></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 OnPostJoinListAsync es https://localhost:5001/Customers/CreateFATH/JoinList.Using the preceding code, the URL path that submits to OnPostJoinListAsync is https://localhost:5001/Customers/CreateFATH/JoinList. La ruta de dirección URL que envía a OnPostJoinListUCAsync es https://localhost:5001/Customers/CreateFATH/JoinListUC.The URL path that submits to OnPostJoinListUCAsync is https://localhost:5001/Customers/CreateFATH/JoinListUC.

El signo ? que sigue a handler significa que el parámetro de ruta es opcional.The ? following handler means the route parameter is optional.

Valores de configuraciónConfiguration and settings

Para configurar opciones avanzadas, use el método de extensión AddRazorPagesOptions en el generador de MVC:To configure advanced options, use the extension method AddRazorPagesOptions on the MVC builder:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            options.RootDirectory = "/MyPages";
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        });
}

Actualmente, puede usar RazorPagesOptions para establecer el directorio raíz de páginas, o agregar convenciones de modelo de aplicación a las páginas.Currently you can use the RazorPagesOptions to set the root directory for pages, or add application model conventions for pages. En el futuro habilitaremos más extensibilidad de este modo.We'll enable more extensibility this way in the future.

Para precompilar vistas, vea Razor view compilation (Compilación de vistas de Razor).To precompile views, see Razor view compilation .

Descargue o vea el código de ejemplo.Download or view sample code.

Vea Introducción a las páginas de Razor, que se basa en esta introducción.See Get started with Razor Pages, which builds on this introduction.

Especificar que las páginas de Razor se encuentran en la raíz de contenidoSpecify that Razor Pages are at the content root

De forma predeterminada, la ruta raíz de las páginas de Razor es el directorio /Pages.By default, Razor Pages are rooted in the /Pages directory. Agregue WithRazorPagesAtContentRoot a AddMvc para especificar que sus Razor Pages están en la ruta raíz de contenido (ContentRootPath) de la aplicación:Add WithRazorPagesAtContentRoot to AddMvc to specify that your Razor Pages are at the content root (ContentRootPath) of the app:

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesAtContentRoot();

Especificar que las páginas de Razor se encuentran en un directorio raíz personalizadoSpecify that Razor Pages are at a custom root directory

Agregue WithRazorPagesRoot a AddMvc para especificar que las páginas de Razor están en un directorio raíz personalizado en la aplicación (proporcione una ruta de acceso relativa):Add WithRazorPagesRoot to AddMvc to specify that your Razor Pages are at a custom root directory in the app (provide a relative path):

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesRoot("/path/to/razor/pages");

Recursos adicionalesAdditional resources