Añadir una galería de imágenes a un editor de texto enriquecido en Razor Pages

El cuadro de diálogo Insert Gallery del ejemplo de Razor Pages con las carpetas blog, products y team y cuatro fotos subidas, una de ellas seleccionada

En una aplicación Razor Pages, la primera idea natural para la galería de imágenes del editor es un conjunto de handlers con nombre en la página, como OnGetGallery y OnPostUpload. Para la galería de RichTextEditor son la herramienta equivocada, por dos motivos propios de Razor Pages. Esta página muestra qué hacer en su lugar: el editor en una Razor Page normal y las tres rutas de la galería como endpoints en la misma aplicación.

Por qué no page handlers

  • Las URL no encajan. A un handler con nombre se llega como /?handler=Upload. El script de carga del editor envía a <base>/upload, y la galería añade ?folder= a galleryEndpoint en cada listado, así que las URL de handler necesitarían cambios en ambos.
  • Cada handler POST comprueba un token antiforgery. Es un valor predeterminado de Razor Pages, y uno bueno. Las solicitudes de carga y de New folder del editor son XHR que construye el propio editor, y la versión actual no puede añadirles el token. Los handlers necesitarían [IgnoreAntiforgeryToken] en el modelo de página, lo que también desactiva la comprobación para el formulario Save de la propia página.

Los endpoints mantienen ambas cosas separadas: la página y su formulario conservan toda su protección, y las rutas JSON toman su propia decisión explícita.

Program.cs

C# · 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() es el mismo grupo de endpoints de Minimal API que usan los demás ejemplos de ASP.NET Core: GET y POST /rte/gallery y POST /rte/upload. UseRteStaticFiles() sirve /uploads (solo los cinco tipos de imagen), el editor en /richtexteditor y /rte-files.js.

La página

Razor · Pages/Index.cshtml
<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>

El editor se crea más abajo en la página con galleryEndpoint: "/rte/gallery". Subir imágenes desde un editor de texto enriquecido en Razor Pages muestra esa parte junto con el formulario.

La ruta del listado

C# · RteEndpoints.cs
rte.MapGet("/gallery", (HttpContext http, string? folder) =>
    Json(RteFiles.List(http, folder)));

La respuesta es la carpeta en JSON: currentFolder, parentFolder (null en la raíz), las subcarpetas y las imágenes con URL, nombre y tamaño. Es el mismo JSON y el mismo código que en la guía de la galería para ASP.NET Core, que explica cada campo.

Las rutas POST y el antiforgery

C# · RteEndpoints.cs
// 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();

Este es el segundo detalle de Razor Pages. AddRazorPages() registra los servicios antiforgery, pero en .NET 8 no añade el middleware antiforgery que usan los endpoints de Minimal API. Un endpoint que enlaza IFormFile o [FromForm] sigue llevando metadatos antiforgery, así que sin .DisableAntiforgery() todos los POST a él fallaban con un 500: "contains anti-forgery metadata, but a middleware was not found that supports anti-forgery". Añadir app.UseAntiforgery() en su lugar lo convierte en un 400, porque el editor no envía ningún token. Desactivarlo en estas dos rutas, y comprobar en su lugar el origen de la solicitud, es lo que hace que funcionen. Carga segura de imágenes en Razor Pages explica esa comprobación.

Pruébalo

Ejecuta el proyecto de ejemplo de esta guía con dotnet run (SDK de .NET 8), abre http://localhost:5303 y haz clic en Insert Gallery. El cuadro de diálogo muestra las carpetas y las imágenes de uploads/. Desde un shell, la ruta de carga responde sin token, pero el POST propio de la página no:

Shell
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/
400

El 400 es Razor Pages rechazando un envío de formulario sin su token antiforgery, que es lo que quieres para la página.

La misma guía para otras plataformas

JavaScript · Next.js · PHP · ASP.NET Core · Blazor · MVC 5 · Web Forms

Para seguir leyendo