目的:要在URL中传递一个参数到服务端。
但是name
参数中有保留字符/
,所以先用UrlEncode
进行编码。
测试代码如下:
[Fact]
public async void GetByName_TestAsync()
{
var name = WebUtility.UrlEncode("aa/bbc");
var response = await Client.GetAsync($"api/users/{name}");
var user = await response.Content.ReadAsAsync<UserDto>();
user.Should().NotBeNull();
}
服务端API:
[HttpGet("{name}")]
public async Task<UserDto> Get(string name)
{
return await _userAppService.GetUserByNameAsync(name);
}
结果查不到name
=aa/bbc
的user。
通过debug发现,请求的结果是404,本来以为是MVC解码了URL再进行路由匹配,因此请求的路径就变成了aa/bbc
。但通过在浏览器中发起请求,就可以等到编码后的值aa%2Fbbc
,所以判断这是HttpClient的问题,它在发起请求之前对URL作了手脚。
那么如何通过URL传递带有保留字符(/
)的参数呢?
为了验证是不是HttpClient的问题,新建了一个控制台程序验证一下
static void Main(string[] args)
{
var client = new HttpClient()
{
BaseAddress = new Uri("http://localhost:5000/")
};
var name = "b/c";
name = WebUtility.UrlEncode(name);
var request = new HttpRequestMessage(HttpMethod.Get, $"users/{name}");
var task = client.SendAsync(request);
task.Wait();
var response = task.Result;
System.Console.WriteLine(response.StatusCode);
var task2 = response.Content.ReadAsStringAsync();
task2.Wait();
System.Console.WriteLine(task2.Result);
Console.WriteLine("Hello World!");
}
打印结果如下:
OK
Demo.UserDto
Hello World!
返回了正确的结果,因此证明了不是HttpClient的问题。但是可以发现,唯一的不同之处就是在测试中使用的Client
不是new HttpClient
而是通过TestServer.CreateClient
创建的,于是clone下aspnet/Hosting的源代码,一探TestServer
的究竟。最终发现问题出在如下代码中:
req.Path = PathString.FromUriComponent(request.RequestUri);
TestServer
创建的HttpClient
中增加了一个自定义的HttpMessageHandler
,其中就把我们传递给Client
的Request
进行拆装,如上所示,通过PathString.FromUriComponent
方法重新构造了请求地址,这个方法把URL给decode了,所以导致发送的请求路径出错了。
总结:asp.net core MVC并不存在所谓的编码问题,是集成测试的时候TestServer
搞的鬼。
如果URL中包含"%2F",可能在某些Server(例如:IIS, Http.sys)上有问题,刚刚部署到IIS中验证了一下,确实如果URL中含有%2F
,还是会404.
所以最好还是把含有"%2F"的片段放在QueryString
中,如果执意要放在路径中,还是用base64编码吧。
给个思路,你在传递参数的时候,先对参数处理/替换,如:
你的url: https://abc.com?parms=test?ceshi
?替换成 “_”
=>处理后的url:https://abc.com?parms=test_ceshi
----------------------
你的url: https://abc.com?parms=test?ceshi&ceshia
?替换成 “_”,&替换成“__”
=>处理后的url:https://abc.com?parms=test_ceshi__ceshia
----------------------
处理完毕以后在接收端解析参数的时候对“_”,"__" 还原即可,当然这样做不严谨,需要分析下你的参数会不会本身也有_的场景,如果有,你可以换成别的不被编码解码的字符
我本地试了一下,如果没有用UrlEncode
的话,确实是取不到全部值,但是加了UrlEncode
后,是可以获取到全部值的.
在本地使用
[HttpGet("api/users/{name}")]
以及
[HttpGet]
[Route("api/users/{name}")]
都可以成功获取
不好意思,问题中的参数填错了,应该是aa/bbc
,问题已更新。
@蝌蝌: 这种 Reserved character
最好的解决办法,还是把其替换成其他字符,然后再进行转义。维基百科上就是这样做的。
@BUTTERAPPLE: 发现不是MVC的问题,问题已更新。
或许用转义字符呢?aa\?bb 试试