Eventos
19 de nov., 23 - 21 de nov., 23
Participe de sessões online no Microsoft Ignite criadas para expandir suas habilidades e ajudá-lo a lidar com os problemas complexos de hoje.
Registrar agoraNão há mais suporte para esse navegador.
Atualize o Microsoft Edge para aproveitar os recursos, o suporte técnico e as atualizações de segurança mais recentes.
Por Rick Anderson, Dave Brock e Kirk Larkin
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. 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 9 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
O Razor Pages torna a codificação de cenários centrados em página mais fácil e produtiva do que com o uso de controladores e exibições.
Se você estiver procurando um tutorial que utiliza a abordagem Modelo-Exibição-Controlador, consulte a Introdução ao ASP.NET Core MVC.
Este documento proporciona uma introdução ao Razor Pages. Este não é um tutorial passo a passo. Se você achar que algumas das seções são muito avançadas, consulte a Introdução ao Razor Pages. Para obter uma visão geral do ASP.NET Core, consulte a Introdução ao ASP.NET Core.
Confira a Introdução ao Razor Pages para obter instruções detalhadas sobre como criar um projeto do Razor Pages.
O Razor Pages é habilitado 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:
@page
<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>
O código anterior se assemelha muito a um arquivo de exibição do Razor usado em um aplicativo ASP.NET Core com controladores e exibições. O que o torna diferentes é a diretiva @page
. O @page
transforma o arquivo em uma ação do MVC, o que significa que ele trata solicitações diretamente, sem passar por um controlador. @page
deve ser a primeira diretiva do Razor em uma página. @page
afeta o comportamento de outros constructos do Razor. Os nomes de arquivo do Razor Pages têm um sufixo .cshtml
.
Uma página semelhante, usando uma classe PageModel
, é mostrada nos dois arquivos a seguir. O arquivo Pages/Index2.cshtml
:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
O modelo de página Pages/Index2.cshtml.cs
:
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 arquivo de classe PageModel
tem o mesmo nome que o arquivo do Razor Page com .cs
acrescentado. Por exemplo, a Razor Page anterior é Pages/Index2.cshtml
. O arquivo que contém a classe PageModel
chama-se Pages/Index2.cshtml.cs
.
As associações de caminhos de URL para páginas são determinadas pelo local da página no sistema de arquivos. A seguinte tabela mostra um caminho de Razor Page e a URL correspondente:
Caminho e nome 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 |
Observações:
Index
é a página padrão quando uma URL não inclui uma página.O Razor Pages foi projetado para facilitar a implementação de padrões comuns usados com navegadores da Web ao criar um aplicativo. Model binding, Auxiliares de Marcação e auxiliares HTML funcionam com as propriedades definidas em uma classe de Razor Page. Considere uma página que implementa um formulário básico "Fale conosco" para o modelo Contact
:
Para as amostras neste documento, o DbContext
é inicializado no arquivo Program.cs.
O banco de dados na memória requer o pacote NuGet Microsoft.EntityFrameworkCore.InMemory
.
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 do banco de dados:
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 arquivo de exibiçã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>
O modelo de página Pages/Customers/Create.cshtml.cs
:
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 classe PageModel
é chamada de <PageName>Model
e está no mesmo namespace que a página.
A classe PageModel
permite separar a lógica de uma página da respectiva apresentação. Ela define manipuladores para as solicitações enviadas e os dados usados para renderizar a página. Essa separação permite:
A página tem um método de manipuladorOnPostAsync
, que é executado em solicitações POST
(quando um usuário posta o formulário). Métodos de manipulador para qualquer verbo HTTP podem ser adicionados. Os manipuladores mais comuns são:
OnGet
para inicializar o estado necessário para a página. No código anterior, o método OnGet
exibe a Razor Page Create.cshtml
.OnPost
para manipular envios de formulário.O sufixo de nomenclatura Async
é opcional, mas costuma ser usado por convenção para funções assíncronas. O código anterior é comum para o Razor Pages.
Se você estiver familiarizado com aplicativos ASP.NET usando controladores e exibições:
OnPostAsync
no exemplo anterior é semelhante ao código de controlador típico.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.
O arquivo de exibiçã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>
A marca HTML renderizada 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 de manipulador OnPostAsync
chama o método auxiliar RedirectToPage. RedirectToPage
retorna uma instância de RedirectToPageResult. RedirectToPage
:
RedirectToAction
ou RedirectToRoute
(usado em controladores e exibições)./Index
). RedirectToPage
é descrito em detalhes na seção Geração de URLs para páginas.Com erros de validação passados para o servidor:
OnPostAsync
chama o método auxiliar Page. Page
retorna uma instância de PageResult. Retornar Page
é semelhante a como as ações em controladores retornam View
. PageResult
é o tipo de retorno padrão para um método de manipulador. Um método de manipulador que retorna void
renderiza a página.[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");
}
Com erros de validação detectados pela validação do lado do cliente:
A propriedade Customer
usa o atributo [BindProperty]
para aceitar o model binding:
[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 contêm propriedades que não devem ser alteradas pelo cliente. Para obter mais informações, confira Postagem em excesso.
O Razor Pages, por padrão, associa propriedades somente com verbos não GET
. A associação a propriedades remove a necessidade de escrever código para converter dados HTTP no tipo de modelo. A associação reduz o código usando a mesma propriedade para renderizar os campos de formulário (<input asp-for="Customer.Name">
) e aceitar a entrada.
Aviso
Por motivos de segurança, você deve aceitar associar os dados da solicitação GET
às propriedades do modelo de página. Verifique a entrada do usuário antes de mapeá-la para as propriedades. Aceitar a associação de GET
é útil ao lidar com cenários que contam com a cadeia de caracteres de consulta ou com os valores de rota.
Para associar uma propriedade a solicitações GET
, defina a propriedade SupportsGet
do atributo [BindProperty]
como true
:
[BindProperty(SupportsGet = true)]
Para obter mais informações, confira ASP.NET Core Community Standup: Bind on GET discussion (YouTube).
Examinando o arquivo de exibiçã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>
<input asp-for="Customer.Name" />
associa o elemento HTML <input>
à expressão de modelo Customer.Name
.@addTagHelper
disponibiliza os Auxiliares de Marca.Index.cshtml
é a 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>
<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 PageModel
associada (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 arquivo Index.cshtml
contém o seguinte markup:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
O auxiliar de marcas de âncora <a /a>
usou o atributo asp-route-{value}
para gerar um link para a página Edit. O link contém dados de rota com a ID de contato. Por exemplo, https://localhost:5001/Edit/1
. Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor.
O arquivo Index.cshtml
também contém a marcação para criar um botão de exclusão para cada contato de 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 de exclusão é renderizado no HTML, o formaction inclui parâmetros para:
asp-route-id
.handler
, especificado pelo atributo asp-page-handler
.Quando o botão é selecionado, uma solicitação de formulário POST
é enviada para o servidor. Por convenção, o nome do método do manipulador é selecionado com base no valor do parâmetro handler
de acordo com o esquema OnPost[handler]Async
.
Como o handler
é delete
neste exemplo, o método do manipulador OnPostDeleteAsync
é usado para processar a solicitação POST
. Se 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
:
id
da cadeia de caracteres de consulta.FindAsync
./Index
).@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 diretiva @page "{id:int}"
. A restrição de roteamento "{id:int}"
informa à página para aceitar solicitações para a página que contêm dados da rota int
. Se uma solicitação para a página não contém dados de rota que podem ser convertidos em um int
, o runtime retorna um erro HTTP 404 (não encontrado). Para tornar a ID opcional, acrescente ?
à restrição de rota:
@page "{id:int?}"
O arquivo 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);
}
}
Regras de validação:
O namespace System.ComponentModel.DataAnnotations fornece um conjunto de atributos de validação internos aplicados de forma declarativa a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como [DataType]
, que ajudam com a formatação e não fornecem validação.
Considere o modelo Customer
:
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 arquivo de exibição 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 código anterior:
Inclui jQuery e scripts de validação de jQuery.
Usa o <div />
e <span />
Auxiliares de Marcação para habilitar:
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 é necessário" no formulário. Se o JavaScript estiver habilitado no cliente, o navegador exibirá o erro sem postar no servidor.
O atributo [StringLength(10)]
gera data-val-length-max="10"
no HTML renderizado. data-val-length-max
impede que navegadores insiram mais do que o comprimento máximo especificado. Se uma ferramenta como o Fiddler for usada para editar e reproduzir a postagem:
Considere o seguinte modelo Movie
:
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 de modelo às quais eles são aplicados:
Os atributos Required
e MinimumLength
indicam que uma propriedade deve ter um valor; porém, nada impede que um usuário insira um espaço em branco para atender a essa validação.
O atributo RegularExpression
é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":
A "Classificação" RegularExpression
:
O atributo Range
restringe um valor a um intervalo especificado.
O atributo StringLength
define o tamanho máximo de uma propriedade de cadeia de caracteres e, opcionalmente, seu tamanho mínimo.
Os tipos de valor (como decimal
, int
, float
, DateTime
) são inerentemente necessários e não precisam do atributo [Required]
.
A página Criar para o modelo Movie
mostra erros com valores inválidos:
Para saber mais, veja:
Isole estilos CSS em páginas, exibições e componentes individuais para reduzir ou evitar:
Para adicionar um arquivo CSS com escopo para uma página ou exibição, coloque os estilos CSS em um arquivo .cshtml.css
complementar correspondente ao nome do arquivo .cshtml
. No exemplo a seguir, um arquivo Index.cshtml.css
fornece estilos CSS aplicados somente à página ou exibição Index.cshtml
.
Pages/Index.cshtml.css
(Razor Pages) ou Views/Index.cshtml.css
(MVC):
h1 {
color: red;
}
O isolamento de CSS ocorre no momento do build. A estrutura reescreve seletores de CSS para fazer a correspondência da marcação renderizada pelas páginas ou exibições do aplicativo. Os estilos CSS reescritos são empacotados e produzidos como um ativo estático, {APP ASSEMBLY}.styles.css
. O espaço reservado {APP ASSEMBLY}
é o nome do assembly do projeto. Um link para os estilos CSS empacotados é colocado no layout do aplicativo.
No conteúdo <head>
do aplicativo Pages/Shared/_Layout.cshtml
(Razor Pages) ou Views/Shared/_Layout.cshtml
(MVC), adicione ou confirme a presença do link para os estilos CSS empacotados:
<link rel="stylesheet" href="~/{APP ASSEMBLY}.styles.css" />
No seguinte exemplo, 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, as declarações CSS h1
definidas em outro lugar no aplicativo não entram em conflito com o estilo de título de Index
. O estilo CSS em cascata e as regras de herança permanecem em vigor para arquivos CSS com escopo. Por exemplo, estilos aplicados diretamente a um elemento <h1>
no arquivo Index.cshtml
substituem os estilos do arquivo CSS com escopo em Index.cshtml.css
.
Observação
Para garantir o isolamento do estilo CSS quando o agrupamento ocorrer, não há suporte para a importação de CSS em blocos de código de Razor.
O isolamento de CSS se aplica apenas a elementos HTML. Não há suporte para isolamento de CSS para Auxiliares de Marcação.
Dentro do arquivo CSS empacotado, cada página, exibição ou componente Razor é associado a um identificador de escopo no formato b-{STRING}
, em que o espaço reservado {STRING}
é uma cadeia de caracteres de dez caracteres gerada pela estrutura. O seguinte exemplo fornece o estilo do elemento <h1>
anterior na página Index
de um aplicativo Razor Pages:
/* /Pages/Index.cshtml.rz.scp.css */
h1[b-3xxtam6d07] {
color: red;
}
Na página Index
em que o estilo CSS é aplicada do arquivo empacotado, o identificador de escopo é acrescentado como um atributo HTML:
<h1 b-3xxtam6d07>
O identificador é exclusivo para um aplicativo. No momento da compilação, um pacote de projeto é criado com a convenção {STATIC WEB ASSETS BASE PATH}/Project.lib.scp.css
, em que o espaço reservado {STATIC WEB ASSETS BASE PATH}
é o caminho base dos ativos da Web estáticos.
Se outros projetos forem utilizados, como pacotes NuGet ou bibliotecas de classes Razor, o arquivo empacotado:
Pré-processadores de CSS são úteis para aprimorar o desenvolvimento de CSS utilizando recursos como variáveis, aninhamento, módulos, mixins e herança. Embora o isolamento de CSS não dê suporte nativo a pré-processadores CSS, como Sass ou Less, a integração de pré-processadores de CSS é contínua desde que a compilação do pré-processador ocorra antes que a estrutura reescreva os seletores de CSS durante o processo de build. Usando o Visual Studio, por exemplo, configure a compilação de pré-processador existente como uma tarefa Before Build no Gerenciador do Executor de Tarefas do Visual Studio.
Muitos pacotes NuGet de terceiros, como AspNetCore.SassCompiler
, podem compilar arquivos SASS/SCSS no início do processo de build antes que o isolamento de CSS ocorra e nenhuma configuração adicional seja necessária.
O isolamento de CSS permite a configuração de alguns cenários avançados, como quando há dependências em ferramentas ou fluxos de trabalho existentes.
Nesta seção, o espaço reservado {Pages|Views}
é Pages
para aplicativos Razor Pages ou Views
para aplicativos MVC.
Por padrão, os identificadores de escopo usam o formato b-{STRING}
, em que o espaço reservado {STRING}
é uma cadeia de caracteres 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 o identificador de escopo de b-{STRING}
para custom-scope-identifier
.
Use identificadores de escopo para obter a herança com arquivos CSS com escopo. No exemplo de arquivo de projeto a seguir, um arquivo BaseView.cshtml.css
contém estilos comuns entre exibições. Um arquivo DerivedView.cshtml.css
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>
O arquivo CSS com escopo é gerado na raiz do aplicativo. No arquivo de projeto, use a propriedade StaticWebAssetBasePath
para alterar o caminho padrão. O seguinte exemplo coloca o arquivo CSS com escopo, e o rest dos ativos do aplicativo, no caminho _content
:
<PropertyGroup>
<StaticWebAssetBasePath>_content/$(PackageId)</StaticWebAssetBasePath>
</PropertyGroup>
Para recusar como a estrutura publica e carrega os arquivos com escopo no runtime, use a propriedade DisableScopedCssBundling
. Ao usar essa propriedade, outras ferramentas ou processos são responsáveis por tirar os arquivos CSS isolados do diretório obj
e publicá-los e carregá-los em runtime:
<PropertyGroup>
<DisableScopedCssBundling>true</DisableScopedCssBundling>
</PropertyGroup>
Quando uma RCL (biblioteca de classes) Razor fornece estilos isolados, o atributo <link>
da marca href
aponta para {STATIC WEB ASSET BASE PATH}/{PACKAGE ID}.bundle.scp.css
, onde estão os espaços reservados:
{STATIC WEB ASSET BASE PATH}
: o caminho de base do ativo da Web estático.{PACKAGE ID}
: o identificador de pacote da biblioteca. O identificador de pacote usará como padrão o nome do assembly do projeto se não for especificado no arquivo de projeto.No exemplo a seguir:
_content/ClassLib
.ClassLib
.Pages/Shared/_Layout.cshtml
(Razor Pages) 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:
Para obter informações sobre o isolamento de CSS de Blazor, consulte Isolamento de CSS do Blazor do ASP.NET Core.
As solicitações HEAD
permitem recuperar os cabeçalhos de um recurso específico. Diferente das solicitações GET
, as solicitações HEAD
não retornam um corpo de resposta.
Geralmente, um manipulador OnHead
é criado e chamado para solicitações HEAD
:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
O Razor Pages chama o manipulador OnGet
quando nenhum manipulador OnHead
é definido.
Os Razor Pages são protegidos pela Validação antifalsificação. O FormTagHelper injeta tokens antifalsificação em elementos de formulário HTML.
As páginas funcionam com todos os recursos do mecanismo de exibição do Razor. Layouts, parciais, modelos, Auxiliares de Marcação, _ViewStart.cshtml
e _ViewImports.cshtml
funcionam da mesma forma que exibições convencionais do Razor.
Organizaremos essa página aproveitando alguns desses recursos.
Adicione uma página de layout para 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:
@RenderBody()
é chamado.Veja página de layout para obter mais informações.
A propriedade Layout é definida em Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
O layout está na pasta Pages/Shared. As páginas buscam outras exibições (layouts, modelos, parciais) hierarquicamente, iniciando na mesma pasta que a página atual. Um layout na pasta Pages/Shared pode ser usado em qualquer página do Razor na pasta Pages.
O arquivo de layout deve entrar na pasta Pages/Shared.
Recomendamos que você não coloque o arquivo de layout na pasta Views/Shared. Views/Shared é um padrão de exibições do MVC. As Páginas do Razor devem confiar na hierarquia de pasta e não nas convenções de caminho.
A pesquisa de modo de exibição de uma Página do Razor inclui a pasta Pages. Os layouts, modelos e parciais que você está usando com controladores MVC e exibições do Razor convencionais apenas funcionam.
Adicione um arquivo Pages/_ViewImports.cshtml
:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace
é explicado posteriormente no tutorial. A diretiva @addTagHelper
coloca os auxiliares de marcas internos em todas as páginas na pasta Pages.
A diretiva @namespace
definida em uma página:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
A diretiva @namespace
define o namespace da página. A diretiva @model
não precisa incluir o namespace.
Quando a diretiva @namespace
está contida em _ViewImports.cshtml
, o namespace especificado fornece o prefixo do namespace gerado na página que importa a diretiva @namespace
. O rest do namespace gerado (a parte do sufixo) é o caminho relativo separado por ponto entre a pasta que contém _ViewImports.cshtml
e a pasta que contém a página.
Por exemplo, a classe PageModel
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 arquivo Pages/_ViewImports.cshtml
define o seguinte namespace:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
O namespace gerado para o Pages/Customers/Edit.cshtml
Razor Page é o mesmo que a classe PageModel
.
@namespace
também funciona com exibições convencionais do Razor.
Considere o arquivo de exibiçã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 Pages/Customers/Create.cshtml
atualizado 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 Marcação. O arquivo de layout importou os arquivos JavaScript.
O projeto inicial do Razor 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.
A página Create
, exibida 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 estrutura de arquivos/pastas a seguir:
Pages/
Index.cshtml
Privacy.cshtml
/Clientes
Create.cshtml
Edit.cshtml
Index.cshtml
As páginas Pages/Customers/Create.cshtml
e Pages/Customers/Edit.cshtml
redirecionam para Pages/Customers/Index.cshtml
após o êxito. 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 página Pages/Customers/Index.cshtml
. Por exemplo:
Url.Page("./Index", ...)
<a asp-page="./Index">Customers Index Page</a>
RedirectToPage("./Index")
O nome da página absoluto /Index
é usado para gerar URLs para a página Pages/Index.cshtml
. 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 da pasta raiz /Pages, incluindo um /
à direita (por exemplo, /Index
). Os exemplos anteriores de geração de URL oferecem opções avançadas e recursos funcionais para codificar uma URL. A geração de URL usa roteamento e pode gerar e codificar parâmetros de acordo com o modo como a rota é definida no caminho de destino.
A Geração de URL para páginas dá suporte a nomes relativos. A tabela a seguir mostra qual página de Índice é selecionada usando diferentes parâmetros de RedirectToPage
em 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 parâmetro RedirectToPage
é combinado com o caminho da página atual para calcular o nome da página de destino.
Vinculação de nome relativo é útil ao criar sites com uma estrutura complexa. Quando nomes relativos são usados para vincular páginas em uma pasta:
Para redirecionar para uma página em uma área diferente, especifique essa área:
RedirectToPage("/Index", new { area = "Services" });
Para obter mais informações, consulte Áreas no ASP.NET Core e Convenções de rota e aplicativo do Razor no ASP.NET Core.
Dados podem ser passados para uma página com ViewDataAttribute. Propriedades com o atributo [ViewData]
têm seus valores armazenados e carregados do ViewDataDictionary.
No seguintes exemplo, o AboutModel
aplica o atributo [ViewData]
à propriedade Title
:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Na página Sobre, acesse a propriedade Title
como uma propriedade de modelo:
<h1>@Model.Title</h1>
No layout, o título é lido a partir do dicionário ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
O ASP.NET Core expõe o TempData. Essa propriedade armazena dados até eles serem lidos. Os métodos Keep e Peek podem ser usados para examinar os dados sem exclusão. TempData
é útil para redirecionamento nos casos em que os dados são necessários para mais de uma única solicitação.
Os conjuntos de código a seguir definem 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 marcação a seguir no arquivo Pages/Customers/Index.cshtml
exibe o valor de Message
usando TempData
.
<h3>Msg: @Model.Message</h3>
O modelo de página Pages/Customers/Index.cshtml.cs
aplica o atributo [TempData]
à propriedade Message
.
[TempData]
public string Message { get; set; }
Para obter mais informações, confira TempData.
A página a seguir gera marcação para dois manipuladores usando o auxiliar de marcação asp-page-handler
:
@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 atributo asp-page-handler
é um complemento para asp-page
. asp-page-handler
gera URLs que enviam para cada um dos métodos de manipulador definidos por uma página. asp-page
não foi especificado porque a amostra está vinculando à 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 nomeados. Métodos de manipulador nomeados são criados colocando o texto no nome após On<HTTP Verb>
e antes de Async
(se houver). No exemplo anterior, os métodos de página são OnPostJoinListAsync e OnPostJoinListUCAsync. Com OnPost e Async removidos, os nomes de manipulador são JoinList
e 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 a OnPostJoinListAsync
é https://localhost:5001/Customers/CreateFATH?handler=JoinList
. O caminho da URL que envia a OnPostJoinListUCAsync
é https://localhost:5001/Customers/CreateFATH?handler=JoinListUC
.
Use a diretiva @page
para:
/Some/Other/Path
com @page "/Some/Other/Path"
.@page "item"
.id
, pode ser necessário para uma página com @page "{id}"
.Há suporte para um caminho relativo à raiz designado por um til (~
) no início do caminho. Por exemplo, @page "~/Some/Other/Path"
é o mesmo que @page "/Some/Other/Path"
.
Se você não deseja a cadeia de consulta ?handler=JoinList
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 diretiva @page
.
@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 a OnPostJoinListAsync
é https://localhost:5001/Customers/CreateFATH/JoinList
. O caminho da URL que envia a OnPostJoinListUCAsync
é https://localhost:5001/Customers/CreateFATH/JoinListUC
.
O ?
após handler
significa que o parâmetro de rota é opcional.
A colocação de arquivos JavaScript (JS) para páginas e exibições é uma maneira conveniente de organizar scripts em um aplicativo.
Colocalize arquivos JS usando as seguintes convenções de extensão de nome de arquivo:
.cshtml.js
. Exemplos: Pages/Index.cshtml.js
para a página Index
de um aplicativo Razor Pages em Pages/Index.cshtml
.Views/Home/Index.cshtml.js
para a exibição Index
de um aplicativo MVC em Views/Home/Index.cshtml
.Arquivos JS colocalizados são endereçáveis publicamente usando o caminho para o arquivo no projeto:
Páginas e exibições de um arquivo de scripts colocados no aplicativo:
{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js
{PATH}
é o caminho para a página, exibição ou componente.{PAGE, VIEW, OR COMPONENT}
é a página, exibição ou componente.{EXTENSION}
corresponde à extensão da página, exibição ou componente, razor
ou cshtml
.Exemplo do Razor Pages:
Um arquivo JS para a página Index
é colocado na pasta Pages
(Pages/Index.cshtml.js
) ao lado da página Index
(Pages/Index.cshtml
). Na página Index
, o script é referenciado no caminho na pasta Pages
:
@section Scripts {
<script src="~/Pages/Index.cshtml.js"></script>
}
O layout Pages/Shared/_Layout.cshtml
padrão pode ser configurado para incluir arquivos JS colocados, eliminando a necessidade de configurar cada página individualmente:
<script asp-src-include="@(ViewContext.View.Path).js"></script>
O download de exemplo usa o snippet de código anterior para incluir arquivos JS colocados 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
, em que o espaço reservado {TARGET FRAMEWORK MONIKER}
é o TFM (Moniker da Estrutura de Destino). Nenhuma alteração é necessária para a URL relativa 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
, em que o espaço reservado {TARGET FRAMEWORK MONIKER}
é o TFM (Moniker da Estrutura de Destino). Nenhuma alteração é necessária para a URL relativa do script no componente Index
.
Para scripts fornecidos por uma RCL (biblioteca de classes) Razor:
_content/{PACKAGE ID}/{PATH}/{PAGE, VIEW, OR COMPONENT}.{EXTENSION}.js
{PACKAGE ID}
é o identificador do pacote da RCL (ou nome da biblioteca para uma biblioteca de classes referenciada pelo aplicativo).{PATH}
é o caminho para a página, exibição ou componente. Se um componente Razor estiver localizado na raiz da RCL, o segmento de linha não será incluído.{PAGE, VIEW, OR COMPONENT}
é a página, exibição ou componente.{EXTENSION}
corresponde à extensão da página, exibição ou componente, razor
ou cshtml
.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 AddRazorPages que configura 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 adicionar as convenções de modelo de aplicativo para páginas. Para obter mais informações sobre convenções, consulte Convenções de autorização do Razor Pages.
Para pré-compilar exibições, consulte Compilação de exibição do Razor.
Por padrão, Razor Pages estão na raiz do diretório /Pages. Adicione WithRazorPagesAtContentRoot para especificar que Razor Pages estão na raiz de 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();
Adicione WithRazorPagesRoot para especificar que Razor Pages 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();
Confira a Introdução ao Razor Pages para obter instruções detalhadas sobre como criar um projeto do Razor Pages.
O Razor Pages é habilitado 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 assemelha muito a um arquivo de exibição do Razor usado em um aplicativo ASP.NET Core com controladores e exibições. O que o torna diferentes é a diretiva @page
. @page
transforma o arquivo em uma ação do MVC – o que significa que ele trata solicitações diretamente, sem passar por um controlador. @page
deve ser a primeira diretiva do Razor em uma página. @page
afeta o comportamento de outros constructos do Razor. Os nomes de arquivo do Razor Pages têm um sufixo .cshtml
.
Uma página semelhante, usando uma classe PageModel
, é mostrada nos dois arquivos a seguir. O arquivo Pages/Index2.cshtml
:
@page
@using RazorPagesIntro.Pages
@model Index2Model
<h2>Separate page model</h2>
<p>
@Model.Message
</p>
O modelo de página Pages/Index2.cshtml.cs
:
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 arquivo de classe PageModel
tem o mesmo nome que o arquivo do Razor Page com .cs
acrescentado. Por exemplo, a Razor Page anterior é Pages/Index2.cshtml
. O arquivo que contém a classe PageModel
chama-se Pages/Index2.cshtml.cs
.
As associações de caminhos de URL para páginas são determinadas pelo local da página no sistema de arquivos. A seguinte tabela mostra um caminho de Razor Page e a URL correspondente:
Caminho e nome 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 |
Observações:
Index
é a página padrão quando uma URL não inclui uma página.O Razor Pages foi projetado para facilitar a implementação de padrões comuns usados com navegadores da Web ao criar um aplicativo. Model binding, auxiliares de marcas e auxiliares HTML funcionam todos apenas com as propriedades definidas em uma classe de Razor Page. Considere uma página que implementa um formulário básico "Fale conosco" para o modelo Contact
:
Para as amostras neste documento, o DbContext
é inicializado no arquivo Startup.cs.
O banco de dados na memória requer o pacote NuGet Microsoft.EntityFrameworkCore.InMemory
.
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 do banco de dados:
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 arquivo de exibiçã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>
O modelo de página Pages/Create.cshtml.cs
:
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 classe PageModel
é chamada de <PageName>Model
e está no mesmo namespace que a página.
A classe PageModel
permite separar a lógica de uma página da respectiva apresentação. Ela define manipuladores para as solicitações enviadas e os dados usados para renderizar a página. Essa separação permite:
A página tem um método de manipuladorOnPostAsync
, que é executado em solicitações POST
(quando um usuário posta o formulário). Métodos de manipulador para qualquer verbo HTTP podem ser adicionados. Os manipuladores mais comuns são:
OnGet
para inicializar o estado necessário para a página. No código anterior, o método OnGet
exibe a Razor Page CreateModel.cshtml
.OnPost
para manipular envios de formulário.O sufixo de nomenclatura Async
é opcional, mas costuma ser usado por convenção para funções assíncronas. O código anterior é comum para o Razor Pages.
Se você estiver familiarizado com aplicativos ASP.NET usando controladores e exibições:
OnPostAsync
no exemplo anterior é semelhante ao código de controlador típico.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.
O arquivo de exibiçã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>
A marca HTML renderizada 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 de manipulador OnPostAsync
chama o método auxiliar RedirectToPage. RedirectToPage
retorna uma instância de RedirectToPageResult. RedirectToPage
:
RedirectToAction
ou RedirectToRoute
(usado em controladores e exibições)./Index
). RedirectToPage
é descrito em detalhes na seção Geração de URLs para páginas.Com erros de validação passados para o servidor:
OnPostAsync
chama o método auxiliar Page. Page
retorna uma instância de PageResult. Retornar Page
é semelhante a como as ações em controladores retornam View
. PageResult
é o tipo de retorno padrão para um método de manipulador. Um método de manipulador que retorna void
renderiza a página.public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customers.Add(Customer);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
Com erros de validação detectados pela validação do lado do cliente:
A propriedade Customer
usa o atributo [BindProperty]
para aceitar o 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]
não deve ser usado em modelos que contêm propriedades que não devem ser alteradas pelo cliente. Para obter mais informações, confira Postagem em excesso.
O Razor Pages, por padrão, associa propriedades somente com verbos não GET
. A associação a propriedades remove a necessidade de escrever código para converter dados HTTP no tipo de modelo. A associação reduz o código usando a mesma propriedade para renderizar os campos de formulário (<input asp-for="Customer.Name">
) e aceitar a entrada.
Aviso
Por motivos de segurança, você deve aceitar associar os dados da solicitação GET
às propriedades do modelo de página. Verifique a entrada do usuário antes de mapeá-la para as propriedades. Aceitar a associação de GET
é útil ao lidar com cenários que contam com a cadeia de caracteres de consulta ou com os valores de rota.
Para associar uma propriedade a solicitações GET
, defina a propriedade SupportsGet
do atributo [BindProperty]
como true
:
[BindProperty(SupportsGet = true)]
Para obter mais informações, confira ASP.NET Core Community Standup: Bind on GET discussion (YouTube).
Examinando o arquivo de exibiçã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>
<input asp-for="Customer.Name" />
associa o elemento HTML <input>
à expressão de modelo Customer.Name
.@addTagHelper
disponibiliza os Auxiliares de Marca.Index.cshtml
é a 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>
<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 PageModel
associada (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 arquivo Index.cshtml
contém o seguinte markup:
<a asp-page="./Edit" asp-route-id="@contact.Id">Edit</a> |
O auxiliar de marcas de âncora <a /a>
usou o atributo asp-route-{value}
para gerar um link para a página Edit. O link contém dados de rota com a ID de contato. Por exemplo, https://localhost:5001/Edit/1
. Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor.
O arquivo Index.cshtml
também contém a marcação para criar um botão de exclusão para cada contato de 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 de exclusão é renderizado no HTML, o formaction inclui parâmetros para:
asp-route-id
.handler
, especificado pelo atributo asp-page-handler
.Quando o botão é selecionado, uma solicitação de formulário POST
é enviada para o servidor. Por convenção, o nome do método do manipulador é selecionado com base no valor do parâmetro handler
de acordo com o esquema OnPost[handler]Async
.
Como o handler
é delete
neste exemplo, o método do manipulador OnPostDeleteAsync
é usado para processar a solicitação POST
. Se 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
:
id
da cadeia de caracteres de consulta.FindAsync
./Index
).@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 diretiva @page "{id:int}"
. A restrição de roteamento "{id:int}"
informa à página para aceitar solicitações para a página que contêm dados da rota int
. Se uma solicitação para a página não contém dados de rota que podem ser convertidos em um int
, o runtime retorna um erro HTTP 404 (não encontrado). Para tornar a ID opcional, acrescente ?
à restrição de rota:
@page "{id:int?}"
O arquivo 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");
}
}
Regras de validação:
O namespace System.ComponentModel.DataAnnotations fornece um conjunto de atributos de validação internos aplicados de forma declarativa a uma classe ou propriedade. DataAnnotations também contém atributos de formatação como [DataType]
, que ajudam com a formatação e não fornecem validação.
Considere o modelo Customer
:
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 arquivo de exibição 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 código anterior:
Inclui jQuery e scripts de validação de jQuery.
Usa o <div />
e <span />
Auxiliares de Marcação para habilitar:
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 é necessário" no formulário. Se o JavaScript estiver habilitado no cliente, o navegador exibirá o erro sem postar no servidor.
O atributo [StringLength(10)]
gera data-val-length-max="10"
no HTML renderizado. data-val-length-max
impede que navegadores insiram mais do que o comprimento máximo especificado. Se uma ferramenta como o Fiddler for usada para editar e reproduzir a postagem:
Considere o seguinte modelo Movie
:
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 de modelo às quais eles são aplicados:
Os atributos Required
e MinimumLength
indicam que uma propriedade deve ter um valor; porém, nada impede que um usuário insira um espaço em branco para atender a essa validação.
O atributo RegularExpression
é usado para limitar quais caracteres podem ser inseridos. No código anterior, "Gênero":
A "Classificação" RegularExpression
:
O atributo Range
restringe um valor a um intervalo especificado.
O atributo StringLength
define o tamanho máximo de uma propriedade de cadeia de caracteres e, opcionalmente, seu tamanho mínimo.
Os tipos de valor (como decimal
, int
, float
, DateTime
) são inerentemente necessários e não precisam do atributo [Required]
.
A página Criar para o modelo Movie
mostra erros com valores inválidos:
Para saber mais, veja:
As solicitações HEAD
permitem recuperar os cabeçalhos de um recurso específico. Diferente das solicitações GET
, as solicitações HEAD
não retornam um corpo de resposta.
Geralmente, um manipulador OnHead
é criado e chamado para solicitações HEAD
:
public void OnHead()
{
HttpContext.Response.Headers.Add("Head Test", "Handled by OnHead!");
}
O Razor Pages chama o manipulador OnGet
quando nenhum manipulador OnHead
é definido.
Os Razor Pages são protegidos pela Validação antifalsificação. O FormTagHelper injeta tokens antifalsificação em elementos de formulário HTML.
As páginas funcionam com todos os recursos do mecanismo de exibição do Razor. Layouts, parciais, modelos, Auxiliares de Marcação, _ViewStart.cshtml
e _ViewImports.cshtml
funcionam da mesma forma que exibições convencionais do Razor.
Organizaremos essa página aproveitando alguns desses recursos.
Adicione uma página de layout para 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:
@RenderBody()
é chamado.Veja página de layout para obter mais informações.
A propriedade Layout é definida em Pages/_ViewStart.cshtml
:
@{
Layout = "_Layout";
}
O layout está na pasta Pages/Shared. As páginas buscam outras exibições (layouts, modelos, parciais) hierarquicamente, iniciando na mesma pasta que a página atual. Um layout na pasta Pages/Shared pode ser usado em qualquer página do Razor na pasta Pages.
O arquivo de layout deve entrar na pasta Pages/Shared.
Recomendamos que você não coloque o arquivo de layout na pasta Views/Shared. Views/Shared é um padrão de exibições do MVC. As Páginas do Razor devem confiar na hierarquia de pasta e não nas convenções de caminho.
A pesquisa de modo de exibição de uma Página do Razor inclui a pasta Pages. Os layouts, modelos e parciais que você está usando com controladores MVC e exibições do Razor convencionais apenas funcionam.
Adicione um arquivo Pages/_ViewImports.cshtml
:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace
é explicado posteriormente no tutorial. A diretiva @addTagHelper
coloca os auxiliares de marcas internos em todas as páginas na pasta Pages.
A diretiva @namespace
definida em uma página:
@page
@namespace RazorPagesIntro.Pages.Customers
@model NameSpaceModel
<h2>Name space</h2>
<p>
@Model.Message
</p>
A diretiva @namespace
define o namespace da página. A diretiva @model
não precisa incluir o namespace.
Quando a diretiva @namespace
está contida em _ViewImports.cshtml
, o namespace especificado fornece o prefixo do namespace gerado na página que importa a diretiva @namespace
. O rest do namespace gerado (a parte do sufixo) é o caminho relativo separado por ponto entre a pasta que contém _ViewImports.cshtml
e a pasta que contém a página.
Por exemplo, a classe PageModel
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 arquivo Pages/_ViewImports.cshtml
define o seguinte namespace:
@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
O namespace gerado para o Pages/Customers/Edit.cshtml
Razor Page é o mesmo que a classe PageModel
.
@namespace
também funciona com exibições convencionais do Razor.
Considere o arquivo de exibiçã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 Pages/Create.cshtml
atualizado 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 Marcação. O arquivo de layout importou os arquivos JavaScript.
O projeto inicial do Razor 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.
A página Create
, exibida 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 estrutura de arquivos/pastas a seguir:
Pages/
Index.cshtml
Privacy.cshtml
/Clientes
Create.cshtml
Edit.cshtml
Index.cshtml
As páginas Pages/Customers/Create.cshtml
e Pages/Customers/Edit.cshtml
redirecionam para Pages/Customers/Index.cshtml
após o êxito. 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 página Pages/Customers/Index.cshtml
. Por exemplo:
Url.Page("./Index", ...)
<a asp-page="./Index">Customers Index Page</a>
RedirectToPage("./Index")
O nome da página absoluto /Index
é usado para gerar URLs para a página Pages/Index.cshtml
. 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 da pasta raiz /Pages, incluindo um /
à direita (por exemplo, /Index
). Os exemplos anteriores de geração de URL oferecem opções avançadas e recursos funcionais para codificar uma URL. A geração de URL usa roteamento e pode gerar e codificar parâmetros de acordo com o modo como a rota é definida no caminho de destino.
A Geração de URL para páginas dá suporte a nomes relativos. A tabela a seguir mostra qual página de Índice é selecionada usando diferentes parâmetros de RedirectToPage
em 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 parâmetro RedirectToPage
é combinado com o caminho da página atual para calcular o nome da página de destino.
Vinculação de nome relativo é útil ao criar sites com uma estrutura complexa. Quando nomes relativos são usados para vincular páginas em uma pasta:
Para redirecionar para uma página em uma área diferente, especifique essa área:
RedirectToPage("/Index", new { area = "Services" });
Para obter mais informações, consulte Áreas no ASP.NET Core e Convenções de rota e aplicativo do Razor no ASP.NET Core.
Dados podem ser passados para uma página com ViewDataAttribute. Propriedades com o atributo [ViewData]
têm seus valores armazenados e carregados do ViewDataDictionary.
No seguintes exemplo, o AboutModel
aplica o atributo [ViewData]
à propriedade Title
:
public class AboutModel : PageModel
{
[ViewData]
public string Title { get; } = "About";
public void OnGet()
{
}
}
Na página Sobre, acesse a propriedade Title
como uma propriedade de modelo:
<h1>@Model.Title</h1>
No layout, o título é lido a partir do dicionário ViewData:
<!DOCTYPE html>
<html lang="en">
<head>
<title>@ViewData["Title"] - WebApplication</title>
...
O ASP.NET Core expõe o TempData. Essa propriedade armazena dados até eles serem lidos. Os métodos Keep e Peek podem ser usados para examinar os dados sem exclusão. TempData
é útil para redirecionamento nos casos em que os dados são necessários para mais de uma única solicitação.
Os conjuntos de código a seguir definem 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 marcação a seguir no arquivo Pages/Customers/Index.cshtml
exibe o valor de Message
usando TempData
.
<h3>Msg: @Model.Message</h3>
O modelo de página Pages/Customers/Index.cshtml.cs
aplica o atributo [TempData]
à propriedade Message
.
[TempData]
public string Message { get; set; }
Para obter mais informações, confira TempData.
A página a seguir gera marcação para dois manipuladores usando o auxiliar de marcação asp-page-handler
:
@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 atributo asp-page-handler
é um complemento para asp-page
. asp-page-handler
gera URLs que enviam para cada um dos métodos de manipulador definidos por uma página. asp-page
não foi especificado porque a amostra está vinculando à 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 nomeados. Métodos de manipulador nomeados são criados colocando o texto no nome após On<HTTP Verb>
e antes de Async
(se houver). No exemplo anterior, os métodos de página são OnPostJoinListAsync e OnPostJoinListUCAsync. Com OnPost e Async removidos, os nomes de manipulador são JoinList
e 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 a OnPostJoinListAsync
é https://localhost:5001/Customers/CreateFATH?handler=JoinList
. O caminho da URL que envia a OnPostJoinListUCAsync
é https://localhost:5001/Customers/CreateFATH?handler=JoinListUC
.
Use a diretiva @page
para:
/Some/Other/Path
com @page "/Some/Other/Path"
.@page "item"
.id
, pode ser necessário para uma página com @page "{id}"
.Há suporte para um caminho relativo à raiz designado por um til (~
) no início do caminho. Por exemplo, @page "~/Some/Other/Path"
é o mesmo que @page "/Some/Other/Path"
.
Se você não deseja a cadeia de consulta ?handler=JoinList
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 diretiva @page
.
@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 a OnPostJoinListAsync
é https://localhost:5001/Customers/CreateFATH/JoinList
. O caminho da URL que envia a OnPostJoinListUCAsync
é https://localhost:5001/Customers/CreateFATH/JoinListUC
.
O ?
após handler
significa que o parâmetro de rota é opcional.
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 AddRazorPages que configura 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 adicionar as convenções de modelo de aplicativo para páginas. Para obter mais informações sobre convenções, consulte Convenções de autorização do Razor Pages.
Para pré-compilar exibições, consulte Compilação de exibição do Razor.
Por padrão, Razor Pages estão na raiz do diretório /Pages. Adicione WithRazorPagesAtContentRoot para especificar que Razor Pages estão na raiz de conteúdo (ContentRootPath) do aplicativo:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeFolder("/MyPages/Admin");
})
.WithRazorPagesAtContentRoot();
}
Adicione WithRazorPagesRoot para especificar que Razor Pages 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");
}
Comentários do ASP.NET Core
O ASP.NET Core é um projeto código aberto. Selecione um link para fornecer comentários:
Eventos
19 de nov., 23 - 21 de nov., 23
Participe de sessões online no Microsoft Ignite criadas para expandir suas habilidades e ajudá-lo a lidar com os problemas complexos de hoje.
Registrar agoraTreinamento
Módulo
Usar estilos CSS em uma página da Web - Training
Descubra como usar estilos CSS em uma página da Web.
Documentação
Saiba como usar layouts comuns, compartilhar diretivas e executar um código comum antes de renderizar exibições em um aplicativo ASP.NET Core.
Razor Referência de sintaxe para ASP.NET Core
Saiba mais sobre a sintaxe de marcação Razor para incorporar um código baseado em servidor em páginas da Web.
Convenções de rota e aplicativo do Razor Pages no ASP.NET Core
Descubra como as convenções do provedor de modelo de aplicativo e rota ajudam você a controlar o roteamento, a descoberta e o processamento de página.