版本106和107中multipart/form-data的使用差异

huangapple go评论69阅读模式
英文:

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(&quot;https://192.168.0.1/&quot;);
client.Timeout = -1;

var request = new RestRequest(&quot;file&quot;, Method.POST);
request.AlwaysMultipartFormData = true;

request.AddHeader(&quot;Authorization&quot;, &quot;Auth-Token&quot; + token);
request.AddHeader(&quot;Content-Type&quot;, &quot;multipart/form-data&quot;);
request.AddHeader(&quot;Accept&quot;, &quot;application/json&quot;);
request.AddFile(&quot;file&quot;, 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=&quot;file&quot;; filename=&quot;filename.txt&quot;
&lt;filename.txt&gt;
----------------------------077067924352348455764323--

Version 107.3.0

var options = new RestClientOptions(&quot;https://192.168.0.1/&quot;)
{
        RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =&gt; true,
        ConfigureMessageHandler = handler =&gt;
            new HttpTracerHandler(handler, new ConsoleLogger(), HttpMessageParts.All),
};


var client = new RestClient(options);

var request = new RestRequest(&quot;file&quot;, Method.Post);
request.AlwaysMultipartFormData = true;

request.AddHeader(&quot;Authorization&quot;, &quot;Auth-Token&quot; + token);
request.AddHeader(&quot;Content-Type&quot;, &quot;multipart/form-data&quot;);
request.AddHeader(&quot;Accept&quot;, &quot;application/json&quot;);
request.AddFile(&quot;file&quot;, 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=&quot;file&quot;; filename=&quot;filename.txt&quot;

/////////////////////////////////

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() &amp;&amp; !_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() &amp;&amp; !_request.AlwaysMultipartFormData) return;

        var mpContent = new MultipartFormDataContent(GetOrSetFormBoundary());

        var boundary = mpContent.Headers.ContentType.Parameters.First(o =&gt; o.Name == &quot;boundary&quot;); ;
        boundary.Value = boundary.Value.Replace(&quot;\&quot;&quot;, String.Empty);
        
        // ...
}

string GetContentTypeHeader(string contentType)
            =&gt; Content is MultipartFormDataContent
                ? $&quot;{contentType}; boundary=\&quot;{GetOrSetFormBoundary()}\&quot;&quot;
                : contentType;
To this
RestRequest.cs

Add new property

public bool RemoveBoundaryQuotes { get; set; }
RequestContent.cs

Process new property

void AddFiles() {
    if (!_request.HasFiles() &amp;&amp; !_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 =&gt; o.Name == &quot;boundary&quot;);
        if (boundary != null) {

            if (string.IsNullOrEmpty(boundary.Value) == false) {
                string boundaryText = boundary.Value;

                if (boundaryText.StartsWith(&quot;\&quot;&quot;) &amp;&amp; boundaryText.EndsWith(&quot;\&quot;&quot;)) {

                    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 ?
            $&quot;{contentType}; boundary={GetOrSetFormBoundary()}&quot; :
            $&quot;{contentType}; boundary=\&quot;{GetOrSetFormBoundary()}\&quot;&quot;;
    }
    else {
        return contentType;
    }
    
}

huangapple
  • 本文由 发表于 2023年2月10日 12:34:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75406988.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定