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
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
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
.
Here, System.Net.Http.MultipartFormDataContent
is created and Boundary is passed as a parameter.
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.
"---------be62f9a5-f149-4782-a129-ad7bdae33924"
However, in version 107 and later, the boundary string is enclosed in double quotation marks.
---------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.
To remove double quotes I had to modify the RestSharp Library myself.
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;
Add new property
public bool RemoveBoundaryQuotes { get; set; }
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;
}
}