using System.Collections.Concurrent;
using System.ComponentModel;
using System.Web;
using AngleSharp;
using Dpz.Core.EnumLibrary;
using Dpz.Core.Public.ViewModel.Response;
using Dpz.Core.Service.ObjectStorage.Services;
using Markdig;
using Microsoft.AspNetCore.Mvc.ModelBinding;

#pragma warning disable CS0162

namespace Dpz.Core.Web.Library;

public static class WebToolsExtensions
{
    public const string DefaultGroupId = "5e09be6ae22e4d3b7810b6c7";

    public static readonly Dictionary<int, string> Wind = new()
    {
        { 0, "静风" },
        { 1, "软风" },
        { 2, "轻风" },
        { 3, "微风" },
        { 4, "和风" },
        { 5, "清风" },
        { 6, "强风" },
        { 7, "疾风" },
        { 8, "大风" },
        { 9, "强大风" },
        { 10, "超强大风" },
        { 11, "暴风" },
        { 12, "飓风" },
    };

    /// <summary>
    /// 缓存取消令牌
    /// </summary>
    public static readonly ConcurrentDictionary<
        string,
        CancellationTokenSource
    > CancellationTokens = new();

    /// <summary>
    /// 系统启动时间
    /// </summary>
    public static readonly DateTime StartTime = new(2018, 2, 11, 13, 56, 24, DateTimeKind.Local);

    /// <summary>
    /// 设置标题
    /// </summary>
    /// <param name="controller"></param>
    /// <param name="title"></param>
    public static void SetTitle(this Controller controller, string title)
    {
        controller.ViewData["Title"] = $"{title} - 叫我阿胖";
    }

    /// <summary>
    /// 是否为ajax请求
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    public static bool IsAjax(this HttpRequest request)
    {
        var with = request.Headers["X-Requested-With"];
        return string.Equals(with, "XMLHttpRequest", StringComparison.OrdinalIgnoreCase);
    }

    /// <summary>
    /// 获取枚举描述
    /// </summary>
    /// <param name="e"></param>
    /// <returns></returns>
    public static string GetDescription(this Enum e)
    {
        var value = e.ToString();
        var attribute = e.GetType().GetField(value)?.GetCustomAttribute<DescriptionAttribute>();
        return attribute?.Description ?? "";
    }

    /// <summary>
    /// 显示文件可读大小
    /// </summary>
    /// <param name="length"></param>
    /// <returns></returns>
    public static string FileSize(this long length)
    {
        var sizeText = length + " bytes";
        if (length > 1024m && length < 1024m * 1024m * 3)
        {
            sizeText = (length / 1024m).ToString("F") + " KB";
        }
        else if (length > 1024m * 1024m * 3 && length < 1024m * 1024m * 1024m)
        {
            sizeText = (length / 1024m / 1024m).ToString("F") + " MB";
        }
        else if (length > 1024m * 1024m * 1024m)
        {
            sizeText = (length / 1024m / 1024m / 1024m).ToString("F") + " GB";
        }

        return sizeText;
    }

    /// <summary>
    /// 从markdown转成html,并从html内容中清除链接的跳转平台,并且在新标签页打开
    /// </summary>
    /// <param name="content"></param>
    /// <returns></returns>
    public static async Task<string> ClearLinkAsync(this string content)
    {
        var pipeline = new MarkdownPipelineBuilder()
            .UsePipeTables()
            .UseTaskLists()
            .UseEmphasisExtras()
            .UseAutoIdentifiers()
            .UseAdvancedExtensions()
            .DisableHtml()
            .Build();
        var detail = Markdown.ToHtml(content, pipeline);
        var context = BrowsingContext.New(Configuration.Default);
        var document = await context.OpenAsync(y => y.Content(detail));
        var images = document.GetElementsByTagName("img");
        foreach (var image in images)
        {
            var src = image.GetAttribute("src");
            image.SetAttribute("class", "lazy");
            image.SetAttribute("loading", "lazy");
            image.SetAttribute("src", $"{Program.LibraryHost}/loaders/bars.svg");
            image.SetAttribute("data-src", src ?? $"{Program.AssetsHost}/images/notfound.png");
        }

        var links = document.GetElementsByTagName("a");
        foreach (var item in links)
        {
            var href = item.GetAttribute("href");
            if (href != null)
            {
                var uri = new Uri(href);
                var parameters = HttpUtility.ParseQueryString(uri.Query);
                var target = parameters["target"];
                if (!string.IsNullOrEmpty(target))
                {
                    item.SetAttribute("href", target);
                }

                item.SetAttribute("target", "_blank");
            }
        }

        return document.Body?.InnerHtml ?? "";
    }

    public static async Task DeleteMusicAsync(
        this IObjectStorageOperation objectStorageService,
        MusicResponse musicResponse
    )
    {
        if (musicResponse == null)
        {
            throw new ArgumentNullException(nameof(musicResponse));
        }
        if (string.IsNullOrEmpty(musicResponse.MusicUrl))
        {
            throw new ArgumentException(
                "property MusicUrl is empty or null",
                nameof(musicResponse.MusicUrl)
            );
        }

        await objectStorageService.DeleteAsync(musicResponse.MusicUrl);

        if (!string.IsNullOrEmpty(musicResponse.CoverUrl))
        {
            await objectStorageService.DeleteAsync(musicResponse.CoverUrl);
        }

        if (!string.IsNullOrEmpty(musicResponse.LyricUrl))
        {
            await objectStorageService.DeleteAsync(musicResponse.LyricUrl);
        }
    }

    public static ModelStateResult CheckModelState(this ModelStateDictionary modelState)
    {
        if (modelState.IsValid)
        {
            var messages = modelState
                .SelectMany(x => x.Value?.Errors ?? new ModelErrorCollection())
                .Select(x => x.ErrorMessage)
                .Where(x => !string.IsNullOrEmpty(x))
                .ToList();
            if (messages.Count == 0)
            {
                return new ModelStateResult(false, []);
            }

            return new ModelStateResult(true, messages);
        }

        return new ModelStateResult(false, []);
    }

    private static bool TryConvertClaimValue(string raw, Type targetType, out object? value)
    {
        if (targetType == typeof(string))
        {
            value = raw;
            return true;
        }

        var underlyingType = Nullable.GetUnderlyingType(targetType);
        if (underlyingType != null)
        {
            if (string.IsNullOrWhiteSpace(raw))
            {
                value = null;
                return true;
            }

            return TryConvertClaimValue(raw, underlyingType, out value);
        }

        if (targetType.IsEnum)
        {
            if (Enum.TryParse(targetType, raw, true, out var enumValue))
            {
                value = enumValue;
                return true;
            }

            value = null;
            return false;
        }

        if (targetType == typeof(DateTime))
        {
            if (
                DateTime.TryParse(
                    raw,
                    CultureInfo.InvariantCulture,
                    DateTimeStyles.RoundtripKind,
                    out var date
                )
            )
            {
                value = date;
                return true;
            }

            if (DateTime.TryParse(raw, out date))
            {
                value = date;
                return true;
            }

            value = null;
            return false;
        }

        if (targetType == typeof(Guid))
        {
            if (Guid.TryParse(raw, out var guid))
            {
                value = guid;
                return true;
            }

            value = null;
            return false;
        }

        var converter = TypeDescriptor.GetConverter(targetType);
        if (converter.CanConvertFrom(typeof(string)))
        {
            try
            {
                value = converter.ConvertFromInvariantString(raw);
                return true;
            }
            catch
            {
                // ignored, fallback below
            }
        }

        try
        {
            value = Convert.ChangeType(raw, targetType, CultureInfo.InvariantCulture);
            return true;
        }
        catch
        {
            value = null;
            return false;
        }
    }

    extension(ClaimsPrincipal? principal)
    {
        /// <summary>
        /// 用户ID
        /// </summary>
        public string? UserId => principal?.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        /// <summary>
        /// 严格获取用户ID
        /// <exception cref="InvalidCredentialException">如果用户未登录,将会引发此异常</exception>
        /// </summary>
        public string RequiredUserId => principal?.UserId ?? throw new InvalidCredentialException();

        /// <summary>
        /// 用户信息
        /// </summary>
        public VmUserInfo? UserInfo => principal.GetUserInfo();

        /// <summary>
        /// 严格获取用户信息
        /// <exception cref="InvalidCredentialException">如果用户未登录,将会引发此异常</exception>
        /// </summary>
        public VmUserInfo RequiredUserInfo =>
            principal?.UserInfo ?? throw new InvalidCredentialException();

        /// <summary>
        /// 权限
        /// </summary>
        public Permissions? Permissions => principal.ParsePermissions();

        /// <summary>
        /// 是否具有系统权限
        /// </summary>
        public bool HasSystem => principal.HasSystemPermission();

        private VmUserInfo? GetUserInfo()
        {
            if (principal is not { Authenticated: true })
            {
                return null;
            }

            var userInfo = VmUserInfo.Empty();
            var writeableProperties = VmUserInfo.GetCanWriteProperties();

            foreach (var claims in principal.Claims)
            {
                if (!writeableProperties.TryGetValue(claims.Type, out var property))
                {
                    continue;
                }

                var setter = ApplicationTools.GetSetter<VmUserInfo>(property.Name);
                if (setter == null)
                {
                    continue;
                }

                if (
                    !TryConvertClaimValue(
                        claims.Value,
                        property.PropertyType,
                        out var convertedValue
                    )
                )
                {
                    continue;
                }

                setter(userInfo, convertedValue);
            }

            return userInfo;
        }

        private Permissions? ParsePermissions()
        {
            var permissionClaim = principal?.FindFirst("Permissions")?.Value;
            if (permissionClaim is null)
            {
                return null;
            }
            if (Enum.TryParse<Permissions>(permissionClaim, ignoreCase: true, out var permissions))
            {
                return permissions;
            }
            return null;
        }

        private bool HasSystemPermission()
        {
            var permissions = principal?.ParsePermissions();
            return permissions.HasValue
                && (permissions.Value & Permissions.System) == Permissions.System;
        }

        /// <summary>
        /// 是否已认证
        /// </summary>
        public bool Authenticated => principal?.Identity?.IsAuthenticated == true;
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

上述代码是一个 C# 类库,名为 WebToolsExtensions,它包含了一系列的扩展方法和工具函数,主要用于 ASP.NET Core Web 应用程序的开发。以下是对代码中各个部分功能的详细解释:

1. 常量和字段

  • Version: 获取当前应用程序的版本。
  • DefaultGroupId: 定义一个默认的组 ID。
  • Wind: 一个字典,映射风速等级到中文描述。
  • StartTime: 系统启动时间的静态字段。

2. 用户身份相关方法

  • GetIdentity: 从 ClaimsPrincipal 中提取用户信息,返回一个 VmUserInfo 对象。如果用户未登录,则返回 null
  • GetStrictIdentity: 获取用户身份信息,如果未登录则抛出 InvalidCredentialException
  • GetIdentity (HttpContext): 根据提供的 TokenManagement 验证 JWT 令牌并获取用户身份信息。
  • ValidationJwtBearer: 验证 JWT 令牌的有效性,设置 HttpContext.User
  • GetIdentity (Controller): 从控制器的 ViewData 中获取当前用户信息。

3. 权限和登录状态检查

  • IsAdmin: 检查当前用户是否具有管理员权限。
  • IsLogin: 检查用户是否已登录。

4. 请求和会话相关方法

  • GetUserInfo: 获取当前用户信息,基于 Cookie 认证。
  • SessionId: 获取当前会话 ID。
  • GetIpAddress: 获取请求的 IP 地址,支持 X-Forwarded-For 头。

5. 视图和 AJAX 相关方法

  • SetTitle: 设置页面标题。
  • IsAjax: 检查请求是否为 AJAX 请求。

6. 文章标签和枚举描述

  • ArticleTagsAsync: 异步获取文章标签并返回 SelectListItem 列表。
  • GetDescription: 获取枚举值的描述属性。

7. 文件大小格式化

  • FileSize: 将文件大小(以字节为单位)格式化为可读的字符串(如 KB、MB、GB)。

8. 控制器公共操作

  • GetPublicAction: 获取所有公共控制器操作的方法,返回一个字典,键为控制器名称,值为方法名称列表。

9. 网页数据源转换

  • ToGridManagerSource: 将分页列表转换为适合 GridManager 的数据源格式。

10. HTML 清理和 Markdown 转换

  • CustomHtmlSanitizer: 创建一个自定义的 HTML 清理器,基于配置文件中的规则。
  • ClearLinkAsync: 将 Markdown 内容转换为 HTML,并清理链接,确保在新标签页中打开。

11. URI 处理

  • ForceHttps: 将给定的 URL 强制转换为 HTTPS。

12. 音乐文件删除

  • DeleteMusicAsync: 异步删除音乐及其相关文件(封面、歌词)。

13. 配置获取

  • GetUpyunHost: 从配置中获取 Upyun 的主机地址。

14. 模型状态检查

  • CheckModelState: 检查模型状态是否有效,并返回相应的结果。

总结

这个类库提供了多种实用的扩展方法,旨在简化 ASP.NET Core Web 应用程序中的常见任务,如用户身份验证、请求处理、数据格式化、HTML 清理等。通过这些工具,开发人员可以更高效地构建和维护 Web 应用程序。

评论加载中...