自制一个可编辑QueryString的类URLModifier
有些情况下,需要 新增/删除/替换 url中的部分Querystring中的参数,而.net自带的Uri类只能解析,不能编辑,,并且如果是Relative类型的链接,转成Uri类型之后,很多参数又不能很好的读取,因此,,,自己动手,丰衣足食,,大家不用复制文内的代码,,,文章最后,给出完整的类的代码链接,,有需要的,直接复制就行了
使用的场景如:
当前request的url= https://www.xxx.com/doc/base/infrastructure.html?categoryid=5&order=2&state=2u#topoop
在前端razor输出的时候,需要保留order和state的情况下.修改categoryid值:
@{
var requestUrlModifier = ViewContext.HttpContext.Request.GetDisplayUrl().ToUrlModifier();
}
@foreach (var category in Model.Categories)
{
<a href="@requestUrlModifier.Clone().ReplaceQuery("categoryID", category.CategoryID.ToStringEx()).ToString()"></a>
}
首先明确这个工具类的几个作用
1,可 新增/删除/替换 链接中的QueryString 中的各个keyvalue
2.可 替换/设置/保留 锚点anchor
3.对原有的Host/端口/协议/路径 保留原有值
4.最后可输出编辑后的结果
5.对不太标准的Url或部分有问题的QueryString支持
测试主要以下两种Url:
1.https://www.layui.com/doc/base/infrastructure.html?=&o==ppp&&pppp=iii&o1=ooo&test2=&u#topoop
这个链接QueryString中,除了正常的KeyValue对之外,,还有出现:
1) =& 开头的无效字符
2) o==ppp 中间包含两个==号的错误连续
3) &&pppp=iii 出现 && 两个连续字符
4) test2=& 这样的只有key,没有value
5) &u 这样的只有key,连等号都没有的
6) #topoop 链接后的锚点
2.https://www.layui.com/doc/base/infrastructure.html?=&o==ppp&&pppp=iii&o1=ooo&test2=&
1) 链接最后以 & 结尾
3. /doc/base/infrastructure.html?=&o==ppp&&pppp=iii&o1=ooo&test2=&u#topoop
1) 链接以 / 开头的相对链接,不包含域名信息和端口等信息
如果还有其他情况,可以留言,增加测试用例
由于该类在实际使用过程中,可能会频繁被调用,因此,需要尽量少的消耗资源,尽量少的变量定义
处理的流程主要分以下几个步骤:
1.从后往前,计算出是否包含锚点,并计算锚点所在的index1
2.从前往后,计算出?号所在index2
3.截取出从0到index2的所有字符
4.从index2到锚点index1之间的字符串就是QueryString了
5.循环判断QueryString的起始和结束index,为了去掉前后的无效字符
6.循环获取=和&号的位置,切分字符串
到此,完成对Url字符串的解析,,这里为什么不用正则表达式呢,,,因为第一慢,第二闲,,没事自己写,省事省空间
接下来就上代码了,,每个处理步骤,请看代码的注释文字
1.在构造函数中,对传入的Url进行解析操作
private string _anchor = string.Empty;
private string _hostAndPath =string.Empty;
private List<(string key, string value)> _queryKeys = new List<(string key, string value)>(); public UrlModifier(string url) //直接传入string,不用uri,省的相对路径下,Uri类在读取Query属性的之后报错
{
if (string.IsNullOrWhiteSpace(url))
{
return;
} var endIndex = url.Length - ; //从后往前扫描锚点#
for (int i = url.Length-; i >= ; i--)
{
if (url[i]=='#')
{
_anchor = url.Substring(i+);
endIndex = i - ;
break;
} //防止出现无#字符的情况
if (url[i] == '=' || url[i] == '&' || url[i] == ' ' || url[i] == '?' || url[i]=='/')
{
endIndex = url.Length-;
break;
}
} //截取域名段,包含协议,端口号等
var hostEndIndex = endIndex;
for (int i = ; i < endIndex; i++)
{
if (url[i]=='?') //查找?号所在index
{
hostEndIndex = i - ;
}
} if (hostEndIndex>0) //如果是绝对路径的,获取Host和Path的字符串
{
if(url[hostEndIndex]=='?')
{
_hostAndPath=url.Substring(0,hostEndIndex);
}
else
{
_hostAndPath=url.Substring(0,hostEndIndex+1);
}
hostEndIndex++;
} if (hostEndIndex > && hostEndIndex < endIndex)
{
//排除掉使用=号或者&或者空格结尾的字符,减少后续判断的麻烦,计算出实际的结束index
for (int i = endIndex; i >= hostEndIndex; i--)
{
var c = url[i];
if (c != '=' && c != '&' && c != ' ')
{
endIndex = i;
break;
}
} var keyword = "";
var value = "";
var startIndex = ;
char lastKeyword ='\0' ; for (int i = hostEndIndex; i < endIndex; i++)
{
var c = url[i];
//排除掉使用 = 号或者 & 或者 ? 或者空格开头的字符,减少后续判断的麻烦,计算出实际起始index
if (c != '=' && c != '&' && c != ' ' && c!='?')
{
startIndex = i;
break;
}
} if (startIndex>=endIndex) //如果没字符了,整个都是特殊字符,则直接返回
{
return;
}
//接下来就是解析QueryString的过程了,比较繁琐,具体就是查找=和&符号,并截取中间的字符串作为key和value
for (int i = startIndex; i <= endIndex; i++)
{
var c = url[i]; if (c == '=')
{
if (lastKeyword=='=') //处理 ?s==0
{
startIndex=i+;
continue;
} keyword = url.Substring(startIndex, i - startIndex);
lastKeyword = c;
startIndex = i+;
//startIndex++;
} if (c == '&')
{
if (url[i-] == '&') //处理 ?s=0&& 的情况
{
startIndex = i + ;
continue;
} if (lastKeyword=='=' || lastKeyword=='\0') // 处理 ?ss=0 的情况
{
value = url.Substring(startIndex , i-startIndex);
}
// 处理 ?ddd& 或者 ?p=0&ddd 这种情况,切分出来的,算key
else if (lastKeyword=='&' || lastKeyword =='\0')
{
keyword = url.Substring(startIndex, i - startIndex);
value = string.Empty;
} lastKeyword = '\0';
startIndex = i + ; if (!string.IsNullOrEmpty(keyword))
{
AddQuery(keyword, value); //添加入列表
} }
} //如果还有剩余的字符,则处理完剩下的字符串
if (startIndex <= endIndex)
{
if (lastKeyword=='=' && !string.IsNullOrEmpty(keyword)) //处理 ?d=value 的情况
{
value = url.Substring(startIndex,endIndex - startIndex);
}
else if ((lastKeyword == '=' && string.IsNullOrEmpty(keyword)) ||
lastKeyword == '&' ||
lastKeyword == '\0')
{
keyword = url.Substring(startIndex, endIndex+ - startIndex);
value = string.Empty;
} AddQuery(keyword, value);
}
} }
2.增加几个处理函数
/// <summary>
/// 添加一对参数值
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public UrlModifier AddQuery(string key, string value)
{
//如果存在相同的Key的,则直接附加到原有值的后面
var index = _queryKeys.IndexOf(x => x.key.CompareTo(key, true)); if (index>)
{
var orgValue = _queryKeys[index].value;
_queryKeys[index] = (key, $"{orgValue},{value}");
}
else
{
_queryKeys.Add((key, value));
} return this;
} /// <summary>
/// 添加一个keyvalue
/// </summary>
/// <param name="pair"></param>
/// <returns></returns>
public UrlModifier AddQuery((string key, string value) pair)
{
return AddQuery(pair.key, pair.value);
} /// <summary>
/// 删除指定key的项目
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public UrlModifier RemoveQuery(string key)
{
_queryKeys.Remove(x => x.key == key); return this;
} /// <summary>
/// 替换指定key的数据
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public UrlModifier ReplaceQuery(string key, string value)
{
var index = _queryKeys.IndexOf(x => x.key == key); if (index < )
{
_queryKeys.Add((key, value));
}
else
{
_queryKeys[index] = (key, value);
} return this;
} /// <summary>
/// 设置锚点值
/// </summary>
/// <param name="anchor"></param>
/// <returns></returns>
public UrlModifier SetAnchor(string anchor)
{
_anchor = anchor;
return this;
}
3.重载ToString函数,用于拼接并输出操作后的结果
public override string ToString()
{
var sb=new StringBuilder(); if (!string.IsNullOrEmpty(_hostAndPath))
{
sb.Append(_hostAndPath);
} if (_queryKeys!=null && _queryKeys.Count>)
{
sb.Append('?'); foreach (var item in _queryKeys)
{
sb.Append(item.key); if (!string.IsNullOrEmpty(item.value))
{
sb.Append('=');
sb.Append(item.value); }
sb.Append('&');
} if (sb[sb.Length-]=='&')
{
sb.Remove(sb.Length - , );
} } if (!string.IsNullOrEmpty(_anchor))
{
sb.Append('#');
sb.Append(_anchor);
} return sb.ToString();
}
4.当然,为了能节省解析的次数,毕竟如果是循环内修改参数的,那么基本上基础的url是相同的,因此,我们可以实现一个Clone,让解析的结果可以作为模板复用
public UrlModifier Clone()
{
var item=new UrlModifier("") ; item._queryKeys.AddRange(this._queryKeys);
item._hostAndPath = this._hostAndPath;
item._anchor = this._anchor; return item;
}
5.为了方便开发,其实还可以再增加一些扩展函数,比如直接从Url的string转为URLModifier,或者增加不同的隐式转换的函数,具体可参考文章最后的源码文件
6.调用测试:
public void TestUrlModifier()
{
var url = "https://www.xxx.com/doc/base/infrastructure.html";
var newUrl = url.ToUrlModifier().SetAnchor("topoop").RemoveQuery("keyword").ReplaceQuery("pppp", "iii")
.AddQuery("o1", "ooo");
var newUrlStr = newUrl.ToString(); var test5 = "https://www.xxx.com/doc/base/infrastructure.html?=&o==ppp&&pppp=iii&o1=ooo&test2=&u#topoop"; //出现连续两个=号及连续的&号
var newUrl5 = test5.ToUrlModifier();
var newStr5 = newUrl5.ToString(); var test6 = "/doc/base/infrastructure.html?=&o==ppp&&pppp=iii&o1=ooo&test2=&u#topoop"; //出现连续两个=号及连续的&号,并且是相对路径
var newUrl6 = test6.ToUrlModifier();
var newStr6 = newUrl6.ToString();
}
输出的结果为:
newUrlStr: https://www.xxx.com/doc/base/infrastructure.html?pppp=iii&o1=ooo#topoop
newStr5: https://www.xxx.com/doc/base/infrastructure.html?o=ppp&pppp=iii&o1=ooo&test2&u#topoop //排除掉错误的格式,并格式化输出
newStr6: /doc/base/infrastructure.html?o=ppp&pppp=iii&o1=ooo&test2&u#topoop //输出原始的相对路径 并排除错误格式
注意:该类并不会对QueryString中的Key或者Value进行编码,因此,有需要编码的,可增加一个AddQuery的重载,进行处理
完整的类的代码在:https://github.com/kugarliyifan/Kugar.Core/blob/master/Kugar.Core.NetCore/Network/UrlModifier.cs 稍作修改就可以单独使用
AddQuery函数中使用到了一个IndexOf的扩展方法,在:https://github.com/kugarliyifan/Kugar.Core/blob/master/Kugar.Core/ExtMethod/ListExtMethod.cs#L1193
自制一个可编辑QueryString的类URLModifier的更多相关文章
- 3.实现一个名为Person的类和它的子类Employee,Employee有两个子类Faculty 和Staff。
23.实现一个名为Person的类和它的子类Employee,Employee有两个子类Faculty 和Staff. 具体要求如下: (1)Person类中的属性有:姓名name(String类型) ...
- classmethod一个用处是创建可选类构造器
Definition and Introduction通常来说, descriptor 是一种绑定着特殊行为属性的对象, 在访问它时行为被descriptor协议定义的方法所重载.这些方法是__get ...
- 一天一个Java基础——对象和类
1.在Java中你所做的全部工作就是定义类,产生那些类的对象,以及发送消息给这些对象 2.可以在类中设置两种类型的元素:字段(也被称作数据成员)和方法(也被称作成员函数) 3.字段可以是任何类型的对象 ...
- 一个漂亮的php验证码类
一个漂亮的php验证码类(分享) 作者: 字体:[增加 减小] 类型:转载 下面小编就为大家分享一个漂亮的php验证码类.需要的朋友可以过来参考下 直接上代码: 复制代码 代码如下: //验证 ...
- 设计一个 Java 程序,自定义异常类,从命令行(键盘)输入一个字符串,如果该字符串值为“XYZ”。。。
设计一个 Java 程序,自定义异常类,从命令行(键盘)输入一个字符串,如果该字符串值为“XYZ”,则抛出一个异常信息“This is a XYZ”,如果从命令行输入 ABC,则没有抛出异常.(只有 ...
- CSS一个元素同时使用多个类选择器(class selector)
CSS类选择器参考手册 一个元素同时使用多个类选择器 CSS中类选择器用点号表示.实际项目中一个div元素为了能被多个样式表匹配到(样式复用),通常div的class中由好几段组成,如<div ...
- 定义一个复数(z=x+iy)类Complex,包含: 两个属性:实部x和虚部y 默认构造函数 Complex(),设置x=0,y=0 构造函数:Complex(int i,int j) 显示复数的方法:showComp()将其显示为如: 5+8i或5-8i 的形式。 求两个复数的和的方法:(参数是两个复数类对象,返回值是复数类对象)public Complex addComp(Compl
因标题框有限,题目未显示完整,以下再放一份: 定义一个复数(z=x+iy)类Complex,包含: 两个属性:实部x和虚部y 默认构造函数 Complex(),设置x=0,y=0 构造函数:Compl ...
- Eclipse里选中一个变量后,这个类里的该变量不变色了?
Eclipse里选一个变量后,这个类里的该变量不变色了. 1.使用“Alt+Shift+O”对该提示功能的开/关切换 2.可以在以下设置选中后的文本提示颜色 window--> Prefere ...
- String、StringBuffer和StringBuilder,定义一个自己的StringBuilder的类
String Java中的字符串值属于String类,虽然有其它方法表示字符串(如字符数组),但Java一般使用String类作为字符串的标准格式,Java编译器把字符串值作为String对象; St ...
随机推荐
- rpm包管理工具
介绍: RPM [1] 是Red-Hat Package Manager(RPM软件包管理器)的缩写,这一文件格式名称虽然打上了RedHat的标志,但是其原始设计理念是开放式的,现在包括OpenLi ...
- Tidb go mac 上开发环境搭建
1.安装golang 运行环境 2.安装lite ide 工具 3.安装dep 包管理工具 4.安装delve debuger 调试工具 我用的是mac hight sierra 10.13 版, 会 ...
- reactor-core
<dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core&l ...
- 吴裕雄--天生自然 pythonTensorFlow自然语言处理:Attention模型--训练
import tensorflow as tf # 1.参数设置. # 假设输入数据已经转换成了单词编号的格式. SRC_TRAIN_DATA = "F:\\TensorFlowGoogle ...
- Snapchat欲联手亚马逊推扫一扫功能,社交应用营收来源将有大变化?
当下的社交应用,已经不能完全仅用"社交"的标签进行定义.因为目前的社交应用不仅承载着大众的喜怒哀乐和沟通指责,更在逐渐打造起一个连接多方的生态系统.甚至只从自身的营收.利润出发,社 ...
- 吴裕雄--天生自然C语言开发:错误处理
#include <stdio.h> #include <errno.h> #include <string.h> extern int errno ; int m ...
- git的命令操作指南
Git图形化界面我用的还可以,但是命令就不太会了,索性和大家一起学习下Git命令的用法...一般来说,日常使用只要记住下图6个命令,就可以了.但是熟练使用,恐怕要记住60-100个命令. fetch ...
- python后端向前台返回字节流文件
python后端向前台返回字节流文件,浏览器访问地址自动下载文件: from django.http.response import StreamingHttpResponse # 要下载的文件路径 ...
- android高仿抖音、点餐界面、天气项目、自定义view指示、爬取美女图片等源码
Android精选源码 一个爬取美女图片的app Android高仿抖音 android一个可以上拉下滑的Ui效果 android用shape方式实现样式源码 一款Android上的新浪微博第三方轻量 ...
- git 第一次上传本地代码到远程仓库,解决 ! [rejected] master -> master (non-fast-forward)错误
使用git想GitHub远程仓库上传代码的基本步骤一般是 初始化为git仓库 git init 添加所有要提交的文件 git add . 本次提交说明 git commit -m '提交说明' 关联G ...