Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Post with file parameter order #2115

Closed
kyugoa opened this issue Jul 14, 2023 · 4 comments
Closed

Post with file parameter order #2115

kyugoa opened this issue Jul 14, 2023 · 4 comments

Comments

@kyugoa
Copy link

kyugoa commented Jul 14, 2023

DO NOT USE ISSUES FOR QUESTIONS

Describe the bug
when posting to an endpoint with File attachment, if there are other input parameters in the body, the File parameter is always render first regardless of the order the parameter is added.

To Reproduce
call an api endpoint with file attachment and additional parameter like fileName and fileType.

` using var fileServiceClient = GetFileServiceClient();
var request = new RestRequest("file/v1/files", Method.Post);

  //added these parameters first
  request.AddJsonBody(new FileUploadRequestModel { Type = "tps-volume-titles", FileName = fileName });

  //then the file parameter
  request.AddFile("file", fileContent, fileName, "text/csv");
  request.AlwaysMultipartFormData = true;

  var response = await fileServiceClient.PostAsync(request).ConfigureAwait(false);

  if (!response.IsSuccessStatusCode)
  {
    _logger.LogError($"Failed to upload file {fileName}");
  }

`

Expected behavior
Expected the http request to be:

POST https://somedomain.com/file/v1/files HTTP/1.1
Host: file-service.adsp-uat.alberta.ca
Authorization: Bearer eyxxx
Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml
User-Agent: RestSharp/110.2.0.0
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary="0a1f4db8-6981-4592-aff5-7b12b4e00197"
Content-Length: 115113

--0a1f4db8-6981-4592-aff5-7b12b4e00197
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=type

tps-volume-titles
--0a1f4db8-6981-4592-aff5-7b12b4e00197
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=filename

1680672668.csv
--0a1f4db8-6981-4592-aff5-7b12b4e00197
Content-Type: text/csv
Content-Disposition: form-data; name="file"; filename="1680672668.csv"

"xxx","2023-07-13 09:28:48",,ON00001,,"242006882001","S","*",,"2023-07-11","Subdivision Plan",,,"N",,"T","Fee Simple","242 006 882 +2",,,"2023-07-11"

--0a1f4db8-6981-4592-aff5-7b12b4e00197--

Actual rendered http request

POST https://somedomain.com/file/v1/files HTTP/1.1
Host: file-service.adsp-uat.alberta.ca
Authorization: Bearer eyxxx
Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml
User-Agent: RestSharp/110.2.0.0
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary="0a1f4db8-6981-4592-aff5-7b12b4e00197"
Content-Length: 115113

--0a1f4db8-6981-4592-aff5-7b12b4e00197
Content-Type: text/csv
Content-Disposition: form-data; name="file"; filename="1680672668.csv"

"xxx","2023-07-13 09:28:48",,ON00001,,"242006882001","S","*",,"2023-07-11","Subdivision Plan",,,"N",,"T","Fee Simple","242 006 882 +2",,,"2023-07-11"

--0a1f4db8-6981-4592-aff5-7b12b4e00197
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=type

tps-volume-titles
--0a1f4db8-6981-4592-aff5-7b12b4e00197
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=filename

1680672668.csv
--0a1f4db8-6981-4592-aff5-7b12b4e00197--

Stack trace

Desktop (please complete the following information):

  • OS: windows
  • .NET version 6
  • Version 110.2.0

Additional context
Add any other context about the problem here.

@kyugoa kyugoa added the bug label Jul 14, 2023
@b166er
Copy link

b166er commented Jul 14, 2023

I have the same problem!

my first code, was very simple and naive. unfortunately it did not work as i expected:

// Create RestClient
RestClient client = new RestClient("https://api-server");

// Create RestRequest
RestRequest request = new RestRequest("path", Method.POST) {FormBoundary = "MySpecialBounderyIdentifierTest123"};

request.AddJsonBody(myJsonData);
request.AddFile(filename, fileContent, filename);

// overwrite Content-Type with my custom requirement
request.AddOrUpdateHeader("Content-Type", "multipart/mixed");

// Execute the request
IRestResponse response = client.Execute(request);

// Handle the response
if (response.IsSuccessful)
{
    Console.WriteLine("File uploaded successfully!");
}
else
{
    Console.WriteLine("File upload failed. Error: " + response.ErrorMessage);
} 

output with Version 110.2.0 is:

Authorization: Basic myloginauthkey
Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml
User-Agent: RestSharp/110.2.0.0
Content-Type: multipart/mixed; 
boundary="MySpecialBounderyIdentifierTest123"
Host: api-server
Content-Length: 123
Connection: Keep-Alive

--MySpecialBounderyIdentifierTest123
Content-Type: application/zip
Content-Disposition: form-data; name="mytest.zip"; filename="mytest.zip"
...binary-bytes...
--MySpecialBounderyIdentifierTest123
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name="application/json"
{"meta": [{"title": "My Homework","author": {"name": "Tessa","adress": null},"created": 1550743894373}]}
--MySpecialBounderyIdentifierTest123--

my expected output was:

Authorization: Basic myloginauthkey
Accept: application/json, text/json, text/x-json, text/javascript, application/xml, text/xml
User-Agent: RestSharp/110.2.0.0
Content-Type: multipart/mixed; boundary="MySpecialBounderyIdentifierTest123"
Host: api-server
Content-Length: 123
Connection: Keep-Alive

--MySpecialBounderyIdentifierTest123
Content-Type: application/json; charset=utf-8
Content-Disposition: form-data; name="application/json"
{"meta": [{"title": "My Homework","author": {"name": "Tessa","adress": null},"created": 1550743894373}]}
--MySpecialBounderyIdentifierTest123
Content-Type: application/zip
Content-Disposition: form-data; name="mytest.zip"; filename="mytest.zip"
...binary-bytes...
--MySpecialBounderyIdentifierTest123--

Here is my ugly code to set the order manually. It would be so great if the output of the parameters were in the same order as the executing Add-Methods, or at least some simple functionality to set the order, e.g. an order parameter.

using System.Text;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        // Create RestClient
        RestClient client = new RestClient("https://api-server");
        
        // Create RestRequest
        RestRequest request = new RestRequest("path", Method.POST);
        
        string boundary = "MySpecialBounderyIdentifierTest123";
        string filename = "mytest.zip";
        
        // set manually content and encode all data to bytes
        byte[] start = Encoding.UTF8.GetBytes($"--{boundary}{Environment.NewLine}");
        byte[] separator = Encoding.UTF8.GetBytes($"{Environment.NewLine}{boundary}--{Environment.NewLine}");
        byte[] end = Encoding.UTF8.GetBytes($"{Environment.NewLine}--{boundary}--{Environment.NewLine}");
        
        string json = @"{
            ""meta"": [{
                    ""title"": ""My Homework"",
                    ""author"": 
                    {
                        ""name"": ""Tessa"",
                        ""adress"": null
                    }, 
                    ""created"": 1550743894373
                }]
        }";
        // Content-Disposition is optional and not necessary for me/the api-server
        byte[] jsonMeta = Encoding.UTF8.GetBytes($"Content-Type: application/json{Environment.NewLine}{Environment.NewLine}{json}");
        byte[] fileMeta = Encoding.UTF8.GetBytes($"Content-Type: application/zip{Environment.NewLine}Content-Disposition: form-data; name=\"file\"; filename=\"{filename}\"{Environment.NewLine}{Environment.NewLine}");
        
        // use Linqs byte-concatenation
        IEnumerable<byte> requestBytes;
        
        // Combine first json data, then file content! the order is very important.
        requestBytes = start // start with boundary-start-sequence
            .Concat(jsonMeta) // important: json Data MUST be in the first position, before binary file payload
            .Concat(separator) // add boundary separator
            .Concat(fileMeta) // file data need as second parameter, after json data!
            .Concat(File.ReadAllBytes(filename)) // add file binary payload data as bytes
            .Concat(end) // add boundary-endsequence
        ;
        
        // in my personal case, I have to set an additional custom Content-Type Header
        // multipart/form-data does not work with my particular api-server!
        string header = $"multipart/mixin; boundary={boundary}";

        // set manually header and byte-content to parameter
        request.AddParameter(header, requestBytes.toArray(), ParameterType.RequestBody);
        
        // Execute the request
        IRestResponse response = client.Execute(request);
        
        // Handle the response
        if (response.IsSuccessful)
        {
            Console.WriteLine("File uploaded successfully!");
        }
        else
        {
            Console.WriteLine("File upload failed. Error: " + response.ErrorMessage);
        }       
    }
}

@koo9
Copy link

koo9 commented Jul 14, 2023

just render the paramter by the order added would be sufficient. I believe you can set the file content type in the .AddFile() method but the overall content-type is still multipart/form-data

@alexeyzimarev alexeyzimarev removed the bug label Jul 15, 2023
@alexeyzimarev alexeyzimarev changed the title Post with file parameter order bug Post with file parameter orde Jul 15, 2023
@alexeyzimarev alexeyzimarev changed the title Post with file parameter orde Post with file parameter order Jul 15, 2023
@alexeyzimarev
Copy link
Member

Body parameters are added to the request message in the same order as they are added to RestRequest. But, files are added separately, as well as POST parameters when the content is a multipart form.

@alexeyzimarev
Copy link
Member

Duplicate of #2098

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants