ASP.NET Core – 操作 Uri 和 Query
前言
以前就有写过了 Asp.net core 学习笔记 (操作 URL 和 Query), 但很乱, 这篇作为整理.
Uri 介绍
结构:
[Scheme]://[Host]:[Port][/Path][?Query][#Fragment]
对应:
[https:]//[www.stooges.com.my]:[443][/blog/air%20service][?title=my%20title][#my-id]
path 和 query 都需要 encode.
%20 就是空格 (space) 的 encode.
ASP.NET Core 有 2 个方法用来 encode 它们
var path = new PathString("/first name?name=derrick yam"); // must start with /
var encodedPath = path.ToString(); // /first%20name%3Fname=derrick%20yam
var queryString = $"?name={UrlEncoder.Default.Encode("derrick yam")}"; // ?name=derrick%20yam
类似 JavaScript 的 encodeURI 和 encodeURIComponent
Read URI (from request)
在 读写 Request / Response 有提到过如何获取 request URI. 这里补充一些细节.
1. Fragment 是无法从 request URI 获取的. 因为那个是 for client side 的.
2. HostString 对象
HostString hostString = HttpContext.Request.Host;
string host = HttpContext.Request.Host.Host; // www.stooges.com.my
int? port = HttpContext.Request.Host.Port; // 44300
string hostAndPort = HttpContext.Request.Host.ToString(); // www.stooges.com.my:44300
3. PathString 对象
PathString pathString = HttpContext.Request.Path;
string? path = pathString.Value; // "/contact me/dada"
string path1 = pathString.ToString(); // "/contact%20me/dada"
注意: 1 个有 decode 1 个没有 decode
4. QueryString 对象
和 PathString 差不多接口
QueryString queryString = HttpContext.Request.QueryString;
string query1 = queryString.ToString(); // "?page=%201&value=xx"
string? query2 = queryString.Value; // "?page=%201&value=xx"
注意: 2 个都是没有 decode 的哦,
5. Query 对象
QueryString 没有太多的作用, 真的想方便的话用 Query
if (HttpContext.Request.Query.TryGetValue("page", out StringValues values))
{
// ?page=1&page=%202
string value1 = values.ElementAt(0); // "1"
string value2 = values.ElementAt(1); // " 2"
var values3 = values.ToString(); // "1, 2"
}
注意: 它是 decode 好的哦. 当有重复 key 的时候, value 会放到 values 里面.
也可以遍历它.
IEnumerable<KeyValuePair<string, StringValues>> keyValuePairs = HttpContext.Request.Query;
foreach (var kv in keyValuePairs)
{
string key = kv.Key;
var value = kv.Value.ToString(); // "1, 2"
}
注意: key 是不会重复的, 因为已经合并到 values 里的.
5.1 Query shortcut
也可以直接用 ["key"] 的方式拿来 compare 这样会比较方便
var value1 = Request.Query["value"] == StringValues.Empty; // 当没有 key 的时候
var value2 = Request.Query["value"] == ""; // value= 当有 key 但没有值的时候
var value3 = Request.Query["value"] == "a"; // value=a 当有 key 有值的时候
var value4 = Request.Query["value"] == "a,b"; // value=a&value=b 当有 2 个 key 的时候 或者 value=a%2Cb 当有 1 个 key
6. GetDisplayUrl
它是 extension Microsoft.AspNetCore.Http.Extensions, 获取当前完整路径.
注意:
它不包含 #Fragment, Path 会 decode, Query 不会 decode. 和上面的逻辑是一致的.
如果想获取没有 decode 的 URL, 可以调用 GetEncodedUrl()
Read URI (from string)
var uri = new Uri("https://www.stooges.com.my/about%20us/detail?page=1#my-id");
string scheme = uri.Scheme; // https
string host = uri.Host; // www.stooges.com.my
int port = uri.Port; // 443
string path = uri.LocalPath; // /about us/detail (decoded)
string absolutePath = uri.AbsolutePath; // "/about%20us/detail"
string query = uri.Query; // ?page=1
string fragment = uri.Fragment; // #my-id
string pathAndQuery = uri.PathAndQuery; // "/about%20us/detail?page=1"
几个点注意:
1. Frament 可以拿到
2. Path, Query, Fragment 分别 starts with "/", "?", "#"
3. Query 是没有 decode 的
4. LocalPath 有 decode, AbsolutePath 没有 decode, PathAndQuery 没有 decode
5. 除了 port 全部返回都是 string 而不是 HostString, PathString, QueryString 这类的.
比起 read URI from request, new Uri 简单多了. 它的 Query 最好交给 QueryHelper 处理哦.
另外, new Uri 默认必须是绝对路径
var uri = new Uri("/test"); // 报错
如果想放相对路径要特别声明 UriKind.Relative
var uri = new Uri("/test", UriKind.Relative);
var uri = new Uri("test", UriKind.Relative);
注: relative URI 不支持获取 query 哦.uri.Query 会直接报错.
C# 没有很好的方法可以从 relative URI 拿到 query string. 参考: Stack Overflow – How to get the parameter from a relative URL string in C#?
QueryHelpers
Request.Query 很方便, 但是 Uri 没有这些. 所以需要 QueryHelpers 帮忙
var uri = new Uri("https://www.stooges.com.my?page=1&page=%202");
string queryString = uri.Query; // ?page=1&page=%202
Dictionary<string, StringValues> query = QueryHelpers.ParseQuery(queryString);
var value1 = query["page"].ElementAt(0); // "1"
var value2 = query["page"].ElementAt(1); // " 2" decoded
var value3 = query["page"].ToString(); // "1, 2"
foreach (KeyValuePair<string, StringValues> kv in query)
{
string key = kv.Key; // page
StringValues values = kv.Value;
}
用起来很简单, 把 Uri.Query 传给 QueryHelpers 就可以了, 基本上就有了 Request.Query 的所有能力了.
返回字典, 字典继承了 IEnumerable<KeyValuePair<string, StringValues>> 所以也可以遍历拿到 KeyValuePair.
注: ParseQuery 的参数一定要是 query string, 可以 starts with ? 或者直接 key=value, 但不可以 starts with path 哦, 比如 "/login?name=derrick" 这样是 parse 失败的.
QueryBuilder
介绍完 read, 现在介绍 build. 先从简单的 build query 开始.
var queryBuilder = new QueryBuilder();
queryBuilder.Add("page", "1");
queryBuilder.Add("page", " 2");
queryBuilder.Add("value", "abc"); QueryString queryString = queryBuilder.ToQueryString(); var queryString1 = queryString.Value; // "?page=1&page=%202&value=abc"
var queryString2 = queryString.ToString(); // "?page=1&page=%202&value=abc"
添加, 最后 ToQueryString 就可以了,
1. value 不需要自己 encode
2. 不管是 queryString.Value 还是 .ToString() 都是 encoded 的 (QueryString 都是 encoded 的上面介绍 read 的时候也有提到)
QueryBuilder 初始化还支持 KeyValuePairs 哦
var keyValuePairs = new Dictionary<string, StringValues>
{
["page"] = new StringValues(new[] { "1", " 2" }),
["value"] = "abc"
};
var queryBuilder = new QueryBuilder(keyValuePairs);
这样和上面的例子是等价的效果.
注意: QueryBuilder 只能 add, 不能 set 也不能 remove.
QueryHelpers + QueryBuilder
把 read 和 write 加起来就变成了 modify.
var queryParams = QueryHelpers.ParseQuery("?key1=value1&key2=value2");
queryParams["key1"] = "value11"; // edit value or add key value
queryParams.Add("key3", "key3"); // add key
// query.Add("key2", "key22"); // Error: can't add when key existed
queryParams["key2"] = new StringValues(queryParams["key2"].Append("value22").ToArray()); // add more value to existing key
queryParams.Remove("key2"); // all values under this key will be removed
var queryBuilder = new QueryBuilder(queryParams);
queryBuilder.Add("salary", "500"); // add extra key
queryBuilder.Add("salary", "600"); // add more value to existing key (it will not get error like above)
var newQueryString = queryBuilder.ToQueryString().Value; // ?key1=value11&key3=key3&salary=500&salary=600
1. queryParams.Add 会报错, 当 key 已存在
2. queryBuilder.Add 不会报错, 当 key 已存在
它没有批量 remove multiple key 的方法, 必须先 filter 出来然后一个一个 remove
var keys = queryParams.Keys.Where(k => k != "page").ToList();
keys.ForEach(key => queryParams.Remove(key));
UriBuilder
var uriBuilder = new UriBuilder();
uriBuilder.Scheme = "https";
uriBuilder.Host = "www.stooges.com.my";
uriBuilder.Port = 443; // 通常是不需要放的, https 自动是 443, http 自动是 80
uriBuilder.Path = "/blog/air service"; // better starts with /, make it consistency
uriBuilder.Query = "?page=1"; // better starts with ?, make it consistency
uriBuilder.Fragment = "#my-id"; // better starts with #, make it consistency
var url = uriBuilder.ToString(); // "https://www.stooges.com.my:443/blog/air%20service?page=1#my-id"
1. Path 不需要 encode
2. Query 需要 encode, 最好是通过 QueryBuilder 制作
3. Path, Query, Fragment 最好 starts with "/", "?", "#" 因为 Read 的时候是有的, 让它们一致性.
总结
1. 通过 Request 获取 information 比较 Uri 方便, 尤其是 Query 的部分, 因为它有封装.
2. 可以借助 QueryHelpers 来读取 Query, Request.Query 内部也是靠它的.
3. Path 都是自动 encode, decode 的.
4. Query string 需要 Builder, Helpers 来做帮助 encode, decode.
ASP.NET Core – 操作 Uri 和 Query的更多相关文章
- asp.net core 操作误区
更新时提示数据变化错误 在更新事件中提示下面错误,在网上找了一下,大部分都是说是冲突问题,但是测试时同时只有一个客户端在进行操作,不应该会有冲突问题,后来发现编辑加载时的ID,和更新提交时的ID不同了 ...
- ASP.NET Core使用EF Core操作MySql数据库
ASP.NET Core操作MySql数据库, 这样整套环境都可以布署在Linux上 使用微软的 Microsoft.EntityFrameworkCore(2.1.4) 和MySql出的 MySql ...
- ASP.NET Core 开发-Entity Framework (EF) Core 1.0 Database First
ASP.NET Core 开发-Entity Framework Core 1.0 Database First,ASP.NET Core 1.0 EF Core操作数据库. Entity Frame ...
- ASP.NET Core 开发 - Entity Framework (EF) Core
EF Core 1.0 Database First http://www.cnblogs.com/linezero/p/EFCoreDBFirst.html ASP.NET Core 开发 - En ...
- ASP.NET Core 中间件自定义全局异常处理
目录 背景 ASP.NET Core过滤器(Filter) ASP.NET Core 中间件(Middleware) 自定义全局异常处理 .Net Core中使用ExceptionFilter .Ne ...
- 用asp.net core结合fastdfs打造分布式文件存储系统
最近被安排开发文件存储微服务,要求是能够通过配置来无缝切换我们公司内部研发的文件存储系统,FastDFS,MongDb GridFS,阿里云OSS,腾讯云OSS等.根据任务紧急度暂时先完成了通过配置来 ...
- Filters in ASP.NET Core(Net6之过滤器)
Filters in ASP.NET Core 如果觉得样式不好:跳转即可 (md文件复制过来有些样式会不一样) 原文地址:https://lifengying.site/archives/net6% ...
- ASP.NET Core 1.0 使用 Dapper 操作 MySql(包含事务)
操作 MySql 数据库使用MySql.Data程序包(MySql 开发,其他第三方可能会有些问题). project.json 代码: { "version": "1. ...
- 在ASP.NET Core中使用AOP来简化缓存操作
前言 关于缓存的使用,相信大家都是熟悉的不能再熟悉了,简单来说就是下面一句话. 优先从缓存中取数据,缓存中取不到再去数据库中取,取到了在扔进缓存中去. 然后我们就会看到项目中有类似这样的代码了. pu ...
- Asp.Net Core 2.0 项目实战(11) 基于OnActionExecuting全局过滤器,页面操作权限过滤控制到按钮级
1.权限管理 权限管理的基本定义:百度百科. 基于<Asp.Net Core 2.0 项目实战(10) 基于cookie登录授权认证并实现前台会员.后台管理员同时登录>我们做过了登录认证, ...
随机推荐
- 外部网关协议BGP
不能全部使用RIP与OSPF的原因有二:互联网规模太大,自治系统间路由选择困难:自治系统间路由选择必须考虑有关策略. 在每一个自治系统中有两种不同功能的路由器,边界路由器和内部路由器. BGP四种报文 ...
- yolov5 损失函数代码详解
前言 模型的损失计算包括3个方面,分别是: 定位损失 分类损失 置信度损失 损失的计算公式如下: 损失计算的代码流程也是按照这三大块来计算的.本篇主要讲解yolov5中损失计算的实现,包括损失的逻辑实 ...
- Mysql实现主从复制(一主双从)
一.环境介绍 LNMP(centos7,mysql5.6) vmware workstation pro配置了3个虚拟机,均安装了LNMP环境: master: 192.168.0.105 slav ...
- [oeasy]python0033_任务管理_jobs_切换任务_进程树结构_fg
查看进程 回忆上次内容 上次先进程查询 ps -elf 查看所有进程信息 ps -lf 查看本终端相关进程信息 杀死进程 kill -9 PID 给进程发送死亡信号 运行多个 python3 sh ...
- 机器学习:详解迁移学习(Transfer learning)
详解迁移学习 深度学习中,最强大的理念之一就是,有的时候神经网络可以从一个任务中习得知识,并将这些知识应用到另一个独立的任务中.所以例如,也许已经训练好一个神经网络,能够识别像猫这样的对象,然后使用那 ...
- NAS使用
openwrt下的samba设置 - 百度文库 (baidu.com) openwrt下 samba设置 (wjhsh.net) opkg updateopkg install shadow-user ...
- P6680 [CCO2019] Marshmallow Molecules 题解
P6680 题意 一个 \(n\) 点 \(m\) 边的图,图无重边,无自环. 满足这样一条性质:如果三边互不相等,则三边可以构成三角形. 思路 思路简单,用集合的思想来做. 引用一下 K0stlin ...
- MSPM0G3507外设DMA学习笔记
概述 变量的存储 正常情况下,变量存储在SRAM中,如果要发送该变量的值到外设,需要调用内核操作,使SRAM中的数据送到外设. 此类型操作过多会导致占用CPU高,整体卡顿. DMA控制概述 DMA:D ...
- Jmeter函数助手10-regexFunction
regexFunction函数用于对上一个请求进行正则表达式提取处理,类似正则表达式. 用于从前一个请求搜索结果的正则表达式:填入正则表达式 Template for the replacement ...
- vue里使用px2rem
安装 yarn add postcss-px2rem 配置 在vue.config.js中添加以下配置 const px2rem = require('postcss-px2rem') module. ...