Adicionar uma galeria de imagens a um editor de texto rico no Razor Pages

Em um aplicativo Razor Pages, a primeira ideia natural para a galeria de imagens do editor é um conjunto de handlers nomeados na página, como OnGetGallery e OnPostUpload. Para a galeria do RichTextEditor, eles são a ferramenta errada, por dois motivos específicos do Razor Pages. Esta página mostra o que fazer no lugar deles: o editor em uma Razor Page comum e as três rotas da galeria como endpoints no mesmo aplicativo.
Por que não usar handlers de página
- As URLs não se encaixam. Um handler nomeado é acessado como
/?handler=Upload. O script de upload do editor faz POST para<base>/upload, e a galeria acrescenta?folder=agalleryEndpointem cada listagem, então URLs de handler exigiriam mudanças nos dois. - Todo handler POST verifica um token antiforgery. Esse é um padrão do Razor Pages, e um bom padrão. As requisições de upload e de New folder do editor são XHRs que o próprio editor monta, e a versão atual não consegue acrescentar o token a elas. Os handlers precisariam de
[IgnoreAntiforgeryToken]no page model, o que também desativa a verificação para o formulário Save da própria página.
Os endpoints mantêm as duas coisas separadas: a página e o formulário dela mantêm a proteção completa, e as rotas JSON fazem a própria escolha explícita.
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
app.UseStaticFiles();
app.UseRteStaticFiles(); // /uploads, /richtexteditor, /rte-files.js
app.MapRazorPages(); // "/" -> Pages/Index.cshtml; its own form POST keeps antiforgery validation
app.MapRteFiles(); // GET+POST /rte/gallery, POST /rte/upload
app.Run();MapRteFiles() é o mesmo grupo de endpoints de Minimal API que os outros exemplos ASP.NET Core usam: GET e POST /rte/gallery e POST /rte/upload. UseRteStaticFiles() serve /uploads (apenas os cinco tipos de imagem), o editor em /richtexteditor e /rte-files.js.
A página
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<script src="/rte-files.js"></script>O editor é criado mais abaixo na página com galleryEndpoint: "/rte/gallery". Enviar imagens de um editor de texto rico no Razor Pages mostra essa parte junto com o formulário.
A rota de listagem
rte.MapGet("/gallery", (HttpContext http, string? folder) =>
Json(RteFiles.List(http, folder)));A resposta é a pasta como JSON: currentFolder, parentFolder (null na raiz), as subpastas e as imagens com URL, nome e tamanho. É o mesmo JSON e o mesmo código do guia de galeria do ASP.NET Core, que explica cada campo.
As rotas POST e o antiforgery
// Binding [FromForm] or IFormFile makes .NET 8 demand an antiforgery token on these endpoints.
// The editor's XHRs cannot send one, so it is switched off here and RteFiles checks the
// request's origin instead (see RequireSameOrigin). guide-notes.md has the details.
rte.MapPost("/gallery", (HttpContext http, [FromForm] string? action, [FromForm] string? folder, [FromForm] string? name) =>
Json(RteFiles.CreateFolder(http, action, folder, name)))
.DisableAntiforgery();Aqui está o segundo detalhe do Razor Pages. AddRazorPages() registra os serviços de antiforgery, mas no .NET 8 não adiciona o middleware de antiforgery que os endpoints de Minimal API usam. Um endpoint que faz binding de IFormFile ou [FromForm] ainda carrega metadados de antiforgery, então, sem .DisableAntiforgery(), todo POST para ele falhava com um 500: "contains anti-forgery metadata, but a middleware was not found that supports anti-forgery". Adicionar app.UseAntiforgery() em vez disso transforma o erro em um 400, porque o editor não envia token. Desativá-lo nessas duas rotas e verificar a origem da requisição no lugar dele é o que as faz funcionar. Upload seguro de imagens no Razor Pages explica essa verificação.
Teste
Execute o projeto de exemplo deste guia com dotnet run (SDK do .NET 8), abra http://localhost:5303 e clique em Insert Gallery. A caixa de diálogo lista as pastas e imagens em uploads/. Em um shell, a rota de upload responde sem token, enquanto o POST da própria página não:
curl -F "file=@photo.jpg" http://localhost:5303/rte/upload
{"url":"/uploads/photo-4cdaeb3e.jpg"}
curl -s -o /dev/null -w "%{http_code}\n" -d "Html=x" http://localhost:5303/
400O 400 é o Razor Pages recusando um POST de formulário sem o token antiforgery, que é exatamente o que você quer para a página.
O mesmo guia para outras plataformas
JavaScript · Next.js · PHP · ASP.NET Core · Blazor · MVC 5 · Web Forms