当前位置: 首页 > news >正文

C# 解析 URL URI 中的参数

C# 解析 URL URI 中的参数

文章目录

    • 完整代码
    • 概述
      • 核心功能概述
      • 查询参数解析方法对比
    • 核心方法实现解析
      • 1. 查询参数解析逻辑
      • 2. URL构建逻辑
      • 特殊场景处理说明
      • 测试用例验证
      • 使用注意事项

完整代码

namespace System
{using System.Collections.Generic;using System.Collections.Specialized;using System.Text;/// <summary>/// 函数<see cref="GetQueryDictionary(string, bool)"/> 和 函数<see cref="GetQueryCollection(string, bool)"/><br />/// 支持解析示例:<br />/// <![CDATA[    https://www.google.com/index?page=1&lang=eng    ]]> <br />/// <![CDATA[    https://www.google.com/    ]]> <br />/// <![CDATA[    https://www.google.com/index?    ]]> <br />/// <![CDATA[    https://www.google.com/index?page=title=index=1&lang=&chang&char=?&id=123    ]]> <br />/// </summary>public static class UrlHelper{#region Test 测试示例。/// <summary>/// 使用示例。/// </summary>[System.Diagnostics.Conditional("DEBUG")]public static void Test(){TestGetQueryNormal();TestGetQuerySpecial();TestGetUrlStringNormal();}/// <summary>/// 常见的使用示例。解析URL中的参数。/// </summary>[System.Diagnostics.Conditional("DEBUG")]public static void TestGetQueryNormal(){string urlTest1 = "https://www.google.com/index?page=1&lang=eng";string dictionaryBaseUrlTest1;var dictionaryTest1 = GetQueryDictionary(urlTest1, out dictionaryBaseUrlTest1);System.Diagnostics.Debug.Assert(dictionaryBaseUrlTest1 == "https://www.google.com/index" &&dictionaryTest1["page"] == "1" && dictionaryTest1["lang"] == "eng","GetQueryDictionary Test1");System.Diagnostics.Debug.Assert(GetUrlString(dictionaryBaseUrlTest1, dictionaryTest1) == urlTest1,"GetUrlString by Dictionary Test1");string collectionBaseUrlTest1;var collectionTest1 = GetQueryCollection(urlTest1, out collectionBaseUrlTest1);System.Diagnostics.Debug.Assert(collectionBaseUrlTest1 == "https://www.google.com/index" &&collectionTest1["page"] == "1" && collectionTest1["lang"] == "eng","GetQueryCollection Test1");System.Diagnostics.Debug.Assert(GetUrlString(collectionBaseUrlTest1, collectionTest1) == urlTest1,"GetUrlString by Collection Test1");string urlTest2 = "https://www.google.com/";string dictionaryBaseUrlTest2;GetQueryDictionary(urlTest2, out dictionaryBaseUrlTest2);System.Diagnostics.Debug.Assert(dictionaryBaseUrlTest2 == "https://www.google.com/","GetQueryDictionary Test2");string collectionBaseUrlTest2;GetQueryCollection(urlTest2, out collectionBaseUrlTest2);System.Diagnostics.Debug.Assert(collectionBaseUrlTest2 == "https://www.google.com/","GetQueryCollection Test2");string urlTest3 = "https://www.google.com/index?";string dictionaryBaseUrlTest3;GetQueryDictionary(urlTest3, out dictionaryBaseUrlTest3);System.Diagnostics.Debug.Assert(dictionaryBaseUrlTest3 == "https://www.google.com/index","GetQueryDictionary Test3");string collectionBaseUrlTest3;GetQueryCollection(urlTest3, out collectionBaseUrlTest3);System.Diagnostics.Debug.Assert(collectionBaseUrlTest3 == "https://www.google.com/index","GetQueryCollection Test4");}/// <summary>/// 不常见的使用示例。解析URL中的参数。/// </summary>[System.Diagnostics.Conditional("DEBUG")]public static void TestGetQuerySpecial(){string urlSpecial4 = "https://www.google.com/index?page=title=index=1&lang=&chang&char=?&id=123";string dictionaryBaseUrlSpecial4;var dictionarySpecial4 = GetQueryDictionary(urlSpecial4, out dictionaryBaseUrlSpecial4);System.Diagnostics.Debug.Assert(dictionaryBaseUrlSpecial4 == "https://www.google.com/index" &&dictionarySpecial4["page"] == "title=index=1" && dictionarySpecial4["lang"] == "" &&dictionarySpecial4["chang"] == null && dictionarySpecial4["char"] == "?" && dictionarySpecial4["id"] == "123","GetQueryDictionary UrlSpecial4");string collectionBaseUrlSpecial4;var collectionSpecial4 = GetQueryCollection(urlSpecial4, out collectionBaseUrlSpecial4);System.Diagnostics.Debug.Assert(collectionBaseUrlSpecial4 == "https://www.google.com/index" &&collectionSpecial4["page"] == "title=index=1" && collectionSpecial4["lang"] == "" &&collectionSpecial4["chang"] == null && collectionSpecial4["char"] == "?" && collectionSpecial4["id"] == "123","GetQueryCollection UrlSpecial4");string urlSpecial5 = "https://www.google.com/index?page=1&&lang=eng";string dictionaryBaseUrlSpecial5;var dictionarySpecial5 = GetQueryDictionary(urlSpecial5, out dictionaryBaseUrlSpecial5);System.Diagnostics.Debug.Assert(dictionaryBaseUrlSpecial5 == "https://www.google.com/index" &&dictionarySpecial5["page"] == "1" && dictionarySpecial5["lang"] == "eng","GetQueryDictionary UrlSpecial5");string collectionBaseUrlSpecial5;var collectionSpecial5 = GetQueryCollection(urlSpecial5, out collectionBaseUrlSpecial5);System.Diagnostics.Debug.Assert(collectionBaseUrlSpecial5 == "https://www.google.com/index" &&collectionSpecial5["page"] == "1" && collectionSpecial5["lang"] == "eng","GetQueryCollection UrlSpecial5");}/// <summary>/// 常见的使用示例。拼接URL的<paramref name="baseUrl"/>和URL中的参数。/// </summary>[System.Diagnostics.Conditional("DEBUG")]public static void TestGetUrlStringNormal(){string baseUrl1 = "https://www.google.com/index?page=1&lang=eng";System.Diagnostics.Debug.Assert(GetUrlString(baseUrl1, new Dictionary<string, string>(){["TestKey1"] = "TestValue1",["TestKey2"] = "TestValue2",}) == "https://www.google.com/index?page=1&lang=eng&TestKey1=TestValue1&TestKey2=TestValue2","GetUrlString by Dictionary BaseUrl1");System.Diagnostics.Debug.Assert(GetUrlString(baseUrl1, new NameValueCollection(){["TestKey1"] = "TestValue1",["TestKey2"] = "TestValue2",}) == "https://www.google.com/index?page=1&lang=eng&TestKey1=TestValue1&TestKey2=TestValue2","GetUrlString by Collection BaseUrl1");string baseUrl2 = "https://www.google.com/index";System.Diagnostics.Debug.Assert(GetUrlString(baseUrl2, new Dictionary<string, string>(){["TestKey1"] = "TestValue1",["TestKey2"] = "TestValue2",}) == "https://www.google.com/index?TestKey1=TestValue1&TestKey2=TestValue2","GetUrlString by Dictionary BaseUrl2");System.Diagnostics.Debug.Assert(GetUrlString(baseUrl2, new NameValueCollection(){["TestKey1"] = "TestValue1",["TestKey2"] = "TestValue2",}) == "https://www.google.com/index?TestKey1=TestValue1&TestKey2=TestValue2","GetUrlString by Collection BaseUrl2");}#endregion Test 测试示例。/// <summary>/// 解析URL中的参数。会覆盖重复键的值。<br />/// 注意:在<see cref="Dictionary{TKey, TValue}"/>中,/// 通过<see cref="Dictionary{TKey, TValue}"/>的<![CDATA[    this[TKey key]    ]]>,/// 直接读取不存在的键值对时,会抛出异常。<br />/// </summary>/// <param name="url"></param>/// <param name="ignoreCase"></param>/// <returns></returns>public static Dictionary<string, string> GetQueryDictionary(string url, bool ignoreCase = false){string baseUrl;return GetQueryDictionary(url, out baseUrl, ignoreCase);}/// <summary>/// 解析URL中的参数。会覆盖重复键的值。<br />/// 注意:在<see cref="Dictionary{TKey, TValue}"/>中,/// 通过<see cref="Dictionary{TKey, TValue}"/>的<![CDATA[    this[TKey key]    ]]>,/// 直接读取不存在的键值对时,会抛出异常。<br />/// </summary>/// <param name="url"></param>/// <param name="baseUrl"> URL中符号“?”的前面部分。</param>/// <param name="ignoreCase"></param>/// <returns></returns>public static Dictionary<string, string> GetQueryDictionary(string url, out string baseUrl, bool ignoreCase = false){StringComparer comparer;if (ignoreCase){comparer = StringComparer.InvariantCultureIgnoreCase;}else{comparer = StringComparer.InvariantCulture;}return GetQueryDictionary(url, out baseUrl, comparer);}/// <summary>/// 解析URL中的参数。会覆盖重复键的值。<br />/// 注意:在<see cref="Dictionary{TKey, TValue}"/>中,/// 通过<see cref="Dictionary{TKey, TValue}"/>的<![CDATA[    this[TKey key]    ]]>,/// 直接读取不存在的键值对时,会抛出异常。<br />/// </summary>/// <param name="url"></param>/// <param name="baseUrl"> URL中符号“?”的前面部分。</param>/// <param name="comparer"></param>/// <returns></returns>public static Dictionary<string, string> GetQueryDictionary(string url, out string baseUrl, StringComparer comparer){baseUrl = null;// 第一个“?”符号的下标。// 用于支持,参数中包括“?”符号的URL。int indexQuestionMark = url.IndexOf('?');int countQuery = url.Length - indexQuestionMark - 1;Dictionary<string, string> info = null;// 如果URL中包括有效的参数。if (indexQuestionMark > -1 && countQuery > 0){string queryString = url.Substring(indexQuestionMark + 1, countQuery);// 为空的键值对没有意义,所以,舍弃为空的键值对。string[] keyAndValuePairs = queryString.Split(new char[] { '&' }/*, StringSplitOptions.RemoveEmptyEntries*/);info = new Dictionary<string, string>(keyAndValuePairs.Length + 1, comparer);foreach (var pair in keyAndValuePairs){// 第一个“=”符号的下标。// 用于支持,value中包括“=”符号的参数。int indexEquals = pair.IndexOf('=');// 如果包含“=”符号。if (indexEquals > -1){string key = pair.Substring(0, indexEquals);key = Uri.UnescapeDataString(key);int countValue = pair.Length - indexEquals - 1;// 避免“=”符号后面没有内容时,indexEquals + 1超出数组的有效索引。string value = (countValue == 0) ? string.Empty : pair.Substring(indexEquals + 1, countValue);value = Uri.UnescapeDataString(value);info[key] = value;}// 用关键字保存特殊参数。else{info[pair] = null;}}}info = info ?? new Dictionary<string, string>(1, comparer);if (indexQuestionMark < 0 || indexQuestionMark > url.Length){indexQuestionMark = url.Length;}// URL中符号“?”的前面部分。baseUrl = url.Substring(0, indexQuestionMark);return info;}/// <summary>/// 解析URL中的参数。支持重复键。<br />/// 注意:在<see cref="NameValueCollection"/>中,直接读取不存在的键值对时,返回 null ,不会抛出异常。<br />/// </summary>/// <param name="url"></param>/// <param name="ignoreCase"></param>/// <returns></returns>public static NameValueCollection GetQueryCollection(string url, bool ignoreCase = false){string baseUrl;return GetQueryCollection(url, out baseUrl, ignoreCase);}/// <summary>/// 解析URL中的参数。支持重复键。<br />/// 注意:在<see cref="NameValueCollection"/>中,直接读取不存在的键值对时,返回 null ,不会抛出异常。<br />/// </summary>/// <param name="url"></param>/// <param name="baseUrl"> URL中符号“?”的前面部分。</param>/// <param name="ignoreCase"></param>/// <returns></returns>public static NameValueCollection GetQueryCollection(string url, out string baseUrl, bool ignoreCase = false){StringComparer comparer;if (ignoreCase){comparer = StringComparer.InvariantCultureIgnoreCase;}else{comparer = StringComparer.InvariantCulture;}return GetQueryCollection(url, out baseUrl, comparer);}/// <summary>/// 解析URL中的参数。支持重复键。<br />/// 注意:在<see cref="NameValueCollection"/>中,直接读取不存在的键值对时,返回 null ,不会抛出异常。<br />/// </summary>/// <param name="url"></param>/// <param name="baseUrl"> URL中符号“?”的前面部分。</param>/// <param name="comparer"></param>/// <returns></returns>public static NameValueCollection GetQueryCollection(string url, out string baseUrl, StringComparer comparer){// 第一个“?”符号的下标。// 用于支持,参数中包括“?”符号的URL。int indexQuestionMark = url.IndexOf('?');int countQuery = url.Length - indexQuestionMark - 1;NameValueCollection info = null;// 如果URL中包括有效的参数。if (indexQuestionMark > -1 && countQuery > 0){string queryString = url.Substring(indexQuestionMark + 1, countQuery);// 为空的键值对没有意义,所以,舍弃为空的键值对。string[] keyAndValuePairs = queryString.Split(new char[] { '&' }/*, StringSplitOptions.RemoveEmptyEntries*/);info = new NameValueCollection(keyAndValuePairs.Length + 1, comparer);foreach (var pair in keyAndValuePairs){// 第一个“=”符号的下标。// 用于支持,value中包括“=”符号的参数。int indexEquals = pair.IndexOf('=');// 如果包含“=”符号。if (indexEquals > -1){string key = pair.Substring(0, indexEquals);key = Uri.UnescapeDataString(key);int countValue = pair.Length - indexEquals - 1;// 避免“=”符号后面没有内容时,indexEquals + 1超出数组的有效索引。string value = (countValue == 0) ? string.Empty : pair.Substring(indexEquals + 1, countValue);value = Uri.UnescapeDataString(value);info.Add(key, value);}// 用关键字保存特殊参数。else{info.Add(pair, null);}}}info = info ?? new NameValueCollection(1, comparer);if (indexQuestionMark < 0 || indexQuestionMark > url.Length){indexQuestionMark = url.Length;}// URL中符号“?”的前面部分。baseUrl = url.Substring(0, indexQuestionMark);return info;}/// <summary>/// 拼接URL的<paramref name="baseUrl"/>(可以是URL中符号“?”的前面部分,也可以是已经包含参数的URL)和URL中的参数。/// </summary>/// <param name="baseUrl"></param>/// <param name="parameters"></param>/// <returns></returns>public static string GetUrlString(string baseUrl, ICollection<KeyValuePair<string, string>> parameters){if (parameters != null && parameters.Count > 0){var indexEndSplit = baseUrl.LastIndexOf('/');if (indexEndSplit < 0){indexEndSplit = 0;}StringBuilder builder = new StringBuilder(baseUrl.Length + 128 + 1);var indexQuestionMark = baseUrl.IndexOf('?', indexEndSplit);if (indexQuestionMark < 0){builder.Append(baseUrl);bool hasQueryItem = false;bool addQuestionMark = true;GetUrlQueryStringCore(parameters, builder, hasQueryItem, addQuestionMark);}else{builder.Append(baseUrl);bool hasQueryItem = indexQuestionMark < baseUrl.Length - 1;bool addQuestionMark = false;GetUrlQueryStringCore(parameters, builder, hasQueryItem, addQuestionMark);}return builder.ToString();}return baseUrl;}/// <summary>/// 拼接URL的<paramref name="baseUrl"/>(可以是URL中符号“?”的前面部分,也可以是已经包含参数的URL)和URL中的参数。/// </summary>/// <param name="baseUrl"></param>/// <param name="parameters"></param>/// <returns></returns>public static string GetUrlString(string baseUrl, NameValueCollection parameters){if (parameters != null && parameters.Count > 0){var indexEndSplit = baseUrl.LastIndexOf('/');if (indexEndSplit < 0){indexEndSplit = 0;}StringBuilder builder = new StringBuilder(baseUrl.Length + 128 + 1);var indexQuestionMark = baseUrl.IndexOf('?', indexEndSplit);if (indexQuestionMark < 0){builder.Append(baseUrl);bool hasQueryItem = false;bool addQuestionMark = true;GetUrlQueryStringCore(parameters, builder, hasQueryItem, addQuestionMark);}else{builder.Append(baseUrl);bool hasQueryItem = indexQuestionMark < baseUrl.Length - 1;bool addQuestionMark = false;GetUrlQueryStringCore(parameters, builder, hasQueryItem, addQuestionMark);}return builder.ToString();}return baseUrl;}public static string GetUrlQueryString(ICollection<KeyValuePair<string, string>> parameters, bool addQuestionMark){StringBuilder builder = new StringBuilder(128);bool hasQueryItem = false;GetUrlQueryStringCore(parameters, builder, hasQueryItem, addQuestionMark);return builder.ToString();}public static string GetUrlQueryString(NameValueCollection parameters, bool addQuestionMark){StringBuilder builder = new StringBuilder(128);bool hasQueryItem = false;GetUrlQueryStringCore(parameters, builder, hasQueryItem, addQuestionMark);return builder.ToString();}private static void GetUrlQueryStringCore(ICollection<KeyValuePair<string, string>> parameters, StringBuilder builder, bool hasQueryItem, bool addQuestionMark){foreach (KeyValuePair<string, string> item in parameters){GetUrlQueryStringForAddParameterBefore(builder, ref hasQueryItem, addQuestionMark);builder.Append(Uri.EscapeDataString(item.Key));if (item.Value != null){builder.Append("=");builder.Append(Uri.EscapeDataString(item.Value));}}}private static void GetUrlQueryStringCore(NameValueCollection parameters, StringBuilder builder, bool hasQueryItem, bool addQuestionMark){foreach (object itemKeyObj in parameters.Keys){string itemKey = itemKeyObj?.ToString();if (itemKey != null){var itemValues = parameters.GetValues(itemKey);if (itemValues == null){GetUrlQueryStringForAddParameterBefore(builder, ref hasQueryItem, addQuestionMark);builder.Append(Uri.EscapeDataString(itemKey));}else{foreach (var itemValue in itemValues){GetUrlQueryStringForAddParameterBefore(builder, ref hasQueryItem, addQuestionMark);builder.Append(Uri.EscapeDataString(itemKey));if (itemValue != null){builder.Append("=");builder.Append(Uri.EscapeDataString(itemValue));}}}}}}private static void GetUrlQueryStringForAddParameterBefore(StringBuilder builder, ref bool hasQueryItem, bool addQuestionMark){if (hasQueryItem){builder.Append("&");}else{hasQueryItem = true;if (addQuestionMark){builder.Append('?');}}}}
}

概述

核心功能概述

  1. 查询参数解析

    • GetQueryDictionary():将URL查询参数解析为字典(键值唯一,后值覆盖前值)
    • GetQueryCollection():将URL查询参数解析为集合(支持多值键)
  2. URL构建

    • GetUrlString():将基础URL与参数集合/字典拼接成完整URL
  3. 特殊支持

    • 处理含特殊字符的键值(如?=
    • 支持无值参数(如&key
    • 处理参数中出现的等号和问号

查询参数解析方法对比

特性GetQueryDictionaryGetQueryCollection
返回值类型Dictionary<string, string>NameValueCollection
键唯一性✔️(后值覆盖前值)✖️(支持多值键)
读取不存在键抛出异常返回null
参数格式键=值(值中可含=)键=值(值中可含=)
无值参数处理键→null键→null

核心方法实现解析

1. 查询参数解析逻辑

// 解析字典示例
int indexQuestionMark = url.IndexOf('?');
if (indexQuestionMark > -1)
{string queryString = url.Substring(indexQuestionMark + 1);string[] pairs = queryString.Split('&');foreach (var pair in pairs){int indexEquals = pair.IndexOf('=');if (indexEquals > -1){string key = Uri.UnescapeDataString(pair.Substring(0, indexEquals));string value = Uri.UnescapeDataString(pair.Substring(indexEquals + 1));dict[key] = value; // 字典直接赋值(覆盖)}else{dict[pair] = null; // 无值参数处理}}
}
baseUrl = url.Substring(0, indexQuestionMark);

2. URL构建逻辑

// URL拼接核心逻辑
private static void GetUrlQueryStringCore(NameValueCollection parameters, StringBuilder builder)
{foreach (string key in parameters.Keys){string[] values = parameters.GetValues(key);foreach (string value in values){// 添加分隔符(?或&)if (builder.Length > baseUrlLength) builder.Append('&');else if (needQuestionMark) builder.Append('?');builder.Append(Uri.EscapeDataString(key));if (value != null){builder.Append('=');builder.Append(Uri.EscapeDataString(value));}}}
}

特殊场景处理说明

  1. 值中含等号
    使用首次出现的=分割键值:

    // 示例参数:title=index=1
    // 解析结果:key="title", value="index=1"
    int indexEquals = pair.IndexOf('=');
    
  2. 无值参数
    被解析为key → null

    // 示例URL:https://site.com?key1&key2=val
    // 解析结果:key1=null, key2="val"
    
  3. 问号处理
    仅识别第一个?作为查询起始:

    // 示例:https://site.com?param=?
    // 正确解析:param="?"
    int indexQuestionMark = url.IndexOf('?');
    

测试用例验证

测试场景:
1. 标准URL解析✓ https://site.com?k1=v1&k2=v2 → k1="v1", k2="v2"2. 特殊字符处理✓ https://site.com?key=? → key="?"✓ https://site.com?k=1=2=3 → k="1=2=3"3. 边界情况✓ https://site.com? (空参数) → 空集合✓ https://site.com (无参数) → 空集合4. URL重建验证✓ 解析后重建URL应与原始URL一致

使用注意事项

  1. 字典读取风险

    // 安全读取方式
    if (dict.TryGetValue("key", out var value)) { ... }
    
  2. 编码规范

    • 使用Uri.EscapeDataString处理特殊字符
    • 使用Uri.UnescapeDataString反向解码

相关文章:

  • 大模型量化与剪枝
  • 【0.0 漫画C语言计算机基础 - 从二进制开始认识计算机】
  • Cursor链接远程服务器实现项目部署
  • 【python】bash: !‘: event not found
  • ABC410 : F - Balanced Rectangles
  • nginx 配置返回 文件大小
  • 2025年渗透测试面试题总结-浙江东岸检测[实习]安全工程师(题目+回答)
  • 【华为开发者学堂】HarmonyOS
  • 16.vue.js watch()和watchEffect()的对比?(追踪依赖)(3)
  • Python 训练营打卡 Day 45-Tensorboard
  • 20250614让NanoPi NEO core开发板在Ubuntu core16.04系统下使用耳机播音测试
  • conda虚拟环境管理
  • Qt事件处理
  • SpringBoot 自动化部署实战:从环境搭建到 CI/CD 全流程
  • Prompt工程在企业场景的实战应用:用Grok 3 API优化客服系统的3个技巧
  • Android Activity全面解析:从创建到生命周期的完整指南
  • cesium入门
  • event.target 详解:理解事件目标对象
  • 有关Spring事务的传播机制
  • Java 单例模式实现方式
  • 做医院网站/百家港 seo服务
  • 平台推广员是干嘛的/账号seo是什么
  • 网站建设销售兼职合同/随州今日头条新闻
  • 模板网站做外贸可以吗/成都网站快速排名软件
  • 网站做app的软件/上海百度seo网站优化
  • 加强网站及微信平台建设/银川网站seo