FormData in post to API

j3k01

Member
Joined
Sep 6, 2023
Messages
12
Programming Experience
1-3
Hi, I'm struggling with post to api on endpoint that requires formdata content-type. I dont have idea why json in postman works, but in my code not. I'll be glad for any type of help :)


post to api:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using Newtonsoft.Json.Linq;
using System.Text;
using System.Net;
using Newtonsoft.Json;
using mesaCon;

namespace MesaConnection
{
    class Program
    {
        private const string MesaApiUrl = "url";
        private const string MesaApiUrlPost = "url2";

        static async Task Main(string[] args)
        {
            try
            {
                var authToken = await GetAuthToken();

                byte[] Blob64byte = Encoding.ASCII.GetBytes("qwerty");

                var requestData = new Root
                {
                    Metadata = new Metadata
                    {
                        Data = new Data
                        {
                            Name = "Najnowsza27",
                            Category = "5000025",
                            MainLang = "6",
                            UploadFile = new UploadFile
                            {
                                FileName = "Mesa n3.pdf",
                                Description = "Mesa n3",
                                Blob64 = Blob64byte
                            },
                            AREQ = "2",
                            SendNotification = "2",
                            S01_1 = "3000033",
                            S01_3 = "3000263",
                            Ambito = new List<string> { "3000756", "3000744" }
                        }
                    },
                    DeleteFile = new List<object>(),
                    Files = new List<object>()
                };

                string jsonRequest = JsonConvert.SerializeObject(requestData);


                Console.WriteLine("JSON Request:");
                Console.WriteLine(jsonRequest);

                var postMessage = await ApiPostRequest(authToken, jsonRequest);



                Console.WriteLine("Response from POST Request:");
                Console.WriteLine(postMessage);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught: " + ex.Message);
            }
        }


        private static async Task<string> GetAuthToken()
        {
            using (var client = new HttpClient())
            {
                var baseUri = "url";
                var username = "1";
                var password = "2!";
                var clientId = "3";

                var formData = new List<KeyValuePair<string, string>>
                {
                    new KeyValuePair<string, string>("an_login_type_id", "1"),
                    new KeyValuePair<string, string>("client_id", clientId),
                    new KeyValuePair<string, string>("grant_type", "password"),
                    new KeyValuePair<string, string>("password", password),
                    new KeyValuePair<string, string>("username", username),
                };

                var content = new FormUrlEncodedContent(formData);
                var response = await client.PostAsync(baseUri + "/auth/token", content);

                if (response.IsSuccessStatusCode)
                {
                    var responseContent = await response.Content.ReadAsStringAsync();
                    var jwtToken = JObject.Parse(responseContent)["access_token"].ToString();
                    return jwtToken;
                }

                throw new Exception("Nie udało się uzyskać tokena JWT.");
            }
        }

        private static async Task<string> ApiGetRequest(string authToken)
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);

                var endpoint = await client.GetAsync(MesaApiUrl);

                if (endpoint.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    throw new Exception("Brak autoryzacji. Sprawdź swoje uwierzytelnienie.");
                }

                endpoint.EnsureSuccessStatusCode();
                string responseBody = await endpoint.Content.ReadAsStringAsync();
                return responseBody;
            }
        }

        private static async Task<string> ApiPostRequest(string authToken, string jsonRequest)
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);

                string boundary = "Test";

                JObject jsonDataObject = JObject.Parse(jsonRequest);

                var content = new MultipartFormDataContent(boundary);

                foreach (var property in jsonDataObject["Metadata"]["Data"].Children())
                {
                    content.Add(new StringContent(property.First.ToString()), property.Path);
                }

                string fileName = jsonDataObject["Metadata"]["Data"]["UploadFile"]["FileName"].ToString();
                byte[] fileData = Convert.FromBase64String(jsonDataObject["Metadata"]["Data"]["UploadFile"]["Blob64"].ToString());
                var fileContent = new ByteArrayContent(fileData);
                fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
                content.Add(fileContent, "file", fileName);

                content.Headers.Remove("Content-Type");
                content.Headers.TryAddWithoutValidation("Content-Type", $"multipart/form-data; boundary={boundary}");

                var response = await client.PostAsync(MesaApiUrlPost, content);

                if (response.StatusCode == HttpStatusCode.Unauthorized)
                {
                    throw new Exception("Brak autoryzacji. Sprawdź swoje uwierzytelnienie.");
                }

                return await response.Content.ReadAsStringAsync();
            }
        }

    }
}
 
No, no problem with C#. It's every bit as capable at putting bytes down a socket as any other modern language is.. you're just putting the wrong bytes down the socket, that's all

The flurl library I usually recommend for these kind of works has methods for making your multi part form life easier - How can I upload a file and form data using Flurl?
 
Last edited:
Report it as a bug to postman team; postman generated c# code that doesn't work. Probably they'll say it's just a helper for you, rather than intended to do all your work for you, and some user debugging efforts may be required. Or it may be that the postman code would work against a different API so maybe not fully postman team's fault

All in, it's definitely not C#'s fault that postman's code doesn't work with this particular api
 
What error are you getting?
 
Show us all salient differences between the request postman makes and the request your code makes
 
What's confusing me is that the OP keeps saying that he is posting JSON in PostMan, but the code he is presenting above looks to be posting multipart form (lines 132-146). The only JSON that I'm seeing (other than for getting the auth token), is from line 51 being sent to line 130 via line 122. It looks like the JSON there is only being used to pass a stringly-typed data structure to ApiPostRequest(). Folllowing best practices, he should be passing a strongly-typed Root[/code] object instead of that string [icode]jsonRequest.
 
Could be that janky approach of sending a file/image to an api where a multi part form is used with thejson in one part and the file in another part

(not that there isn't a janky approach, generally, to transmit a file to a json consuming api; b64'ing the image data and sending it as a json prop is also fairly horrendous)
 
Sure, maybe I explained something wrong. This api requires sending files in form-data format, which I don't quite know how to handle This is my postman request (ofc instead of qwerty I have really long pdf in base64):
Code:
--TestoLimite
Content-Disposition: form-data; name="metadata"

{"Metadata":{"Data":{"sectionPolicies":[{}],"Name":"abc123","Category":"5000051","UploadFile":{"FileName":"REST API - testowanie.pdf","Description":"REST API - testowanie","SmartTag":null,"Blob64":"qwerty","Errors":[]},"AREQ":"2","SendNotification":"2","S01_1":"3000033","S01_3":"3000263","S01_4":"3000269","S01_5":"3000271","S01_6":"3001256"}},"DeleteFile":[],"Files":[]}
--TestoLimite--
 
Last edited by a moderator:
If your c# sent the same bytes as postman then it would work.. so.. where is it differing?
 
If your c# sent the same bytes as postman then it would work.. so.. where is it differing?
Sorry, so do you see any difference between my postman request and this one in c#? I albo tried debug code and use request from jsonRequest in postman and everything works fine so I think that I probably made some mistakes with MultipartFormDataContent.
 
I also tried this approach, but with same results:

ApiPostRequest:
        private static async Task<string> ApiPostRequest(string authToken, string requestData)
        {
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);

                string boundary = "Test";

                var content = new MultipartFormDataContent(boundary);

                var jsonRequest = JsonConvert.SerializeObject(requestData);

                var jsonContent = new StringContent(jsonRequest, Encoding.UTF8, "multipart/form-data");

                content.Add(jsonContent, "Metadata");

                Console.WriteLine(content);

                var response = await client.PostAsync(MesaApiUrlPost, content);

                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    throw new Exception("Brak autoryzacji. Sprawdź swoje uwierzytelnienie.");
                }

                return await response.Content.ReadAsStringAsync();
            }
        }
 
Can you post, in CODE tags so it doesn't turn into a mess, the request being emitted by C# that doesn't work and the postman request that does.

Request bytes, not c# code. Send a hello world text file in each so you don't have to snip a megabyte JPEG

Don't forget to include the request headers
 
As I vaguely recall, Postman can also generate C# code for you. What does that C# code look like as compared to your C# code.
 
(not that there isn't a janky approach, generally, to transmit a file to a json consuming api; b64'ing the image data and sending it as a json prop is also fairly horrendous)
Apparently the API designer took the horrendous janky approach based on the response of the OP:
This is my postman request (ofc instead of qwerty I have really long pdf in base64):
 
Back
Top Bottom