Integración de la Carga de Archivos de UEditor en Proyectos ASP.NET Core

La implementación de funcionalidades de carga de archivos en editores WYSIWYG a menudo requiere una integración robusta con el backend. Este artículo detalla cómo configurar la carga de archivos en UEditor utilizando la librería UEditor.Core en un entorno ASP.NET Core.

  1. Instalación de la Dependencia del Servicio

Para comenzar, es necesario agregar el paquete NuGet UEditor.Core a su proyecto. Este paquete proporciona los servicios esenciales para manejar las operaciones de UEditor en el lado del servidor.

Una vez instalado, registre el servicio de UEditor en el método ConfigureServices de su archivo Startup.cs (o Program.cs si usa el modelo de host mínimo):

using Microsoft.Extensions.DependencyInjection;
using UEditor.Core; // Asegúrate de incluir esta directiva.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Añade los servicios de UEditor al contenedor de inyección de dependencias.
        services.AddUEditorService();
        
        // ... otros servicios
    }
    // ...
}

  1. Configuración del Editor Frontend

Descargue la última versión del paquete de UEditor desde su sitio web oficial. Extraiga el contenido y colóquelo en la carpeta wwwroot/lib/ueditor (o una ubicación similar) de su proyecto ASP.NET Core.

Posteriormente, en las páginas donde desee utilizar el editor, incluya los archivos ueditor.config.js y ueditor.all.min.js. Es crucial que el archivo de configuración se cargue antes que el principal del editor.

<!-- Referencia al archivo de configuración de UEditor -->
<script src="~/lib/ueditor/ueditor.config.js" asp-append-version="true"></script>
<!-- Referencia al archivo principal del editor -->
<script src="~/lib/ueditor/ueditor.all.min.js" asp-append-version="true"></script>
<!-- Opcional: Archivo de idioma. Por ejemplo, español -->
<script src="~/lib/ueditor/lang/es/es.js" asp-append-version="true"></script>

Modifique el archivo ueditor.config.js para que el parámetro serverUrl apunte a la acción de su controlador de backend que manejará las solicitudes de UEditor. Por ejemplo, si su controlador se llama UploaderApiController y la acción es ProcessUeditorUpload, la ruta podría ser /api/editor/upload.

/* En ueditor.config.js */
// Ruta unificada para las solicitudes al servidor
// , serverUrl: "/ueditor/upload" // Comentado o eliminado
, serverUrl: "/api/editor/upload" // Nueva ruta al endpoint del backend

  1. Configuración de Archivos Estáticos para Contenido Subido

Para que los archivos cargados (imágenes, videos, documentos) sean accesibles a través de URLs, configure el middleware de archivos estáticos en el método Configure de Startup.cs. Se recomienda un directorio específico para las cargas, como /uploads.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.FileProviders;
using System.IO;

public class Startup
{
    // ... ConfigureServices

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ... otros middlewares

        // Habilita el servicio de archivos estáticos para la carpeta wwwroot
        app.UseStaticFiles();

        // Configuración para servir archivos de la carpeta de subidas
        app.UseStaticFiles(new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "uploads")),
            RequestPath = "/uploads", // Ruta URL para acceder a los archivos subidos
            OnPrepareResponse = ctx =>
            {
                // Opcional: Añadir encabezados de caché
                ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=36000");
            }
        });

        // ... otros middlewares y enrutamiento
    }
}

  1. Manejo de Caracteres Extendidos (UTF-8)

Si trabaja con idiomas que utilizan caracteres chinos o un amplio rango de Unicode, es posible que UEditor codifique el HTML de manera inesperada. Para solucionar esto, configure las opciones de codificación de texto en ConfigureServices:

using System.Text.Encodings.Web;
using System.Text.Unicode;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddUEditorService();

        // Solución para evitar la codificación HTML de caracteres no básicos.
        services.Configure<TextEncoderSettings>(options =>
        {
            options.AllowRanges(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs);
        });

        // ...
    }
    // ...
}

  1. Configuración del Backend UEditor (ueditor.json)

Cree un archivo llamado ueditor.json en el directorio raíz de su proyecto. Este archivo contendrá la configuración para las operaciones de backend de UEditor (subida de imágenes, videos, archivos, etc.). Este archivo reemplaza el config.json tradicional de UEditor .NET. Adapte las rutas de guardado (por ejemplo, "uploads/images") para que coincidan con la configuración de archivos estáticos definida anteriormente.

/* Configuración para la comunicación del frontend con el backend */
{
  /* Configuración para la carga de imágenes */
  "imageActionName": "uploadimage", /* Acción a ejecutar para subir imágenes */
  "imageFieldName": "upfile", /* Nombre del campo del formulario para la imagen */
  "imageMaxSize": 2048000, /* Límite de tamaño, en Bytes (2MB) */
  "imageAllowFiles": [ ".png", ".jpg", ".jpeg", ".gif", ".bmp" ], /* Formatos permitidos */
  "imageCompressEnable": true, /* Habilitar compresión */
  "imageCompressBorder": 1600, /* Borde máximo para compresión */
  "imageInsertAlign": "none", /* Alineación de la imagen insertada */
  "imageUrlPrefix": "/", /* Prefijo de URL para acceder a las imágenes */
  "imagePathFormat": "uploads/images/{yyyy}{mm}{dd}/{time}{rand:6}", /* Ruta y formato de nombre de archivo */

  /* Configuración para la carga de archivos */
  "fileActionName": "uploadfile", /* Acción para subir archivos */
  "fileFieldName": "upfile", /* Nombre del campo del formulario para el archivo */
  "filePathFormat": "uploads/files/{yyyy}{mm}{dd}/{time}{rand:6}", /* Ruta y formato de nombre de archivo */
  "fileUrlPrefix": "/", /* Prefijo de URL para acceder a los archivos */
  "fileMaxSize": 51200000, /* Límite de tamaño, en Bytes (50MB) */
  "fileAllowFiles": [
    ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb",
    ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
    ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls",
    ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
  ], /* Formatos de archivo permitidos */

  /* Listado de archivos subidos */
  "fileManagerActionName": "listfile", /* Acción para listar archivos */
  "fileManagerListPath": "uploads/files", /* Directorio a listar */
  "fileManagerListSize": 20, /* Cantidad de archivos por página */
  "fileManagerUrlPrefix": "/", /* Prefijo de URL */
  "fileManagerAllowFiles": [
    ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".flv", ".swf", ".mkv", ".avi", ".rm", ".rmvb",
    ".mpeg", ".mpg", ".ogg", ".ogv", ".mov", ".wmv", ".mp4", ".webm", ".mp3", ".wav", ".mid",
    ".rar", ".zip", ".tar", ".gz", ".7z", ".bz2", ".cab", ".iso", ".doc", ".docx", ".xls",
    ".xlsx", ".ppt", ".pptx", ".pdf", ".txt", ".md", ".xml"
  ] /* Tipos de archivo a listar */
  // ... (otras configuraciones como video, scrawl, etc.)
}

  1. Implementación del Controlador de Backend

Cree un controlador ASP.NET Core que actuará como el endpoint de UEditor. Inyecte UEditorService y utilice su método UploadAndGetResponse para manejar las solicitudes del editor.

Controlador Básico:

using Microsoft.AspNetCore.Mvc;
using UEditor.Core;

namespace MyApplication.Web.Controllers
{
    public class UploaderApiController : Controller
    {
        private readonly UEditorService _uploaderService;

        public UploaderApiController(UEditorService editorService)
        {
            _uploaderService = editorService;
        }

        // Este endpoint manejará todas las solicitudes de carga y gestión de archivos de UEditor.
        [Route("api/editor/upload")] // La URL debe coincidir con serverUrl en ueditor.config.js
        [HttpGet, HttpPost]
        public ContentResult ProcessUeditorUpload()
        {
            var serverResponse = _uploaderService.UploadAndGetResponse(HttpContext);
            return Content(serverResponse.Result, serverResponse.ContentType);
        }
    }
}

Controlador con Áreas y Rutas Personalizadas:

Si su aplicación utiliza áreas o requiere una configuración de enrutamiento más compleja, puede definir el controlador de la siguiente manera:

using Microsoft.AspNetCore.Mvc;
using UEditor.Core;
using Microsoft.AspNetCore.Authorization; // Para [Authorize] y [AllowAnonymous]
using Microsoft.AspNetCore.Antiforgery; // Para [IgnoreAntiforgeryToken]

namespace MyProject.Admin.Controllers
{
    [Area("Admin")] // Define que este controlador pertenece al área "Admin".
    [Authorize] // Requiere autenticación para acceder.
    [IgnoreAntiforgeryToken] // Puede ser necesario para que UEditor funcione correctamente con peticiones POST.
    [Route("admin/[controller]")] // Ruta base para este controlador dentro del área.
    public class MediaUploadController : Controller
    {
        private readonly UEditorService _mediaUploadService;

        public MediaUploadController(UEditorService editorUploadService)
        {
            _mediaUploadService = editorUploadService;
        }

        [Route("ueditor-handler")] // Ruta específica para la acción de UEditor.
        [AllowAnonymous] // Permite el acceso sin autenticación a este endpoint si es necesario.
        [HttpGet, HttpPost]
        public ContentResult UEditorHandler()
        {
            var editorResponse = _mediaUploadService.UploadAndGetResponse(HttpContext);
            return Content(editorResponse.Result, editorResponse.ContentType);
        }
    }
}

En este caso, la serverUrl en ueditor.config.js debería ser /admin/MediaUpload/ueditor-handler.

  1. Integración del Editor en Vistas CSHTML

En su archivo .cshtml, inserte un elemento <textarea> que servirá como contenedor para el editor UEditor. Luego, inicialice el editor con JavaScript, asegurándose de que el ID del textarea coincida con el ID utilizado en UE.getEditor().

<!-- Contenedor para el editor WYSIWYG -->
<div class="editor-container">
    <textarea id="articleContentEditor" name="articleContent" style="width:100%; height:500px;"></textarea>
</div>

<!-- Scripts de UEditor (asegúrese de que las rutas sean correctas) -->
<script src="~/lib/ueditor/ueditor.config.js" asp-append-version="true"></script>
<script src="~/lib/ueditor/ueditor.all.min.js" asp-append-version="true"></script>
<script src="~/lib/ueditor/lang/es/es.js" asp-append-version="true"></script>

<!-- Script para inicializar el editor -->
<script type="text/javascript">
    var editorInstance = UE.getEditor('articleContentEditor', {
        initialFrameHeight: 500,
        // Opcional: especificar el serverUrl aquí si no está en ueditor.config.js
        serverUrl: '/api/editor/upload' 
    });

    // Ejemplo de cómo asignar contenido inicial después de que el editor esté listo
    editorInstance.ready(function() {
        // editorInstance.setContent('Contenido inicial aquí.');
    });
</script>

Integración con Frameworks JS (Ej. Layui) y Envío AJAX:

Si está utilizando un framework como Layui, la inicialización y gestión del editor puede integrarse en el flujo de su aplicación de la siguiente manera:

var articleEditorInstance; // Una instancia global del editor.

layui.use(['layer', 'form'], function () {
    var layer = layui.layer;
    var form = layui.form;

    // Función para gestionar la vida del editor (crear/destruir)
    function manageEditorLifecycle(elementId, initialHtmlContent) {
        if (articleEditorInstance) {
            articleEditorInstance.destroy(); // Destruye la instancia existente para evitar duplicados.
        }
        articleEditorInstance = UE.getEditor(elementId, {
            initialFrameHeight: 500
        });

        // Espera a que el editor esté listo antes de intentar asignar contenido.
        articleEditorInstance.ready(function () {
            if (initialHtmlContent) {
                articleEditorInstance.setContent(initialHtmlContent);
            }
        });
    }

    // Ejemplo de inicialización, quizás después de abrir un popup de edición.
    // manageEditorLifecycle('articleContentEditor', '<p>Este es el contenido de un artículo.</p>');

    // Manejo del envío de formularios con Layui
    form.on('submit(submitArticleForm)', function (data) {
        var formData = data.field; // Obtiene los campos del formulario.

        // Asegúrate de que el editor está inicializado y obtiene el contenido.
        var editorContent = articleEditorInstance ? articleEditorInstance.getContent() : '';
        formData.ArticleHtmlContent = editorContent; // Añade el contenido del editor al FormData.

        $.ajax({
            type: 'POST',
            url: "/Admin/Article/SaveEditedArticle", // Endpoint para guardar el artículo.
            data: formData,
            dataType: "json",
            success: function (response) {
                if (response.code === 0) { // Suponiendo que 'code: 0' indica éxito.
                    layer.msg('Artículo guardado correctamente!', { icon: 1 });
                    // Posiblemente cerrar el popup o refrescar la tabla.
                } else {
                    layer.msg('Error al guardar: ' + response.msg, { icon: 2 });
                }
            },
            error: function (xhr, status, error) {
                console.error("Error AJAX:", status, error);
                layer.msg('Fallo en la comunicación con el servidor.', { icon: 2 });
            }
        });
        return false; // Previene el envío estándar del formulario.
    });
});

  1. Corección de la Lista Blanca XSS (Atributos 'class', 'style')

Por defecto, UEditor puede "limpiar" atributos como class y style de las etiquetas HTML, lo que impide aplicar estilos o clases CSS. Para permitir estos atributos, debe modificar la sección whitList en ueditor.config.js. Tenga en cuenta que al hacer esto, está relajando las protecciones XSS, lo que podría introducir riesgos de seguridad si no se gestiona adecuadamente el contenido.

/* En ueditor.config.js, alrededor de la línea 366 (puede variar) */
// xss过滤白名单 名单来源: https://raw.githubusercontent.com/leizongmin/js-xss/master/lib/default.js
,whitList: {
    a:      ['target', 'href', 'title', 'class', 'style'], // Permite class y style en enlaces
    abbr:   ['title', 'class', 'style'],
    address: ['class', 'style'],
    // ... otros elementos HTML
    div:    ['class', 'style'], // Ejemplo: permitir class y style en div
    img:    ['src', 'alt', 'title', 'width', 'height', 'id', '_src', 'loadingclass', 'class', 'data-latex', 'style'], // Importante para imágenes
    p:      ['class', 'style'], // Ejemplo: permitir class y style en párrafos
    span:   ['class', 'style'], // Ejemplo: permitir class y style en span
    table:  ['width', 'border', 'align', 'valign', 'class', 'style'], // Permite class y style en tablas
    video:  ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width', 'class', 'style'] // Permite class y style en videos
    // Asegúrese de revisar y añadir 'class', 'style' a todos los elementos necesarios.
}

Al realizar esta modificación, se recomienda encarecidamente implementar otras capas de seguridad a nivel de servidor para validar y desinfectar el HTML recibido, mitigando así posibles vulnerabilidades XSS.

Etiquetas: ASP.NET Core ueditor C# File Upload JavaScript

Publicado el 10-26 08:29