using AngleSharp;
using AngleSharp.Dom;
using Ganss.Xss;
using Markdig;
using IConfiguration = Microsoft.Extensions.Configuration.IConfiguration;

namespace Dpz.Core.Web.Library;

/// <summary>
/// Mumble 视图帮助类,提供图片处理和 Markdown 转换等功能
/// </summary>
public class MumbleViewHelper(IConfiguration configuration)
{
    private readonly string _cdnHost = configuration["upyun:Host"] ?? string.Empty;
    private readonly IBrowsingContext _context = BrowsingContext.New(Configuration.Default);
    private readonly HtmlSanitizer _sanitizer = CreateSanitizer(configuration);

    /// <summary>
    /// 判断图片是否为自有 CDN 图片
    /// </summary>
    /// <param name="imageUrl">图片 URL</param>
    /// <returns>是否为自有 CDN 图片</returns>
    private bool IsOwnCdnImage(string? imageUrl)
    {
        if (string.IsNullOrWhiteSpace(_cdnHost) || string.IsNullOrWhiteSpace(imageUrl))
        {
            return false;
        }

        try
        {
            var imageUri = new Uri(imageUrl, UriKind.Absolute);
            var cdnUri = new Uri(_cdnHost, UriKind.Absolute);
            return string.Equals(imageUri.Host, cdnUri.Host, StringComparison.OrdinalIgnoreCase);
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 获取缩略图 URL
    /// </summary>
    /// <param name="imageUrl">原图 URL</param>
    /// <returns>缩略图 URL</returns>
    public string GetThumbnailUrl(string imageUrl)
    {
        return IsOwnCdnImage(imageUrl) ? imageUrl + "!thumb" : imageUrl;
    }

    /// <summary>
    /// 处理 Markdown 并提取图片
    /// </summary>
    /// <param name="html">HTML 内容</param>
    /// <param name="sanitize">是否进行 XSS 清理,默认为 true</param>
    /// <returns>处理后的内容和图片列表</returns>
    public async Task<(string content, List<string> images)> ProcessHtmlAndExtractImages(
        string html,
        bool sanitize = true
    )
    {
        var document = await _context.OpenAsync(y => y.Content(html));
        var imageUrls = new List<string>();

        ProcessLinks(document);
        ExtractImages(document, imageUrls);

        var content = sanitize
            ? _sanitizer.Sanitize(document.Body?.InnerHtml ?? string.Empty)
            : document.Body?.InnerHtml ?? string.Empty;

        return (content, imageUrls);
    }

    /// <summary>
    /// 从 Markdown 提取图片(用于历史记录)
    /// </summary>
    /// <param name="markdown">Markdown 内容</param>
    /// <returns>处理后的 HTML 内容和图片列表</returns>
    public async Task<(string content, List<string> images)> ExtractImagesFromMarkdown(
        string markdown
    )
    {
        var pipeline = new MarkdownPipelineBuilder().DisableHtml().Build();
        var html = Markdown.ToHtml(markdown, pipeline);
        var document = await _context.OpenAsync(x => x.Content(html));
        var imageUrls = new List<string>();

        ExtractImages(document, imageUrls);

        return (document.Body?.InnerHtml ?? string.Empty, imageUrls);
    }

    /// <summary>
    /// 处理链接,添加 target 和 rel 属性
    /// </summary>
    private static void ProcessLinks(IDocument document)
    {
        var links = document.GetElementsByTagName("a");
        foreach (var link in links)
        {
            var href = link.GetAttribute("href");
            if (
                href != null
                && !href.StartsWith("javascript", StringComparison.CurrentCultureIgnoreCase)
            )
            {
                link.SetAttribute("target", "_blank");
                link.SetAttribute("rel", "noopener noreferrer");
            }
        }
    }

    /// <summary>
    /// 提取并移除图片元素
    /// </summary>
    private static void ExtractImages(IDocument document, List<string> imageUrls)
    {
        var images = document.GetElementsByTagName("img");
        var imagesToRemove = new List<IElement>();

        foreach (var img in images)
        {
            var src = img.GetAttribute("src");
            if (
                !string.IsNullOrWhiteSpace(src)
                && !src.Contains("notfound.png", StringComparison.OrdinalIgnoreCase)
            )
            {
                imageUrls.Add(src);
                imagesToRemove.Add(img);
            }
        }

        foreach (var img in imagesToRemove)
        {
            img.Remove();
        }
    }

    /// <summary>
    /// 创建 HtmlSanitizer 实例
    /// </summary>
    private static HtmlSanitizer CreateSanitizer(IConfiguration configuration)
    {
        var rule = configuration.GetSection("XSS").Get<CrossSiteRule>();
        return rule?.CustomHtmlSanitizer() as HtmlSanitizer ?? new HtmlSanitizer();
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个名为 MumbleViewHelper 的 C# 工具类,主要用于处理网页内容中的 HTML、Markdown 和图片。让我详细解释一下这个类的功能和实现:

类的整体结构

这是一个使用主构造函数的类(C# 12+ 特性),接收一个 IConfiguration 参数用于获取配置信息。

主要字段

  • _cdnHost: 从配置中获取的 CDN 主机地址
  • _context: AngleSharp 的浏览上下文,用于解析 HTML
  • _sanitizer: HTML 清理器,用于防止 XSS 攻击

核心功能

1. CDN 图片识别 (IsOwnCdnImage)

private bool IsOwnCdnImage(string? imageUrl)
  • 判断给定的图片 URL 是否属于自己的 CDN
  • 通过比较图片 URL 的主机名与配置的 CDN 主机名来判断

2. 缩略图处理 (GetThumbnailUrl)

public string GetThumbnailUrl(string imageUrl)
  • 如果是自有 CDN 图片,在 URL 后添加 "!thumb" 参数获取缩略图
  • 否则返回原图 URL

3. HTML 处理和图片提取 (ProcessHtmlAndExtractImages)

public async Task<(string content, List<string> images)> ProcessHtmlAndExtractImages(string html, bool sanitize = true)

这是核心方法,功能包括:

  • 解析 HTML 内容
  • 处理链接(添加安全属性)
  • 提取图片 URL 并从内容中移除图片元素
  • 可选择性地进行 XSS 清理
  • 返回处理后的内容和图片列表

4. Markdown 图片提取 (ExtractImagesFromMarkdown)

public async Task<(string content, List<string> images)> ExtractImagesFromMarkdown(string markdown)
  • 将 Markdown 转换为 HTML
  • 提取其中的图片信息
  • 主要用于处理历史记录

辅助方法

  • 为所有链接添加 target="_blank"rel="noopener noreferrer" 属性
  • 提高安全性,防止新窗口访问原页面的 window 对象

ExtractImages

  • 遍历文档中的所有图片元素
  • 提取图片的 src 属性到列表中
  • 过滤掉 "notfound.png" 图片
  • 从文档中移除图片元素

CreateSanitizer

  • 根据配置创建 HTML 清理器
  • 用于防止 XSS 攻击

使用的第三方库

  • AngleSharp: HTML/XML 解析库
  • Ganss.Xss: XSS 防护库
  • Markdig: Markdown 解析库

典型使用场景

这个类适用于类似微博、博客或社交媒体平台,需要:

  • 处理用户输入的 HTML 或 Markdown 内容
  • 提取内容中的图片进行单独展示
  • 确保内容安全性(防 XSS)
  • 优化图片显示(缩略图)

整体来说,这是一个功能完善的内容处理工具类,特别适合处理富文本内容和图片管理的场景。

评论加载中...