英文:
Difference in multipart/form-data usage in versions 106 and 107
问题
我尝试将RestSharp版本从104升级到108。
其他API(application/json)正常。但在使用multipart/form-data传输文件时,在服务器端没有文件。
我尝试尽可能使它们相似,但仍然不起作用。
所以我认为随着版本的更改会有一些变化。
我找到了一个不起作用的版本。
正常工作:版本104.4.0,106.15.0
不起作用:版本107.3.0,108.0.3
在从版本107传输文件时,是否还需要做其他操作?
版本106.15.0
var client = new RestClient("https://192.168.0.1/");
client.Timeout = -1;
var request = new RestRequest("file", Method.POST);
request.AlwaysMultipartFormData = true;
request.AddHeader("Authorization", "Auth-Token" + token);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Accept", "application/json");
request.AddFile("file", path);
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var response = client.Execute(request);
Console.WriteLine(response.Content);
从Postman控制台中捕获
POST /file HTTP/1.1
Authorization: Auth-Token ----
Postman-Token: ecff7025-298c-4b29-8b15-ebd0d679aad8
Host: 192.168.0.1
Content-Type: multipart/form-data; boundary=--------------------------077067924352348455764323
Content-Length: 2409
----------------------------077067924352348455764323
Content-Disposition: form-data; name="file"; filename="filename.txt"
<filename.txt>
----------------------------077067924352348455764323--
版本107.3.0
var options = new RestClientOptions("https://192.168.0.1/")
{
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
ConfigureMessageHandler = handler =>
new HttpTracerHandler(handler, new ConsoleLogger(), HttpMessageParts.All),
};
var client = new RestClient(options);
var request = new RestRequest("file", Method.Post);
request.AlwaysMultipartFormData = true;
request.AddHeader("Authorization", "Auth-Token" + token);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Accept", "application/json");
request.AddFile("file", path);
var response = client.ExecuteAsync(request).Result;
Console.WriteLine(response.Content);
从HttpTracer中捕获
==================== HTTP REQUEST: [POST] ====================
POST https://192.168.0.1/file
Authorization: Auth-Token ----
Accept: application/json
User-Agent: RestSharp/0.0.0.0
--94000a34-a3fa-4eeb-b803-52eea1c7cbc9
Content-Type: application/octet-stream
Content-Disposition: form-data; name="file"; filename="filename.txt"
/////////////////////////////////
文件内容...
/////////////////////////////////
--94000a34-a3fa-4eeb-b803-52eea1c7cbc9--
附加:
nginx错误日志
upstream timed out (110: Connection timed out) while reading response header from upstream
编辑:
添加了使用Postman和HttpTracer捕获的Post消息的数据。
编辑2:
添加了nginx错误日志。
英文:
I tried to update RestSharp version from 104 to 108.
Other APIs (application/json) fine.
But in multipart/form-data to transfer file, On server side, there is no file.
I tried to make them as similar as possible. But it still didn't work.
So I thought that there will be some changes as the version changes.
I found a version that doesn't work.
Working : Version 104.4.0, 106.15.0
Not working : Version 107.3.0, 108.0.3
Is there anything else I need to do to transfer files from version 107?
Version 106.15.0
var client = new RestClient("https://192.168.0.1/");
client.Timeout = -1;
var request = new RestRequest("file", Method.POST);
request.AlwaysMultipartFormData = true;
request.AddHeader("Authorization", "Auth-Token" + token);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Accept", "application/json");
request.AddFile("file", path);
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var response = client.Execute(request);
Console.WriteLine(response.Content);
Capture from postman console
POST /file HTTP/1.1
Authorization: Auth-Token ----
Postman-Token: ecff7025-298c-4b29-8b15-ebd0d679aad8
Host: 192.168.0.1
Content-Type: multipart/form-data; boundary=--------------------------077067924352348455764323
Content-Length: 2409
----------------------------077067924352348455764323
Content-Disposition: form-data; name="file"; filename="filename.txt"
<filename.txt>
----------------------------077067924352348455764323--
Version 107.3.0
var options = new RestClientOptions("https://192.168.0.1/")
{
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
ConfigureMessageHandler = handler =>
new HttpTracerHandler(handler, new ConsoleLogger(), HttpMessageParts.All),
};
var client = new RestClient(options);
var request = new RestRequest("file", Method.Post);
request.AlwaysMultipartFormData = true;
request.AddHeader("Authorization", "Auth-Token" + token);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Accept", "application/json");
request.AddFile("file", path);
var response = client.ExecuteAsync(request).Result;
Console.WriteLine(response.Content);
Capture from HttpTracer
==================== HTTP REQUEST: [POST] ====================
POST https://192.168.0.1/file
Authorization: Auth-Token ----
Accept: application/json
User-Agent: RestSharp/0.0.0.0
--94000a34-a3fa-4eeb-b803-52eea1c7cbc9
Content-Type: application/octet-stream
Content-Disposition: form-data; name="file"; filename="filename.txt"
/////////////////////////////////
File contents ...
/////////////////////////////////
--94000a34-a3fa-4eeb-b803-52eea1c7cbc9--
Add :
> nginx error log<br>
> upstream timed out (110: Connection timed out) while reading response header from upstream
Edit :
Added captured data of Post messages using postman and httptracer.
Edit2 :
Added nginx error log
答案1
得分: 0
以下是翻译好的部分:
Problem is System.Net.Http.MultipartFormDataContent(string boundary)
在 restsharp 版本 106.15.0 中,它通过 RestSharp.Http
发起 HTTP 请求。但是从版本 107 开始,restsharp 使用 System.Net.Http
。这导致了一个区别。
在调用 RestSharp.ExecuteAsync
时,它在内部调用了 RestSharp.RequestContent.AddFiles
。
在这里,会创建 System.Net.Http.MultipartFormDataContent
并将 Boundary 作为参数传递。
RequestContent.cs
void AddFiles()
{
if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;
var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
// ...
}
结果是,Boundary 存储在 MultipartFormDataContent.Headers.ContentType.Parameters
中,这就是区别所在。
在 106.15.0 中,Boundary 字符串没有用双引号括起来。
"---------be62f9a5-f149-4782-a129-ad7bdae33924"
然而,在 107版本及以后,Boundary 字符串被双引号括起来。
---------be62f9a5-f149-4782-a129-ad7bdae33924
这导致我的服务器无法识别 Boundary 字符串,因此文件未能传递。
为了去掉双引号,我不得不自己修改 RestSharp 库。
RequestContent.cs
void AddFiles()
{
if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;
var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
var boundary = mpContent.Headers.ContentType.Parameters.First(o => o.Name == "boundary");
boundary.Value = boundary.Value.Replace("\"", String.Empty);
// ...
}
将上述修改应用到以下部分:
RestRequest.cs
添加新属性
public bool RemoveBoundaryQuotes { get; set; }
RequestContent.cs
处理新属性
void AddFiles() {
if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;
var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
if (_request.RemoveBoundaryQuotes == true) {
RemoveBoundaryQuotes(mpContent);
}
// ...
}
private static void RemoveBoundaryQuotes(MultipartFormDataContent mpContent) {
if (mpContent.Headers.ContentType != null) {
var boundary = mpContent.Headers.ContentType.Parameters.First(o => o.Name == "boundary");
if (boundary != null) {
if (string.IsNullOrEmpty(boundary.Value) == false) {
string boundaryText = boundary.Value;
if (boundaryText.StartsWith("\"") && boundaryText.EndsWith("\"")) {
boundaryText = boundaryText.Remove(0, 1);
boundaryText = boundaryText.Remove(boundaryText.Length - 1, 1);
boundary.Value = boundaryText;
}
}
}
}
}
string GetContentTypeHeader(string contentType) {
if(Content is MultipartFormDataContent) {
return _request.RemoveBoundaryQuotes is true ?
$"{contentType}; boundary={GetOrSetFormBoundary()}" :
$"{contentType}; boundary=\"{GetOrSetFormBoundary()}\"";
}
else {
return contentType;
}
}
英文:
Problem is System.Net.Http.MultipartFormDataContent(string boundary)
In restsharp version 106.15.0, it is using http request via RestSharp.Http
.
But since version 107 restsharp uses System.Net.Http
.
This makes a difference.
When calling RestSharp.ExecuteAsync
, it internally calls RestSharp.RequestContent.AddFiles
.<br>
Here, System.Net.Http.MultipartFormDataContent
is created and Boundary is passed as a parameter.
RequestContent.cs
void AddFiles()
{
if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;
var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
// ...
}
As a result, the boundary is stored in MultipartFormDataContent.Headers.ContentType.Parameters
, and this is where the difference occurs.
In 106.15.0 , the boundary string is not enclosed in double quotes.<br>
> "---------be62f9a5-f149-4782-a129-ad7bdae33924"
However, in version 107 and later, the boundary string is enclosed in double quotation marks.<br>
> ---------be62f9a5-f149-4782-a129-ad7bdae33924
That made it seem like my server couldn't recognize the boundary string and the file wasn't being delivered.
<br>
<br>
To remove double quotes I had to modify the RestSharp Library myself.
RequestContent.cs
void AddFiles()
{
if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;
var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
var boundary = mpContent.Headers.ContentType.Parameters.First(o => o.Name == "boundary"); ;
boundary.Value = boundary.Value.Replace("\"", String.Empty);
// ...
}
string GetContentTypeHeader(string contentType)
=> Content is MultipartFormDataContent
? $"{contentType}; boundary=\"{GetOrSetFormBoundary()}\""
: contentType;
To this
RestRequest.cs
Add new property
public bool RemoveBoundaryQuotes { get; set; }
RequestContent.cs
Process new property
void AddFiles() {
if (!_request.HasFiles() && !_request.AlwaysMultipartFormData) return;
var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());
if (_request.RemoveBoundaryQuotes == true) {
RemoveBoundaryQuotes(mpContent);
}
// ...
}
private static void RemoveBoundaryQuotes(MultipartFormDataContent mpContent) {
if (mpContent.Headers.ContentType != null) {
var boundary = mpContent.Headers.ContentType.Parameters.First(o => o.Name == "boundary");
if (boundary != null) {
if (string.IsNullOrEmpty(boundary.Value) == false) {
string boundaryText = boundary.Value;
if (boundaryText.StartsWith("\"") && boundaryText.EndsWith("\"")) {
boundaryText = boundaryText.Remove(0, 1);
boundaryText = boundaryText.Remove(boundaryText.Length - 1, 1);
boundary.Value = boundaryText;
}
}
}
}
}
string GetContentTypeHeader(string contentType) {
if(Content is MultipartFormDataContent) {
return _request.RemoveBoundaryQuotes is true ?
$"{contentType}; boundary={GetOrSetFormBoundary()}" :
$"{contentType}; boundary=\"{GetOrSetFormBoundary()}\"";
}
else {
return contentType;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论