.NET Core HttpClient调用腾讯云对象存储Web API的"ERROR_CGI_PARAM_NO_SUCH_OP"问题
2017-11-10 23:53
627 查看
开门见山地说一下问题的原因:调用 web api 时请求头中多了双引号,请求体中少了双引号。
腾讯云提供的对象存储(COS)C# SDK 是基于 .NET Framework 用 WebRequest 实现的,我们直接将这个实现迁移到 .NET Core 是可以正常调用,但后来我们基于 HttpClient 实现,调用 web api 时总是返回 "ERROR_CGI_PARAM_NO_SUCH_OP" 错误。
用 Wireshark 抓包后发现,基于 WebRequest 的实现的请求包开头比基于 HttpClient 的实现多了个 "Preamble: 0d0a"。
1)基于 WebRequest 的实现
2)基于 HttpClient 的实现
检查代码后发现,在构建 multipart/form-data 时,腾讯云官方基于 WebRequest 的实现是这样构建数据包的开头的:
我们基于 HttpClient 的实现用的是 MultipartFormDataContent :
前者构建的 Multipart 数据包比后者多出了 \r\n (回车换行),而 0d0a 正是 \r\n 的 ASCII 码。根据 Multipart Content-Type 规范,这个多出来的 \r\n 是多余的,所以被解析为 "Preamble: 0d0a" 。
于是修改基于 HttpClient 的实现,也加上这个额外的 \r\n :
但加上后依然是"ERROR_CGI_PARAM_NO_SUCH_OP"错误(实际上不加开头的 \r\n 也没关系,问题与这个无关)。
继续仔细对比抓包,发现 HttpClient 的实现中 form-data 部署少了双引号,比如 name=op ,基于 WebRequest 的实现用的是 name="op"
但加上后依旧是"ERROR_CGI_PARAM_NO_SUCH_OP"错误。
再继续对比抓包,发现 HttpClient 的实现这 Content-Type 中比 WebRequest 的实现多了2个双引号
1) Content-Type: multipart/form-data; boundary="---------------8d5289300ea3a0d"
2) Content-Type: multipart/form-data; boundary=---------------8d527aeed341201
去找这2个双引号之后,问题终于解决了。
最终基于 .NET Core HttpClient 的实现代码如下("Preamble: 0d0a"没有影响,不需要加):
腾讯云提供的对象存储(COS)C# SDK 是基于 .NET Framework 用 WebRequest 实现的,我们直接将这个实现迁移到 .NET Core 是可以正常调用,但后来我们基于 HttpClient 实现,调用 web api 时总是返回 "ERROR_CGI_PARAM_NO_SUCH_OP" 错误。
用 Wireshark 抓包后发现,基于 WebRequest 的实现的请求包开头比基于 HttpClient 的实现多了个 "Preamble: 0d0a"。
1)基于 WebRequest 的实现
2)基于 HttpClient 的实现
检查代码后发现,在构建 multipart/form-data 时,腾讯云官方基于 WebRequest 的实现是这样构建数据包的开头的:
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x"); var beginBoundary = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
我们基于 HttpClient 的实现用的是 MultipartFormDataContent :
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x"); var data = new MultipartFormDataContent(boundary);
前者构建的 Multipart 数据包比后者多出了 \r\n (回车换行),而 0d0a 正是 \r\n 的 ASCII 码。根据 Multipart Content-Type 规范,这个多出来的 \r\n 是多余的,所以被解析为 "Preamble: 0d0a" 。
于是修改基于 HttpClient 的实现,也加上这个额外的 \r\n :
var ms = new MemoryStream(); var bytes = Encoding.UTF8.GetBytes("\r\n"); ms.Write(bytes, 0, bytes.Length); (await data.ReadAsStreamAsync()).CopyTo(ms); ms.Position = 0; var sc = new StreamContent(ms); sc.Headers.ContentType = data.Headers.ContentType; request.Content = sc;
但加上后依然是"ERROR_CGI_PARAM_NO_SUCH_OP"错误(实际上不加开头的 \r\n 也没关系,问题与这个无关)。
继续仔细对比抓包,发现 HttpClient 的实现中 form-data 部署少了双引号,比如 name=op ,基于 WebRequest 的实现用的是 name="op"
但加上后依旧是"ERROR_CGI_PARAM_NO_SUCH_OP"错误。
再继续对比抓包,发现 HttpClient 的实现这 Content-Type 中比 WebRequest 的实现多了2个双引号
1) Content-Type: multipart/form-data; boundary="---------------8d5289300ea3a0d"
2) Content-Type: multipart/form-data; boundary=---------------8d527aeed341201
去找这2个双引号之后,问题终于解决了。
最终基于 .NET Core HttpClient 的实现代码如下("Preamble: 0d0a"没有影响,不需要加):
var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Authorization", signature);
var boundary = "---------------" + DateTime.Now.Ticks.ToString("x"); var data = new MultipartFormDataContent(boundary);
data.Add(new ByteArrayContent(Encoding.UTF8.GetBytes("upload")), "\"op\"");
var streamContent = new StreamContent(uploadStream);
streamContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = "\"fileContent\"",
FileName = "\"" + fileName + "\""
};
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
data.Add(streamContent);
data.Headers.Remove("Content-Type");
data.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary);
request.Content = data;
var response = await _httpClient.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
相关文章推荐
- Android问题集锦之二十九:Cannot run program "/xx/build-tools/xx/aapt": error=2, No such file or directory
- Python2.7以上版本解决 "ImportError: No module named MySQLdb"问题
- cocos2d-x 移植到android中编译的一些问题:fatal error: Box2D/Box2D.h: No such file or directory"
- 在Android中使用HttpClient 4.3.x出现NoSuchFieldError的问题
- Android问题集锦之二十九:Cannot run program "/xx/build-tools/xx/aapt": error=2, No such file or directory
- cocos2d-x 移植到android中编译的一些问题:fatal error: Box2D/Box2D.h: No such file or directory"
- 在Android中使用HttpClient 4.3.x出现NoSuchFieldError的问题
- 解决 "OperationalError: (sqlite3.OperationalError) no such table: ..."问题
- Erlang 编译时出现"no such file or directory" 与 当前工作目录问题
- Cygwin中用NDK来编译程序出现"No such file or directory"问题
- Erlang 编译时出现"no such file or directory" 与 当前工作目录问题
- 在Android中使用HttpClient 4.3.x出现NoSuchFieldError的问题
- Cvs中的"no such repository"问题调试
- Swagger UI post请求时出现"no response from server"问题
- "libcudnn.so.5 cannot open shared object file: No such file or directory"
- Exception in thread "main" java.lang.NoSuchFieldError: level
- error:no such partition.grub rescue>问题
- 问题记录:virsh start error:cannot get interface MTU on 'qbrXXX':No such device
- Java 调用 FFMPEG 命令时用 url 作为输入源,Linux 下出现 “no such file or directory” 问题的解决
- 关于linq to sql调用存储过程,出现"无法枚举查询结果多次"的问题