增加TooManyRequests
石头 authored at 2025-07-13 21:05:51
4.94 KiB
X
namespace NewLife.Web;

/// <summary>资源定位。无限制解析Url地址</summary>
public class UriInfo
{
    #region 属性
    /// <summary>协议</summary>
    public String? Scheme { get; set; }

    /// <summary>主机</summary>
    public String? Host { get; set; }

    /// <summary>端口</summary>
    public Int32 Port { get; set; }

    /// <summary>路径</summary>
    public String? AbsolutePath { get; set; }

    /// <summary>查询</summary>
    public String? Query { get; set; }

    /// <summary>路径与查询</summary>
    public String? PathAndQuery
    {
        get
        {
            if (Query.IsNullOrEmpty()) return AbsolutePath;

            if (Query[0] == '?') return AbsolutePath + Query;

            return $"{AbsolutePath}?{Query}";
        }
    }

    /// <summary>主机与端口。省略默认端口</summary>
    public String? Authority
    {
        get
        {
            if (Port == 0) return Host;
            if (Host.IsNullOrEmpty()) return Host;

            if (Scheme.EqualIgnoreCase("http", "ws"))
                return Port == 80 ? Host : $"{Host}:{Port}";
            else if (Scheme.EqualIgnoreCase("https", "wss"))
                return Port == 443 ? Host : $"{Host}:{Port}";

            return $"{Host}:{Port}";
        }
    }
    #endregion

    #region 构造
    /// <summary>实例化</summary>
    public UriInfo() { }

    /// <summary>实例化</summary>
    /// <param name="value"></param>
    public UriInfo(String value) => Parse(value);
    #endregion

    #region 方法
    /// <summary>尝试解析Url字符串</summary>
    public static Boolean TryParse(String? value, out UriInfo? uriInfo)
    {
        uriInfo = null;
        if (value.IsNullOrWhiteSpace()) return false;

        uriInfo = new UriInfo();
        return uriInfo.Parse(value);
    }

    /// <summary>解析Url字符串</summary>
    /// <param name="value"></param>
    public Boolean Parse(String value)
    {
        if (value.IsNullOrWhiteSpace()) return false;

        // 先处理头尾,再处理中间的主机和端口
        var p = value.IndexOf("://");
        if (p >= 0)
        {
            Scheme = value[..p];
            p += 3;
        }
        else
            p = 0;

        // 第二步找到/,它左边是主机和端口,右边是路径和查询。如果没有/,则整个字符串都是主机和端口
        var p2 = value.IndexOf('/', p);
        if (p2 >= 0)
        {
            ParseHost(value[p..p2]);
            ParsePath(value, p2);
        }
        else
        {
            p2 = value.IndexOf('?', p);
            if (p2 >= 0)
            {
                ParseHost(value[p..p2]);
                Query = value[p2..];
            }
            else
            {
                Host = value[p..];
            }
        }

        // 如果主要部分都没有,标记为失败
        if (Scheme.IsNullOrEmpty() && Host.IsNullOrEmpty() && AbsolutePath.IsNullOrEmpty())
            return false;

        if (AbsolutePath.IsNullOrEmpty()) AbsolutePath = "/";

        return true;
    }

    private void ParsePath(String value, Int32 p)
    {
        // 第二步找到/,它左边是主机和端口,右边是路径和查询。如果没有/,则整个字符串都是主机和端口
        var p2 = value.IndexOf('?', p);
        if (p2 >= 0)
        {
            AbsolutePath = value[p..p2];
            Query = value[p2..];
        }
        else
        {
            AbsolutePath = value[p..];
        }
    }

    private void ParseHost(String value)
    {
        // 拆分主机和端口,注意IPv6地址
        var p2 = value.LastIndexOf(':');
        if (p2 > 0)
        {
            Host = value[..p2];
            Port = value[(p2 + 1)..].ToInt();
        }
        else if (!value.IsNullOrEmpty())
        {
            Host = value;
        }
    }

    /// <summary>拼接请求参数</summary>
    /// <param name="name"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public UriInfo Append(String name, Object? value)
    {
        var q = Query;
        Query = q.IsNullOrEmpty() ? $"{name}={value}" : $"{q}&{name}={value}";

        return this;
    }

    /// <summary>拼接请求参数(非空)</summary>
    /// <param name="name"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public UriInfo AppendNotEmpty(String name, Object? value)
    {
        if (value == null || value.ToString().IsNullOrEmpty()) return this;

        var q = Query;
        Query = q.IsNullOrEmpty() ? $"{name}={value}" : $"{q}&{name}={value}";

        return this;
    }

    /// <summary>已重载。</summary>
    /// <returns></returns>
    public override String? ToString()
    {
        var authority = Authority;
        if (Scheme.IsNullOrEmpty())
        {
            if (authority.IsNullOrEmpty()) return PathAndQuery;

            return $"{authority}{PathAndQuery}";
        }

        return $"{Scheme}://{authority}{PathAndQuery}";
    }
    #endregion
}