Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Por Rick Anderson, Dave Brock e Kirk Larkin
Note
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Warning
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.
Razor As páginas podem tornar a codificação de cenários focados na página mais fácil e produtiva do que o uso de controladores e exibições.
Se você estiver procurando um tutorial que use a abordagem Model-View-Controller, consulte Introdução ao ASP.NET Core MVC.
Este artigo aborda a arquitetura, os conceitos e os padrões que tornam Razor o Pages eficaz na criação de aplicativos Web focados na página. Ele explica como Razor as Páginas funcionam, seus principais componentes e as práticas recomendadas para implementação. Se preferir uma aprendizagem prática com instruções passo-a-passo, consulte Tutorial: Criar uma Razor aplicação Web do Pages com o ASP.NET Core. Para obter uma visão geral do ASP.NET Core, consulte a Introdução ao ASP.NET Core.
Prerequisites
- Visual Studio 2022 com a carga de trabalho de ASP.NET e desenvolvimento web .
- SDK do .NET 6
Criar um projeto Pages Razor
Consulte Introdução Razor ao Pages para obter instruções detalhadas sobre como criar um Razor projeto do Pages.
Razor Páginas
Razor O Pages está ativado em Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
No código anterior:
- AddRazorPages adiciona serviços do Razor Pages ao aplicativo.
- MapRazorPages adiciona pontos de extremidade para Razor Pages ao IEndpointRouteBuilder.
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
O código anterior se parece muito com um Razor arquivo de exibição usado em um aplicativo ASP.NET Core com controladores e exibições. O que a torna diferente é a @page diretiva.
@page transforma o arquivo em uma ação MVC, o que significa que ele lida com solicitações diretamente, sem passar por um controlador.
@page deve ser a primeira Razor diretiva numa página.
@page afeta o comportamento de outras Razor construções.
Razor Os nomes de arquivo de páginas têm um sufixo .cshtml .
Uma página semelhante, usando uma PageModel classe, é mostrada nos dois arquivos a seguir. O ficheiro Pages/Index2.cshtml
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
O Pages/Index2.cshtml.cs modelo de página:
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
Por convenção, o PageModel arquivo de classe tem o mesmo nome que o Razor arquivo Page com .cs anexado. Por exemplo, a Página anterior Razor é Pages/Index2.cshtml. O arquivo que contém a PageModel classe é chamado Pages/Index2.cshtml.cs.
As associações de caminhos de URL para páginas são determinadas pela localização da página no sistema de arquivos. A tabela a seguir mostra um caminho de página Razor e o URL correspondente.
| Nome e caminho do arquivo | URL correspondente |
|---|---|
/Pages/Index.cshtml |
/ ou /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store ou /Store/Index |
Notes:
- O ambiente de execução procura Razor ficheiros Pages na pasta Pages por padrão.
-
Indexé a página padrão quando um URL não inclui uma página.
Escrever um formulário básico
Razor O Pages foi projetado para facilitar a implementação de padrões comuns usados com navegadores da Web ao criar um aplicativo.
A vinculação de modelo, os ajudantes de marcação e os auxiliares de HTML funcionam com as propriedades definidas em uma classe Razor Página. Considere uma página que implemente um formulário básico de "entre em contato conosco" para o Contact modelo:
Para os exemplos neste documento, o DbContext é inicializado no arquivo Program.cs .
O banco de dados na memória requer o Microsoft.EntityFrameworkCore.InMemory pacote NuGet.
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
O modelo de dados:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
O contexto db:
using Microsoft.EntityFrameworkCore;
namespace RazorPagesContacts.Data
{
public class CustomerDbContext : DbContext
{
public CustomerDbContext (DbContextOptions<CustomerDbContext> options)
: base(options)
{
}
public DbSet<RazorPagesContacts.Models.Customer> Customer => Set<RazorPagesContacts.Models.Customer>();
}
}
O Pages/Customers/Create.cshtml arquivo de visualização:
@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>
O Pages/Customers/Create.cshtml.cs modelo de página:
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
Por convenção, a PageModel classe é chamada <PageName>Model e está no mesmo namespace da página.
A PageModel classe permite separar a lógica de uma página de sua apresentação. Ele define manipuladores de página para solicitações enviadas para a página e os dados usados para renderizar a página. Esta separação permite:
- Gerenciamento de dependências de página por meio de injeção de dependência.
- Testes unitários
A página tem um OnPostAsyncmétodo manipulador, que é executado em POST solicitações (quando um utilizador submete o formulário). Métodos de manipulador para qualquer verbo HTTP podem ser adicionados. Os manipuladores mais comuns são:
-
OnGetpara inicializar o estado necessário para a página. No código anterior, oOnGetmétodo exibe aCreate.cshtmlRazor página. -
OnPostpara lidar com envios de formulários.
O Async sufixo de nomenclatura é opcional, mas é frequentemente usado por convenção para funções assíncronas. O código anterior é típico de Razor Pages.
Se estiver familiarizado com ASP.NET aplicações que utilizam controladores e vistas:
- O
OnPostAsynccódigo no exemplo anterior é semelhante ao código de controlador típico. - A maioria das primitivas MVC, como vinculação de modelo, validação e resultados de ação, funciona da mesma forma com Controladores e Razor Páginas.
O método OnPostAsync anterior:
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
O fluxo básico de OnPostAsync:
Verifique se há erros de validação.
- Se não houver erros, salve os dados e redirecione.
- Se houver erros, mostre a página novamente com mensagens de validação. Em muitos casos, erros de validação seriam detetados no cliente e nunca enviados ao servidor.
O Pages/Customers/Create.cshtml arquivo de visualização:
@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>
O HTML gerado a partir de Pages/Customers/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>
No código anterior, postando o formulário:
Com dados válidos:
O método
OnPostAsyncque manipula chama o método auxiliar RedirectToPage.RedirectToPageRetorna uma instância de RedirectToPageResult.RedirectToPage:- É um resultado de ação.
- É semelhante a
RedirectToActionouRedirectToRoute(usado em controladores e visualizações). - É personalizado para páginas. No exemplo anterior, ele redireciona para a página de índice raiz (
/Index).RedirectToPageé detalhado na seção Geração de URL para Páginas .
Com erros de validação que são passados para o servidor:
- O método
OnPostAsyncque manipula chama o método auxiliar Page.PageRetorna uma instância de PageResult. RetornarPageé semelhante ao modo como as ações nos controladores retornamView.PageResulté o tipo de retorno padrão para um método manipulador. Um método manipulador que retornavoidrenderiza a página. - No exemplo anterior, postar o formulário sem valor resulta em ModelState.IsValid retornando false. Neste exemplo, nenhum erro de validação é exibido no cliente. O tratamento de erros de validação é abordado mais adiante neste documento.
[BindProperty] public Customer? Customer { get; set; } public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } if (Customer != null) _context.Customer.Add(Customer); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }- O método
Com erros de validação detetados pela validação do lado do cliente:
- Os dados não são enviados para o servidor.
- A validação do lado do cliente é explicada mais adiante neste documento.
A Customer propriedade usa o atributo [BindProperty] para aderir à vinculação de modelo:
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
[BindProperty]
não deve ser usado em modelos que contenham propriedades que não devem ser alteradas pelo cliente. Para obter mais informações, consulte Overposting.
Razor As páginas, por padrão, associam propriedades apenas com expressões que não sejam verbosGET. A associação a propriedades elimina a necessidade de escrever código para converter dados HTTP para o tipo de modelo. A vinculação reduz o código usando a mesma propriedade para renderizar campos de formulário (<input asp-for="Customer.Name">) e aceitar a entrada.
Warning
Por motivos de segurança, você deve optar por vincular GET os dados da solicitação às propriedades do modelo de página. Verifique a entrada do usuário antes de mapeá-la para as propriedades. Optar pela vinculação GET é útil ao abordar cenários que dependem de cadeia de caracteres de consulta ou valores de rota.
Para vincular uma propriedade nas solicitações de GET, defina a propriedade [BindProperty] do atributo SupportsGet como true.
[BindProperty(SupportsGet = true)]
Para obter mais informações, consulte discussão no ASP.NET Core Community Standup sobre Bind on GET (YouTube).
Rever o ficheiro de visualização Pages/Customers/Create.cshtml:
@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>
- No código precedente, o auxiliar de tag de entrada
<input asp-for="Customer.Name" />vincula o elemento HTML<input>à expressão deCustomer.Namemodelo. -
@addTagHelpertorna disponíveis os Tag Helpers.
A página inicial
Index.cshtml é a página inicial:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@if (Model.Customers != null)
{
foreach (var contact in Model.Customers)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
A classe associada PageModel (Index.cshtml.cs):
public class IndexModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public IndexModel(Data.CustomerDbContext context)
{
_context = context;
}
public IList<Customer>? Customers { get; set; }
public async Task OnGetAsync()
{
Customers = await _context.Customer.ToListAsync();
}
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
}
O Index.cshtml arquivo contém a seguinte marcação:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
O <a /a>Anchor Tag Helper usou o asp-route-{value} atributo para gerar um link para a página Editar. O link contém dados de rota com o ID de contato. Por exemplo, https://localhost:5001/Edit/1.
Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor.
O Index.cshtml arquivo contém marcação para criar um botão de exclusão para cada contato do cliente:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
O HTML renderizado:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Quando o botão delete é renderizado em HTML, sua formaction inclui parâmetros para:
- O ID de contacto do cliente, especificado pelo atributo
asp-route-id. - O
handler, especificado peloasp-page-handleratributo.
Quando o botão é selecionado, uma solicitação de formulário POST é enviada ao servidor. Por convenção, o nome do método manipulador é selecionado com base no valor do handler parâmetro de acordo com o esquema OnPost[handler]Async.
Como o handler é delete neste exemplo, o OnPostDeleteAsync método handler é usado para processar a POST solicitação. Se o asp-page-handler for definido como um valor diferente, como remove, um método de manipulador com o nome OnPostRemoveAsync será selecionado.
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
var contact = await _context.Customer.FindAsync(id);
if (contact != null)
{
_context.Customer.Remove(contact);
await _context.SaveChangesAsync();
}
return RedirectToPage();
}
O método OnPostDeleteAsync:
- Obtém o
idda cadeia de caracteres de consulta. - Consulta a base de dados para o contacto do cliente com
FindAsync. - Se o contato do cliente for encontrado, ele será removido e o banco de dados será atualizado.
- Chamadas RedirectToPage para redirecionar para a página de índice raiz (
/Index).
O arquivo "Edit.cshtml"
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Customer</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Customer!.Id" />
<div class="form-group">
<label asp-for="Customer!.Name" class="control-label"></label>
<input asp-for="Customer!.Name" class="form-control" />
<span asp-validation-for="Customer!.Name" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
A primeira linha contém a @page "{id:int}" diretiva. A restrição de encaminhamento "{id:int}" indica à página que aceite pedidos que contenham dados de rota int. Se uma solicitação à página não contiver dados de rota que possam ser convertidos em um int, o tempo de execução retornará um erro HTTP 404 (não encontrado). Para tornar o ID opcional, acrescente ? à restrição de rota:
@page "{id:int?}"
O ficheiro Edit.cshtml.cs
public class EditModel : PageModel
{
private readonly RazorPagesContacts.Data.CustomerDbContext _context;
public EditModel(RazorPagesContacts.Data.CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Customer = await _context.Customer.FirstOrDefaultAsync(m => m.Id == id);
if (Customer == null)
{
return NotFound();
}
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null)
{
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CustomerExists(Customer.Id))
{
return NotFound();
}
else
{
throw;
}
}
}
return RedirectToPage("./Index");
}
private bool CustomerExists(int id)
{
return _context.Customer.Any(e => e.Id == id);
}
}
Validation
Regras de validação:
- São declarativamente especificados na classe de modelo.
- São aplicados em todos os lugares do aplicativo.
O System.ComponentModel.DataAnnotations namespace fornece um conjunto de atributos de validação internos que são aplicados declarativamente a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como [DataType] esses ajudam na formatação e não fornecem nenhuma validação.
Considere o Customer modelo:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string? Name { get; set; }
}
}
Usando o seguinte Create.cshtml ficheiro de visualização:
@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>
O código anterior:
Inclui scripts jQuery e scripts de validação jQuery.
Usa o
<div />e os<span />Tag Helpers para habilitar:- Validação do lado do cliente.
- Renderização de erro de validação.
Gera o seguinte 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>
Postar o formulário Criar sem um valor de nome exibe a mensagem de erro "O campo Nome é obrigatório." no formulário. Se o JavaScript estiver ativado no cliente, o navegador exibirá o erro sem postar no servidor.
O [StringLength(10)] atributo gera data-val-length-max="10" no HTML renderizado.
data-val-length-max Impede que os navegadores insiram mais do que o comprimento máximo especificado. Se uma ferramenta como o Fiddler for usada para editar e reproduzir a postagem:
- Com o nome com mais de 10 caracteres.
- A mensagem de erro "O campo Nome deve ser uma cadeia de caracteres com um comprimento máximo de 10." é retornada.
Considere o seguinte Movie modelo:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
Os atributos de validação especificam o comportamento a ser imposto nas propriedades do modelo às quais são aplicados:
Os
Requiredatributos eMinimumLengthindicam que uma propriedade deve ter um valor, mas nada impede que um usuário insira espaço em branco para satisfazer essa validação.O
RegularExpressionatributo é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":- Deve usar apenas letras.
- A primeira letra deve ser maiúscula. Espaço em branco, números e caracteres especiais não são permitidos.
A
RegularExpression"classificação":- Requer que o primeiro caractere seja uma letra maiúscula.
- Permite caracteres especiais e números em espaços subsequentes. "PG-13" é válido para uma classificação, mas não é apropriado para um "Género".
O
Rangeatributo restringe um valor a dentro de um intervalo especificado.O
StringLengthatributo define o comprimento máximo de uma propriedade string e, opcionalmente, seu comprimento mínimo.Os tipos de valor (como
decimal,int,float,DateTime) são inerentemente necessários e não precisam do[Required]atributo.
A página Criar para o Movie modelo mostra erros com valores inválidos:
Para obter mais informações, consulte:
Isolamento CSS
Isolar estilos CSS em páginas, exibições e componentes individuais para reduzir ou evitar:
- Dependências de estilos globais que podem ser difíceis de manter.
- Conflitos de estilo em conteúdo aninhado.
Para adicionar um arquivo CSS com escopo para uma página ou exibição, coloque os estilos CSS em um arquivo complementar .cshtml.css correspondente ao nome do .cshtml arquivo. No exemplo a seguir, um Index.cshtml.css arquivo fornece estilos CSS que são aplicados apenas à Index.cshtml página ou exibição.
Pages/Index.cshtml.css (Razor Páginas) ou Views/Index.cshtml.css (MVC):
h1 {
color: red;
}
O isolamento CSS ocorre no momento da compilação. A estrutura reescreve os seletores CSS para corresponder à marcação renderizada pelas páginas ou exibições do aplicativo. Os estilos CSS reescritos são agrupados e produzidos como um ativo estático, {APP ASSEMBLY}.styles.css. O elemento de espaço reservado {APP ASSEMBLY} é o nome do assembly do projeto. Um link para os estilos CSS incluídos é colocado no layout do aplicativo.
<head> No conteúdo da aplicação Pages/Shared/_Layout.cshtml (Razor Pages) ou Views/Shared/_Layout.cshtml (MVC), adicione ou confirme a presença do link para os estilos CSS incorporados:
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />
No exemplo a seguir, o nome do assembly do aplicativo é WebApp:
<link rel="stylesheet" href="WebApp.styles.css" />
Os estilos definidos em um arquivo CSS com escopo são aplicados apenas à saída renderizada do arquivo correspondente. No exemplo anterior, quaisquer declarações CSS definidas noutro lugar da aplicação não entram em conflito com o estilo de título do h1. As regras de herança e cascata no estilo CSS permanecem em vigor para arquivos CSS com escopo. Por exemplo, os estilos aplicados diretamente a um <h1> elemento no arquivo substituem Index.cshtml os estilos do arquivo CSS com escopo no Index.cshtml.css.
Note
Para garantir o isolamento do estilo CSS quando ocorre agregação, não há suporte para a importação de CSS em Razor blocos de código.
O isolamento CSS só se aplica a elementos HTML. O isolamento CSS não é suportado para Tag Helpers.
Dentro do arquivo CSS incluído, cada página, exibição ou Razor componente é associado a um identificador de escopo no formato b-{STRING}, onde o espaço reservado {STRING} é uma cadeia de caracteres de dez caracteres gerada pela estrutura. O exemplo a seguir fornece o estilo para o elemento anterior <h1> na Index página de um Razor aplicativo Pages:
/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
color: red;
}
Na página onde o Index estilo CSS é aplicado a partir do arquivo incluído, o identificador de escopo é acrescentado como um atributo HTML:
<h1 b-3xxtam6d07>
O identificador é exclusivo de um aplicativo. No momento da compilação, um bundle de projeto é criado com a convenção {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css, onde o espaço reservado {STATIC WEB ASSETS BASE PATH} é o caminho base de ativos web estáticos.
Se outros projetos forem utilizados, como pacotes NuGet ou Razor bibliotecas de classes, o arquivo incluído:
- Faz referência aos estilos usando importações CSS.
- Não está publicado como um recurso web estático da aplicação que consome os estilos.
Suporte ao pré-processador CSS
Os pré-processadores CSS são úteis para melhorar o desenvolvimento CSS utilizando recursos como variáveis, aninhamento, módulos, mixins e herança. Embora o isolamento CSS não suporte nativamente pré-processadores CSS, como Sass ou Less, a integração de pré-processadores CSS é perfeita, desde que a compilação do pré-processador ocorra antes que a estrutura reescreva os seletores CSS durante o processo de compilação. Usando o Visual Studio, por exemplo, configure a compilação de pré-processador existente como uma tarefa Antes da compilação no Visual Studio Task Runner Explorer.
Muitos pacotes NuGet de terceiros, como AspNetCore.SassCompiler, podem compilar arquivos SASS/SCSS no início do processo de compilação, antes que ocorra o isolamento CSS, e não é necessária nenhuma configuração adicional.
Configuração de isolamento CSS
O isolamento CSS permite a configuração para alguns cenários avançados, como quando há dependências em ferramentas ou fluxos de trabalho existentes.
Personalizar o formato do identificador de escopo
{Pages|Views}
Por padrão, os identificadores de escopo usam o formato b-{STRING}, onde o espaço reservado {STRING} é uma cadeia de dez caracteres gerada pela estrutura. Para personalizar o formato do identificador de escopo, atualize o arquivo de projeto para um padrão desejado:
<ItemGroup>
<None Update="{Pages|Views}/Index.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
No exemplo anterior, o CSS gerado para Index.cshtml.css altera seu identificador de escopo de b-{STRING} para custom-scope-identifier.
Utilize identificadores de escopo para alcançar herança com ficheiros CSS delimitados. No exemplo de arquivo de projeto a seguir, um BaseView.cshtml.css arquivo contém estilos comuns entre modos de exibição. Um DerivedView.cshtml.css arquivo herda esses estilos.
<ItemGroup>
<None Update="{Pages|Views}/BaseView.cshtml.css" CssScope="custom-scope-identifier" />
<None Update="{Pages|Views}/DerivedView.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Use o operador curinga (*) para compartilhar identificadores de escopo em vários arquivos:
<ItemGroup>
<None Update="{Pages|Views}/*.cshtml.css" CssScope="custom-scope-identifier" />
</ItemGroup>
Alterar o caminho base para recursos estáticos da web
O arquivo CSS com escopo é gerado na raiz da aplicação. No arquivo de projeto, use a StaticWebAssetBasePath propriedade para alterar o caminho padrão. O exemplo a seguir coloca o arquivo CSS com escopo e o restante dos ativos do aplicativo no _content caminho:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
Desativar agregação automática
Para desativar a forma como o framework publica e carrega ficheiros com escopo em tempo de execução, utilize a propriedade DisableScopedCssBundling. Ao usar essa propriedade, outras ferramentas ou processos são responsáveis por retirar os arquivos CSS isolados do obj diretório e publicá-los e carregá-los em tempo de execução:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Razor suporte para biblioteca de classes (RCL)
Quando uma Razor biblioteca de classes (RCL) fornece estilos isolados, o atributo <link> da tag href aponta para {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css, onde os espaços reservados são:
-
{STATIC WEB ASSET BASE PATH}: O caminho base do recurso Web estático. -
{PACKAGE ID}: O identificador de pacote da biblioteca. O identificador de pacote assume como padrão o nome do assembly do projeto se o identificador do pacote não for especificado no arquivo do projeto.
No exemplo a seguir:
- O caminho base dos recursos Web estáticos é
_content/ClassLib. - O nome do assembly da biblioteca de classes é
ClassLib.
Pages/Shared/_Layout.cshtml (Razor Páginas) ou Views/Shared/_Layout.cshtml (MVC):
<link href="_content/ClassLib/ClassLib.bundle.scp.css" rel="stylesheet">
Para obter mais informações sobre RCLs, consulte os seguintes artigos:
- Interface do usuário Razor reutilizável em bibliotecas de classe com ASP.NET Core
- Consumir componentes do ASP.NET Core Razor a partir de uma biblioteca de classes (RCL) Razor
Para obter informações sobre Blazor o isolamento CSS, consulte ASP.NET Core Blazor isolamento CSS.
Tratar pedidos HEAD com uma alternativa do manipulador OnGet
HEAD As solicitações permitem recuperar os cabeçalhos de um recurso específico. Ao contrário dos pedidos GET, os pedidos HEAD não retornam um corpo de resposta.
Normalmente, um OnHead manipulador é criado e chamado para HEAD solicitações:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Razor O Pages reverte para chamar o OnGet handler se nenhum OnHead handler estiver definido.
XSRF/CSRF e Razor Páginas
Razor As páginas são protegidas por validação Antifalsificação. O FormTagHelper injeta tokens antifalsificação em elementos de formulário HTML.
Usando Layouts, Parciais, Modelos e Auxiliares de Tags com Razor Páginas
As páginas funcionam com todos os recursos do Razor mecanismo de exibição. Layouts, parciais, modelos, Tag Helpers, _ViewStart.cshtml e _ViewImports.cshtml funcionam da mesma forma que para visões convencionais Razor.
Vamos descomplicar esta página aproveitando alguns desses recursos.
Adicione uma página de layout a 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>
O layout:
- Controla o layout de cada página (a menos que a página opte por não usar o layout).
- Importa estruturas HTML, como JavaScript e folhas de estilo.
- O conteúdo da Razor página é renderizado onde
@RenderBody()é chamado.
Para obter mais informações, consulte a página de layout.
A propriedade Layout é definida em Pages/_ViewStart.cshtml:
@{
Layout = "_Layout";
}
O layout está na pasta Páginas/Compartilhada . As páginas procuram outros modos de exibição (layouts, modelos, parciais) hierarquicamente, começando na mesma pasta da página atual. Um layout na pasta Pages/Shared pode ser usado a partir de qualquer Razor página na pasta Pages .
O arquivo de layout deve ir na pasta Pages/Shared .
Recomendamos que você não coloque o arquivo de layout na pasta Views/Shared (Exibições/Compartilhados ). Views/Shared é um padrão de visualizações MVC. Razor As páginas devem depender da hierarquia de pastas, não de convenções de caminho.
Visualizar uma pesquisa de uma Razor Página que inclui a pasta Páginas. Os layouts, modelos e parciais usados com controladores MVC e visualizações convencionais Razorsimplesmente funcionam.
Adicione um Pages/_ViewImports.cshtml ficheiro:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace é explicado mais adiante no tutorial. A @addTagHelper diretiva integra os Tag Helpers embutidos em todas as páginas na pasta Pages.
A @namespace diretiva definida numa página:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
A @namespace diretiva define o namespace para a página. A @model diretiva não precisa incluir o namespace.
Quando a @namespace diretiva está contida no _ViewImports.cshtml, o namespace especificado fornece o prefixo para o namespace gerado na página que importa a @namespace diretiva. O restante do namespace gerado (a parte do sufixo) é o caminho relativo separado por pontos entre a pasta que contém _ViewImports.cshtml e a pasta que contém a página.
Por exemplo, a PageModel classe Pages/Customers/Edit.cshtml.cs define explicitamente o namespace:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
O Pages/_ViewImports.cshtml arquivo define o seguinte namespace:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
O namespace gerado para a Pages/Customers/Edit.cshtmlRazor página é o mesmo que a PageModel classe.
@namespace
Também funciona com vistas convencionais Razor .
Considere o ficheiro de visualização Pages/Customers/Create.cshtml
@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>
O arquivo de exibição atualizado Pages/Customers/Create.cshtml com _ViewImports.cshtml e o arquivo de layout anterior:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer!.Name" />
<input type="submit" />
</form>
No código anterior, o _ViewImports.cshtml importou o namespace e os auxiliares de tag. O arquivo de layout importou os arquivos JavaScript.
O Razor projeto inicial do Pages contém o Pages/_ValidationScriptsPartial.cshtml, que conecta a validação do lado do cliente.
Para obter mais informações sobre exibições parciais, consulte Exibições parciais no ASP.NET Core.
Geração de URL para Páginas
A Create página, mostrada anteriormente, usa RedirectToPage:
public class CreateModel : PageModel
{
private readonly Data.CustomerDbContext _context;
public CreateModel(Data.CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer? Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
if (Customer != null) _context.Customer.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
O aplicativo tem a seguinte estrutura de arquivos/pastas:
/Pages
Index.cshtmlPrivacy.cshtml/Customers
Create.cshtmlEdit.cshtmlIndex.cshtml
As páginas Pages/Customers/Create.cshtml e Pages/Customers/Edit.cshtml redirecionam para Pages/Customers/Index.cshtml após o sucesso. A cadeia de caracteres ./Index é um nome de página relativo usado para acessar a página anterior. Ele é usado para gerar URLs para a Pages/Customers/Index.cshtml página. Por exemplo:
Url.Page("./Index", ...)<a asp-page="./Index">Customers Index Page</a>RedirectToPage("./Index")
O nome /Index absoluto da página é usado para gerar URLs para a Pages/Index.cshtml página. Por exemplo:
Url.Page("/Index", ...)<a asp-page="/Index">Home Index Page</a>RedirectToPage("/Index")
O nome da página é o caminho para a página a partir da pasta raiz/Pages, incluindo um prefixo / (por exemplo, /Index). Os exemplos anteriores de geração de URL oferecem opções melhoradas e capacidades funcionais superiores à codificação manual de um URL. A geração de URL usa roteamento e pode gerar e codificar parâmetros de acordo com a forma como a rota é definida no caminho de destino.
A geração de URL para páginas suporta nomes relativos. A tabela a seguir mostra qual página de índice é selecionada usando parâmetros diferentes RedirectToPage no Pages/Customers/Create.cshtml.
| RedirectToPage(x) | Page |
|---|---|
| RedirectToPage("/Index") | Pages/Index |
| RedirectToPage("./Index"); | Pages/Customers/Index |
| RedirectToPage("../Index") | Pages/Index |
| RedirectToPage("Index") | Pages/Customers/Index |
RedirectToPage("Index"), RedirectToPage("./Index"), e RedirectToPage("../Index") são nomes relativos. O RedirectToPage parâmetro é combinado com o caminho da página atual para calcular o nome da página de destino.
A vinculação de nomes relativos é útil na construção de sites com uma estrutura complexa. Quando nomes relativos são usados para vincular entre páginas em uma pasta:
- Renomear uma pasta não quebra os links relativos.
- Os links não são quebrados porque não incluem o nome da pasta.
Para redirecionar para uma página em uma Área diferente, especifique a área:
RedirectToPage("/Index", new { area = "Services" });
Para obter mais informações, consulte Áreas no ASP.NET Core e Razor Rotas de páginas e convenções de aplicação no ASP.NET Core.
Atributo ViewData
Os dados podem ser passados para uma página com ViewDataAttribute. As propriedades com o atributo [ViewData] têm os seus valores armazenados e carregados a partir do ViewDataDictionary.
No exemplo a seguir, o AboutModel aplica o [ViewData] atributo à Title propriedade:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Na página Sobre, acesse a Title propriedade como uma propriedade modelo:
<h1>@Model.Title</h1>
No layout, o título é lido do dicionário ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
ASP.NET Core expõe o TempData. Esta propriedade armazena dados até que sejam lidos. Os Keep métodos e Peek podem ser usados para examinar os dados sem exclusão.
TempData é útil para redirecionamento, quando os dados são necessários para mais de uma única solicitação.
O seguinte código define o valor de Message usando 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");
}
}
A seguinte marcação no arquivo Pages/Customers/Index.cshtml exibe o valor de Message usando TempData.
<h3>Msg: @Model.Message</h3>
O Pages/Customers/Index.cshtml.cs modelo de página aplica o [TempData] atributo à Message propriedade.
[TempData]
public string Message { get; set; }
Para obter mais informações, consulte TempData.
Vários manipuladores por página
A página a seguir gera marcação para dois manipuladores usando o Auxiliar de asp-page-handler tag:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</form>
</body>
</html>
O formulário no exemplo anterior tem dois botões de envio, cada um usando o FormActionTagHelper para enviar para uma URL diferente. O asp-page-handler atributo é um companheiro para asp-page.
asp-page-handler gera URLs que são enviadas para cada um dos métodos de manipulador definidos por uma página.
asp-page não é especificado porque o exemplo está a referenciar a página atual.
O modelo de página:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateFATHModel : PageModel
{
private readonly AppDbContext _db;
public CreateFATHModel(AppDbContext db)
{
_db = db;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostJoinListAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
public async Task<IActionResult> OnPostJoinListUCAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Customer.Name = Customer.Name?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
O código anterior usa métodos de manipulador nomeado. Os métodos do manipulador nomeado são criados tomando o texto no nome depois On<HTTP Verb> e antes Async (se presente). No exemplo anterior, os métodos de página são OnPostJoinListAsync e OnPostJoinListUCAsync. Com OnPost e Async removidos, os nomes do manipulador são JoinList, JoinListUC.
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Usando o código anterior, o caminho da URL que envia para OnPostJoinListAsync é https://localhost:5001/Customers/CreateFATH?handler=JoinList. O caminho da URL que envia para OnPostJoinListUCAsync é https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.
Rotas personalizadas
Use a diretiva @page para:
- Especifique uma rota personalizada para uma página. Por exemplo, a rota para a página Sobre pode ser definida para
/Some/Other/Pathcom@page "/Some/Other/Path". - Acrescentar segmentos à rota padrão de uma página. Por exemplo, um segmento "item" pode ser adicionado à rota padrão de uma página com
@page "item". - Acrescentar parâmetros à rota padrão de uma página. Por exemplo, um parâmetro ID,
id, pode ser necessário para uma página com@page "{id}".
Um caminho relativo à raiz designado por um til (~) no início do caminho é suportado. Por exemplo, @page "~/Some/Other/Path" é o mesmo que @page "/Some/Other/Path".
Se você não gostar da cadeia de caracteres ?handler=JoinList de consulta na URL, altere a rota para colocar o nome do manipulador na parte do caminho da URL. A rota pode ser personalizada adicionando um modelo de rota entre aspas duplas após a @page diretiva.
@page "{handler?}"
@model CreateRouteModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
Usando o código anterior, o caminho da URL que envia para OnPostJoinListAsync é https://localhost:5001/Customers/CreateFATH/JoinList. O caminho da URL que envia para OnPostJoinListUCAsync é https://localhost:5001/Customers/CreateFATH/JoinListUC.
O ? seguinte handler significa que o parâmetro route é opcional.
Organização de ficheiros JavaScript (JS)
A colocação de arquivos JavaScript (JS) para páginas e visualizações é uma forma conveniente para organizar scripts num aplicativo.
Coloque JS arquivos usando as seguintes convenções de extensão de nome de arquivo:
- Páginas de aplicativos Pages Razor e visualizações de aplicativos MVC:
.cshtml.js. Examples:-
Pages/Index.cshtml.jspara aIndexpágina de uma Razor aplicação Pages emPages/Index.cshtml. -
Views/Home/Index.cshtml.jspara aIndexvista de uma aplicação MVC emViews/Home/Index.cshtml.
-
Os arquivos colocados JS são endereçáveis publicamente usando o caminho para o arquivo no projeto:
Páginas e visualizações de um arquivo de scripts colocado no aplicativo:
{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js- O
{PATH}espaço reservado é o caminho para a página, exibição ou componente. - O
{PAGE, VIEW, OR COMPONENT}espaço reservado é a página, o modo de exibição ou o componente. - O espaço reservado
{EXTENSION}corresponde à extensão da página, vista ou componente, sejarazoroucshtml.
Razor Exemplo de páginas:
Um JS arquivo para a
Indexpágina é colocado naPagespasta (Pages/Index.cshtml.js) ao lado daIndexpágina (Pages/Index.cshtml). Na páginaIndex, o script está referenciado no caminho da pastaPages.@section Scripts { <script src="~/Pages/Index.cshtml.js"></script> }- O
O layout Pages/Shared/_Layout.cshtml padrão pode ser configurado para incluir arquivos agrupados JS, eliminando a necessidade de configurar cada página individualmente.
<script asp-src-include="@(ViewContext.View.Path).js"></script>
O download de exemplo usa o trecho de código anterior para incluir arquivos colocados JS no layout padrão.
Quando o aplicativo é publicado, a estrutura move automaticamente o script para a raiz da Web. No exemplo anterior, o script é movido para bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Pages\Index.cshtml.js, onde o espaço reservado {TARGET FRAMEWORK MONIKER} é o Target Framework Moniker (TFM). Nenhuma alteração é necessária no URL relativo do script na página Index.
Quando o aplicativo é publicado, a estrutura move automaticamente o script para a raiz da Web. No exemplo anterior, o script é movido para bin\Release\{TARGET FRAMEWORK MONIKER}\publish\wwwroot\Components\Pages\Index.razor.js, onde o espaço reservado {TARGET FRAMEWORK MONIKER} é o Target Framework Moniker (TFM). Não é necessária nenhuma alteração à URL relativa do script Index no componente.
Para scripts fornecidos por uma Razor biblioteca de classes (RCL):
_content/{PACKAGE ID}/{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js- O espaço reservado
{PACKAGE ID}é o identificador de pacote da RCL (ou o nome da biblioteca de uma biblioteca de classes referenciada pela aplicação). - O
{PATH}espaço reservado é o caminho para a página, exibição ou componente. Se um componente Razor estiver localizado na raiz da RCL, o segmento de caminho não será incluído. - O
{PAGE, VIEW, OR COMPONENT}espaço reservado é a página, o modo de exibição ou o componente. - O espaço reservado
{EXTENSION}corresponde à extensão de página, vista ou componente, ourazoroucshtml.
- O espaço reservado
Configuração e definições avançadas
A configuração e as definições nas seções a seguir não são exigidas pela maioria dos aplicativos.
Para configurar opções avançadas, use a sobrecarga de método que configura AddRazorPages: RazorPagesOptions.
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Use o RazorPagesOptions para definir o diretório raiz para páginas ou adicione convenções de modelo de aplicativo para páginas. Para obter mais informações sobre convenções, consulte Razor Convenções de autorização de páginas.
Para pré-compilar vistas, consulte Razor compilação de vistas.
Especifique que as Razor Páginas estão na raiz do conteúdo
Por padrão, Razor as Páginas são enraizadas no diretório /Pages . Adicione WithRazorPagesAtContentRoot para especificar que suas Razor Páginas estão na raiz do conteúdo (ContentRootPath) do aplicativo:
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Especifique que Razor as Páginas estão em um diretório raiz personalizado
Adicione WithRazorPagesRoot para especificar que Razor as Páginas estão em um diretório raiz personalizado no aplicativo (forneça um caminho relativo):
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
builder.Services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Recursos adicionais
- Consulte Comece com Razor Pages, que é um desenvolvimento desta introdução.
- Atributo de Autorização e Páginas Razor
- Transferir ou visualizar código de exemplo
- Visão geral do ASP.NET Core
- Razor referência de sintaxe para ASP.NET Core
- Áreas em ASP.NET Core
- Tutorial: Introdução ao Razor Pages no ASP.NET Core
- Razor Convenções de autorização de páginas no ASP.NET Core
- Razor Rotas de páginas e convenções de aplicativos no ASP.NET Core
- Testes unitários do Razor Pages no ASP.NET Core
- Visualizações parciais no ASP.NET Core
- Visual Studio 2019 16.4 ou posterior com a carga de trabalho de desenvolvimento Web e ASP.NET
- SDK do .NET Core 3.1
- Visual Studio 2019 16.8 ou posterior com a carga de trabalho de desenvolvimento ASP.NET e Web
- SDK do .NET 5
Criar um projeto Pages Razor
Consulte Introdução Razor ao Pages para obter instruções detalhadas sobre como criar um Razor projeto do Pages.
Razor Páginas
Razor O Pages está ativado em 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();
});
}
}
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
O código anterior se parece muito com um Razor arquivo de exibição usado em um aplicativo ASP.NET Core com controladores e exibições. O que a torna diferente é a @page diretiva.
@page transforma o arquivo em uma ação MVC - o que significa que ele lida com solicitações diretamente, sem passar por um controlador.
@page deve ser a primeira Razor diretiva numa página.
@page afeta o comportamento de outras Razor construções.
Razor Os nomes de arquivo de páginas têm um sufixo .cshtml .
Uma página semelhante, usando uma PageModel classe, é mostrada nos dois arquivos a seguir. O ficheiro Pages/Index2.cshtml
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
O Pages/Index2.cshtml.cs modelo de página:
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System;
namespace RazorPagesIntro.Pages
{
public class Index2Model : PageModel
{
public string Message { get; private set; } = "PageModel in C#";
public void OnGet()
{
Message += $" Server time is { DateTime.Now }";
}
}
}
Por convenção, o PageModel arquivo de classe tem o mesmo nome que o Razor arquivo Page com .cs anexado. Por exemplo, a Página anterior Razor é Pages/Index2.cshtml. O arquivo que contém a PageModel classe é chamado Pages/Index2.cshtml.cs.
As associações de caminhos de URL para páginas são determinadas pela localização da página no sistema de arquivos. A tabela a seguir mostra um caminho de página Razor e o URL correspondente.
| Nome e caminho do arquivo | URL correspondente |
|---|---|
/Pages/Index.cshtml |
/ ou /Index |
/Pages/Contact.cshtml |
/Contact |
/Pages/Store/Contact.cshtml |
/Store/Contact |
/Pages/Store/Index.cshtml |
/Store ou /Store/Index |
Notes:
- O ambiente de execução procura Razor ficheiros Pages na pasta Pages por padrão.
-
Indexé a página padrão quando um URL não inclui uma página.
Escrever um formulário básico
Razor O Pages foi projetado para facilitar a implementação de padrões comuns usados com navegadores da Web ao criar um aplicativo.
A vinculação de modelo, os Auxiliares de Tags e os Auxiliares de HTML funcionam perfeitamente com as propriedades definidas em uma classe Razor Page. Considere uma página que implemente um formulário básico de "entre em contato conosco" para o Contact modelo:
Para os exemplos neste documento, o DbContext é inicializado no arquivo Startup.cs .
O banco de dados na memória requer o Microsoft.EntityFrameworkCore.InMemory pacote NuGet.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<CustomerDbContext>(options =>
options.UseInMemoryDatabase("name"));
services.AddRazorPages();
}
O modelo de dados:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
O contexto db:
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; }
}
}
O Pages/Create.cshtml arquivo de visualização:
@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>
O Pages/Create.cshtml.cs modelo de página:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using RazorPagesContacts.Models;
using System.Threading.Tasks;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateModel : PageModel
{
private readonly CustomerDbContext _context;
public CreateModel(CustomerDbContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
}
Por convenção, a PageModel classe é chamada <PageName>Model e está no mesmo namespace da página.
A PageModel classe permite separar a lógica de uma página de sua apresentação. Ele define manipuladores de página para solicitações enviadas para a página e os dados usados para renderizar a página. Esta separação permite:
- Gerenciamento de dependências de página por meio de injeção de dependência.
- Testes unitários
A página tem um OnPostAsyncmétodo manipulador, que é executado em POST solicitações (quando um utilizador submete o formulário). Métodos de manipulador para qualquer verbo HTTP podem ser adicionados. Os manipuladores mais comuns são:
-
OnGetpara inicializar o estado necessário para a página. No código anterior, oOnGetmétodo exibe aCreateModel.cshtmlRazor página. -
OnPostpara lidar com envios de formulários.
O Async sufixo de nomenclatura é opcional, mas é frequentemente usado por convenção para funções assíncronas. O código anterior é típico de Razor Pages.
Se estiver familiarizado com ASP.NET aplicações que utilizam controladores e vistas:
- O
OnPostAsynccódigo no exemplo anterior é semelhante ao código de controlador típico. - A maioria das primitivas MVC, como vinculação de modelo, validação e resultados de ação, funciona da mesma forma com Controladores e Razor Páginas.
O método OnPostAsync anterior:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
O fluxo básico de OnPostAsync:
Verifique se há erros de validação.
- Se não houver erros, salve os dados e redirecione.
- Se houver erros, mostre a página novamente com mensagens de validação. Em muitos casos, erros de validação seriam detetados no cliente e nunca enviados ao servidor.
O Pages/Create.cshtml arquivo de visualização:
@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>
O HTML gerado a partir de 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>
No código anterior, postando o formulário:
Com dados válidos:
O método
OnPostAsyncque manipula chama o método auxiliar RedirectToPage.RedirectToPageRetorna uma instância de RedirectToPageResult.RedirectToPage:- É um resultado de ação.
- É semelhante a
RedirectToActionouRedirectToRoute(usado em controladores e visualizações). - É personalizado para páginas. No exemplo anterior, ele redireciona para a página de índice raiz (
/Index).RedirectToPageé detalhado na seção Geração de URL para Páginas .
Com erros de validação que são passados para o servidor:
- O método
OnPostAsyncque manipula chama o método auxiliar Page.PageRetorna uma instância de PageResult. RetornarPageé semelhante ao modo como as ações nos controladores retornamView.PageResulté o tipo de retorno padrão para um método manipulador. Um método manipulador que retornavoidrenderiza a página. - No exemplo anterior, postar o formulário sem valor resulta em ModelState.IsValid retornando false. Neste exemplo, nenhum erro de validação é exibido no cliente. O tratamento de erros de validação é abordado mais adiante neste documento.
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { return Page(); } _context.Customers.Add(Customer); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }- O método
Com erros de validação detetados pela validação do lado do cliente:
- Os dados não são enviados para o servidor.
- A validação do lado do cliente é explicada mais adiante neste documento.
A Customer propriedade usa o atributo [BindProperty] para aderir à vinculação de modelo:
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]
não deve ser usado em modelos que contenham propriedades que não devem ser alteradas pelo cliente. Para obter mais informações, consulte Overposting.
Razor As páginas, por padrão, associam propriedades apenas com expressões que não sejam verbosGET. A associação a propriedades elimina a necessidade de escrever código para converter dados HTTP para o tipo de modelo. A vinculação reduz o código usando a mesma propriedade para renderizar campos de formulário (<input asp-for="Customer.Name">) e aceitar a entrada.
Warning
Por motivos de segurança, você deve optar por vincular GET os dados da solicitação às propriedades do modelo de página. Verifique a entrada do usuário antes de mapeá-la para as propriedades. Optar pela vinculação GET é útil ao abordar cenários que dependem de cadeia de caracteres de consulta ou valores de rota.
Para vincular uma propriedade nas solicitações de GET, defina a propriedade [BindProperty] do atributo SupportsGet como true.
[BindProperty(SupportsGet = true)]
Para obter mais informações, consulte discussão no ASP.NET Core Community Standup sobre Bind on GET (YouTube).
Rever o ficheiro de visualização Pages/Create.cshtml:
@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>
- No código precedente, o auxiliar de tag de entrada
<input asp-for="Customer.Name" />vincula o elemento HTML<input>à expressão deCustomer.Namemodelo. -
@addTagHelpertorna disponíveis os Tag Helpers.
A página inicial
Index.cshtml é a página inicial:
@page
@model RazorPagesContacts.Pages.Customers.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Contacts home page</h1>
<form method="post">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var contact in Model.Customer)
{
<tr>
<td> @contact.Id </td>
<td>@contact.Name</td>
<td>
<!-- <snippet_Edit> -->
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
<!-- </snippet_Edit> -->
<!-- <snippet_Delete> -->
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
<!-- </snippet_Delete> -->
</td>
</tr>
}
</tbody>
</table>
<a asp-page="Create">Create New</a>
</form>
A classe associada PageModel (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();
}
}
O Index.cshtml arquivo contém a seguinte marcação:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
O <a /a>Anchor Tag Helper usou o asp-route-{value} atributo para gerar um link para a página Editar. O link contém dados de rota com o ID de contato. Por exemplo, https://localhost:5001/Edit/1.
Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor.
O Index.cshtml arquivo contém marcação para criar um botão de exclusão para cada contato do cliente:
<button type="submit" asp-page-handler="delete" asp-route-id="@contact.Id">delete</button>
O HTML renderizado:
<button type="submit" formaction="/Customers?id=1&handler=delete">delete</button>
Quando o botão delete é renderizado em HTML, sua formaction inclui parâmetros para:
- O ID de contacto do cliente, especificado pelo atributo
asp-route-id. - O
handler, especificado peloasp-page-handleratributo.
Quando o botão é selecionado, uma solicitação de formulário POST é enviada ao servidor. Por convenção, o nome do método manipulador é selecionado com base no valor do handler parâmetro de acordo com o esquema OnPost[handler]Async.
Como o handler é delete neste exemplo, o OnPostDeleteAsync método handler é usado para processar a POST solicitação. Se o asp-page-handler for definido como um valor diferente, como remove, um método de manipulador com o nome OnPostRemoveAsync será selecionado.
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();
}
O método OnPostDeleteAsync:
- Obtém o
idda cadeia de caracteres de consulta. - Consulta a base de dados para o contacto do cliente com
FindAsync. - Se o contato do cliente for encontrado, ele será removido e o banco de dados será atualizado.
- Chamadas RedirectToPage para redirecionar para a página de índice raiz (
/Index).
O arquivo "Edit.cshtml"
@page "{id:int}"
@model RazorPagesContacts.Pages.Customers.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<h1>Edit Customer - @Model.Customer.Id</h1>
<form method="post">
<div asp-validation-summary="All"></div>
<input asp-for="Customer.Id" type="hidden" />
<div>
<label asp-for="Customer.Name"></label>
<div>
<input asp-for="Customer.Name" />
<span asp-validation-for="Customer.Name"></span>
</div>
</div>
<div>
<button type="submit">Save</button>
</div>
</form>
A primeira linha contém a @page "{id:int}" diretiva. A restrição de encaminhamento "{id:int}" indica à página que aceite pedidos que contenham dados de rota int. Se uma solicitação à página não contiver dados de rota que possam ser convertidos em um int, o tempo de execução retornará um erro HTTP 404 (não encontrado). Para tornar o ID opcional, acrescente ? à restrição de rota:
@page "{id:int?}"
O ficheiro Edit.cshtml.cs
public class EditModel : PageModel
{
private readonly CustomerDbContext _context;
public EditModel(CustomerDbContext context)
{
_context = context;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnGetAsync(int id)
{
Customer = await _context.Customers.FindAsync(id);
if (Customer == null)
{
return RedirectToPage("./Index");
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Customer).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
throw new Exception($"Customer {Customer.Id} not found!");
}
return RedirectToPage("./Index");
}
}
Validation
Regras de validação:
- São declarativamente especificados na classe de modelo.
- São aplicados em todos os lugares do aplicativo.
O System.ComponentModel.DataAnnotations namespace fornece um conjunto de atributos de validação internos que são aplicados declarativamente a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como [DataType] esses ajudam na formatação e não fornecem nenhuma validação.
Considere o Customer modelo:
using System.ComponentModel.DataAnnotations;
namespace RazorPagesContacts.Models
{
public class Customer
{
public int Id { get; set; }
[Required, StringLength(10)]
public string Name { get; set; }
}
}
Usando o seguinte Create.cshtml ficheiro de visualização:
@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>
O código anterior:
Inclui scripts jQuery e scripts de validação jQuery.
Usa o
<div />e os<span />Tag Helpers para habilitar:- Validação do lado do cliente.
- Renderização de erro de validação.
Gera o seguinte 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>
Postar o formulário Criar sem um valor de nome exibe a mensagem de erro "O campo Nome é obrigatório." no formulário. Se o JavaScript estiver ativado no cliente, o navegador exibirá o erro sem postar no servidor.
O [StringLength(10)] atributo gera data-val-length-max="10" no HTML renderizado.
data-val-length-max Impede que os navegadores insiram mais do que o comprimento máximo especificado. Se uma ferramenta como o Fiddler for usada para editar e reproduzir a postagem:
- Com o nome com mais de 10 caracteres.
- A mensagem de erro "O campo Nome deve ser uma cadeia de caracteres com um comprimento máximo de 10." é retornada.
Considere o seguinte Movie modelo:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
[StringLength(60, MinimumLength = 3)]
[Required]
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
[StringLength(5)]
[Required]
public string Rating { get; set; }
}
}
Os atributos de validação especificam o comportamento a ser imposto nas propriedades do modelo às quais são aplicados:
Os
Requiredatributos eMinimumLengthindicam que uma propriedade deve ter um valor, mas nada impede que um usuário insira espaço em branco para satisfazer essa validação.O
RegularExpressionatributo é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":- Deve usar apenas letras.
- A primeira letra deve ser maiúscula. Espaço em branco, números e caracteres especiais não são permitidos.
A
RegularExpression"classificação":- Requer que o primeiro caractere seja uma letra maiúscula.
- Permite caracteres especiais e números em espaços subsequentes. "PG-13" é válido para uma classificação, mas não é apropriado para um "Género".
O
Rangeatributo restringe um valor a dentro de um intervalo especificado.O
StringLengthatributo define o comprimento máximo de uma propriedade string e, opcionalmente, seu comprimento mínimo.Os tipos de valor (como
decimal,int,float,DateTime) são inerentemente necessários e não precisam do[Required]atributo.
A página Criar para o Movie modelo mostra erros com valores inválidos:
Para obter mais informações, consulte:
Tratar pedidos HEAD com uma alternativa do manipulador OnGet
HEAD As solicitações permitem recuperar os cabeçalhos de um recurso específico. Ao contrário dos pedidos GET, os pedidos HEAD não retornam um corpo de resposta.
Normalmente, um OnHead manipulador é criado e chamado para HEAD solicitações:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
Razor O Pages reverte para chamar o OnGet handler se nenhum OnHead handler estiver definido.
XSRF/CSRF e Razor Páginas
Razor As páginas são protegidas por validação Antifalsificação. O FormTagHelper injeta tokens antifalsificação em elementos de formulário HTML.
Usando Layouts, Parciais, Modelos e Auxiliares de Tags com Razor Páginas
As páginas funcionam com todos os recursos do Razor mecanismo de exibição. Layouts, parciais, modelos, Tag Helpers, _ViewStart.cshtml e _ViewImports.cshtml funcionam da mesma forma que para visões convencionais Razor.
Vamos descomplicar esta página aproveitando alguns desses recursos.
Adicione uma página de layout a 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>
O layout:
- Controla o layout de cada página (a menos que a página opte por não usar o layout).
- Importa estruturas HTML, como JavaScript e folhas de estilo.
- O conteúdo da Razor página é renderizado onde
@RenderBody()é chamado.
Para obter mais informações, consulte a página de layout.
A propriedade Layout é definida em Pages/_ViewStart.cshtml:
@{
Layout = "_Layout";
}
O layout está na pasta Páginas/Compartilhada . As páginas procuram outros modos de exibição (layouts, modelos, parciais) hierarquicamente, começando na mesma pasta da página atual. Um layout na pasta Pages/Shared pode ser usado a partir de qualquer Razor página na pasta Pages .
O arquivo de layout deve ir na pasta Pages/Shared .
Recomendamos que você não coloque o arquivo de layout na pasta Views/Shared (Exibições/Compartilhados ). Views/Shared é um padrão de visualizações MVC. Razor As páginas devem depender da hierarquia de pastas, não de convenções de caminho.
Visualizar uma pesquisa de uma Razor Página que inclui a pasta Páginas. Os layouts, modelos e parciais usados com controladores MVC e visualizações convencionais Razorsimplesmente funcionam.
Adicione um Pages/_ViewImports.cshtml ficheiro:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace é explicado mais adiante no tutorial. A @addTagHelper diretiva integra os Tag Helpers embutidos em todas as páginas na pasta Pages.
A @namespace diretiva definida numa página:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
A @namespace diretiva define o namespace para a página. A @model diretiva não precisa incluir o namespace.
Quando a @namespace diretiva está contida no _ViewImports.cshtml, o namespace especificado fornece o prefixo para o namespace gerado na página que importa a @namespace diretiva. O restante do namespace gerado (a parte do sufixo) é o caminho relativo separado por pontos entre a pasta que contém _ViewImports.cshtml e a pasta que contém a página.
Por exemplo, a PageModel classe Pages/Customers/Edit.cshtml.cs define explicitamente o namespace:
namespace RazorPagesContacts.Pages
{
public class EditModel : PageModel
{
private readonly AppDbContext _db;
public EditModel(AppDbContext db)
{
_db = db;
}
// Code removed for brevity.
O Pages/_ViewImports.cshtml arquivo define o seguinte namespace:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
O namespace gerado para a Pages/Customers/Edit.cshtmlRazor página é o mesmo que a PageModel classe.
@namespace
Também funciona com vistas convencionais Razor .
Considere o ficheiro de visualização Pages/Create.cshtml
@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>
O arquivo de exibição atualizado Pages/Create.cshtml com _ViewImports.cshtml e o arquivo de layout anterior:
@page
@model CreateModel
<p>Enter a customer name:</p>
<form method="post">
Name:
<input asp-for="Customer.Name" />
<input type="submit" />
</form>
No código anterior, o _ViewImports.cshtml importou o namespace e os auxiliares de tag. O arquivo de layout importou os arquivos JavaScript.
O Razor projeto inicial do Pages contém o Pages/_ValidationScriptsPartial.cshtml, que conecta a validação do lado do cliente.
Para obter mais informações sobre exibições parciais, consulte Exibições parciais no ASP.NET Core.
Geração de URL para Páginas
A Create página, mostrada anteriormente, usa 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");
}
}
O aplicativo tem a seguinte estrutura de arquivos/pastas:
/Pages
Index.cshtmlPrivacy.cshtml/Customers
Create.cshtmlEdit.cshtmlIndex.cshtml
As páginas Pages/Customers/Create.cshtml e Pages/Customers/Edit.cshtml redirecionam para Pages/Customers/Index.cshtml após o sucesso. A cadeia de caracteres ./Index é um nome de página relativo usado para acessar a página anterior. Ele é usado para gerar URLs para a Pages/Customers/Index.cshtml página. Por exemplo:
Url.Page("./Index", ...)<a asp-page="./Index">Customers Index Page</a>RedirectToPage("./Index")
O nome /Index absoluto da página é usado para gerar URLs para a Pages/Index.cshtml página. Por exemplo:
Url.Page("/Index", ...)<a asp-page="/Index">Home Index Page</a>RedirectToPage("/Index")
O nome da página é o caminho para a página a partir da pasta raiz/Pages, incluindo um prefixo / (por exemplo, /Index). Os exemplos anteriores de geração de URL oferecem opções melhoradas e capacidades funcionais superiores à codificação manual de um URL. A geração de URL usa roteamento e pode gerar e codificar parâmetros de acordo com a forma como a rota é definida no caminho de destino.
A geração de URL para páginas suporta nomes relativos. A tabela a seguir mostra qual página de índice é selecionada usando parâmetros diferentes RedirectToPage no Pages/Customers/Create.cshtml.
| RedirectToPage(x) | Page |
|---|---|
| RedirectToPage("/Index") | Pages/Index |
| RedirectToPage("./Index"); | Pages/Customers/Index |
| RedirectToPage("../Index") | Pages/Index |
| RedirectToPage("Index") | Pages/Customers/Index |
RedirectToPage("Index"), RedirectToPage("./Index"), e RedirectToPage("../Index") são nomes relativos. O RedirectToPage parâmetro é combinado com o caminho da página atual para calcular o nome da página de destino.
A vinculação de nomes relativos é útil na construção de sites com uma estrutura complexa. Quando nomes relativos são usados para vincular entre páginas em uma pasta:
- Renomear uma pasta não quebra os links relativos.
- Os links não são quebrados porque não incluem o nome da pasta.
Para redirecionar para uma página em uma Área diferente, especifique a área:
RedirectToPage("/Index", new { area = "Services" });
Para obter mais informações, consulte Áreas no ASP.NET Core e Razor Rotas de páginas e convenções de aplicação no ASP.NET Core.
Atributo ViewData
Os dados podem ser passados para uma página com ViewDataAttribute. As propriedades com o atributo [ViewData] têm os seus valores armazenados e carregados a partir do ViewDataDictionary.
No exemplo a seguir, o AboutModel aplica o [ViewData] atributo à Title propriedade:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Na página Sobre, acesse a Title propriedade como uma propriedade modelo:
<h1>@Model.Title</h1>
No layout, o título é lido do dicionário ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
TempData
ASP.NET Core expõe o TempData. Esta propriedade armazena dados até que sejam lidos. Os Keep métodos e Peek podem ser usados para examinar os dados sem exclusão.
TempData é útil para redirecionamento, quando os dados são necessários para mais de uma única solicitação.
O seguinte código define o valor de Message usando 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");
}
}
A seguinte marcação no arquivo Pages/Customers/Index.cshtml exibe o valor de Message usando TempData.
<h3>Msg: @Model.Message</h3>
O Pages/Customers/Index.cshtml.cs modelo de página aplica o [TempData] atributo à Message propriedade.
[TempData]
public string Message { get; set; }
Para obter mais informações, consulte TempData.
Vários manipuladores por página
A página a seguir gera marcação para dois manipuladores usando o Auxiliar de asp-page-handler tag:
@page
@model CreateFATHModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<!-- <snippet_Handlers> -->
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
<!-- </snippet_Handlers> -->
</form>
</body>
</html>
O formulário no exemplo anterior tem dois botões de envio, cada um usando o FormActionTagHelper para enviar para uma URL diferente. O asp-page-handler atributo é um companheiro para asp-page.
asp-page-handler gera URLs que são enviadas para cada um dos métodos de manipulador definidos por uma página.
asp-page não é especificado porque o exemplo está a referenciar a página atual.
O modelo de página:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
namespace RazorPagesContacts.Pages.Customers
{
public class CreateFATHModel : PageModel
{
private readonly AppDbContext _db;
public CreateFATHModel(AppDbContext db)
{
_db = db;
}
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostJoinListAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_db.Customers.Add(Customer);
await _db.SaveChangesAsync();
return RedirectToPage("/Index");
}
public async Task<IActionResult> OnPostJoinListUCAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Customer.Name = Customer.Name?.ToUpperInvariant();
return await OnPostJoinListAsync();
}
}
}
O código anterior usa métodos de manipulador nomeado. Os métodos do manipulador nomeado são criados tomando o texto no nome depois On<HTTP Verb> e antes Async (se presente). No exemplo anterior, os métodos de página são OnPostJoinListAsync e OnPostJoinListUCAsync. Com OnPost e Async removidos, os nomes do manipulador são JoinList, JoinListUC.
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
Usando o código anterior, o caminho da URL que envia para OnPostJoinListAsync é https://localhost:5001/Customers/CreateFATH?handler=JoinList. O caminho da URL que envia para OnPostJoinListUCAsync é https://localhost:5001/Customers/CreateFATH?handler=JoinListUC.
Rotas personalizadas
Use a diretiva @page para:
- Especifique uma rota personalizada para uma página. Por exemplo, a rota para a página Sobre pode ser definida para
/Some/Other/Pathcom@page "/Some/Other/Path". - Acrescentar segmentos à rota padrão de uma página. Por exemplo, um segmento "item" pode ser adicionado à rota padrão de uma página com
@page "item". - Acrescentar parâmetros à rota padrão de uma página. Por exemplo, um parâmetro ID,
id, pode ser necessário para uma página com@page "{id}".
Um caminho relativo à raiz designado por um til (~) no início do caminho é suportado. Por exemplo, @page "~/Some/Other/Path" é o mesmo que @page "/Some/Other/Path".
Se você não gostar da cadeia de caracteres ?handler=JoinList de consulta na URL, altere a rota para colocar o nome do manipulador na parte do caminho da URL. A rota pode ser personalizada adicionando um modelo de rota entre aspas duplas após a @page diretiva.
@page "{handler?}"
@model CreateRouteModel
<html>
<body>
<p>
Enter your name.
</p>
<div asp-validation-summary="All"></div>
<form method="POST">
<div><label>Name: <input asp-for="Customer.Name" /></label></div>
<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
</form>
</body>
</html>
Usando o código anterior, o caminho da URL que envia para OnPostJoinListAsync é https://localhost:5001/Customers/CreateFATH/JoinList. O caminho da URL que envia para OnPostJoinListUCAsync é https://localhost:5001/Customers/CreateFATH/JoinListUC.
O ? seguinte handler significa que o parâmetro route é opcional.
Configuração e definições avançadas
A configuração e as definições nas seções a seguir não são exigidas pela maioria dos aplicativos.
Para configurar opções avançadas, use a sobrecarga de método que configura AddRazorPages: RazorPagesOptions.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.RootDirectory = "/MyPages";
options.Conventions.AuthorizeFolder("/MyPages/Admin");
});
}
Use o RazorPagesOptions para definir o diretório raiz para páginas ou adicione convenções de modelo de aplicativo para páginas. Para obter mais informações sobre convenções, consulte Razor Convenções de autorização de páginas.
Para pré-compilar vistas, consulte Razor compilação de vistas.
Especifique que as Razor Páginas estão na raiz do conteúdo
Por padrão, Razor as Páginas são enraizadas no diretório /Pages . Adicione WithRazorPagesAtContentRoot para especificar que suas Razor Páginas estão na raiz do conteúdo (ContentRootPath) do aplicativo:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
}
Especifique que Razor as Páginas estão em um diretório raiz personalizado
Adicione WithRazorPagesRoot para especificar que Razor as Páginas estão em um diretório raiz personalizado no aplicativo (forneça um caminho relativo):
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesRoot("/path/to/razor/pages");
}
Recursos adicionais
- Consulte Comece com Razor Pages, que é um desenvolvimento desta introdução.
- Atributo de Autorização e Páginas Razor
- Transferir ou visualizar código de exemplo
- Visão geral do ASP.NET Core
- Razor referência de sintaxe para ASP.NET Core
- Áreas em ASP.NET Core
- Tutorial: Introdução ao Razor Pages no ASP.NET Core
- Razor Convenções de autorização de páginas no ASP.NET Core
- Razor Rotas de páginas e convenções de aplicativos no ASP.NET Core
- Testes unitários do Razor Pages no ASP.NET Core
- Visualizações parciais no ASP.NET Core