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();
            }
        }

    }
}
 
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):
 
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.

Sure, this is also quite interesting, because this postman code don't work aswell, returns:
Code:
System.FormatException: „The format of value 'multipart/form-data; boundary=TestoLimite' is invalid.
    System.Net.Http.Headers.MediaTypeHeaderValue.CheckMediaTypeFormat(string, string) w MediaTypeHeaderValue.cs
    System.Net.Http.Headers.MediaTypeHeaderValue.MediaTypeHeaderValue(string) w MediaTypeHeaderValue.cs
    System.Net.Http.StringContent.StringContent(string, System.Text.Encoding, string) w StringContent.cs
    Program.Main(string[]) w Program.cs
    System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() w ExceptionDispatchInfo.cs
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task) w TaskAwaiter.cs
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task) w TaskAwaiter.cs
    System.Runtime.CompilerServices.TaskAwaiter.GetResult() w TaskAwaiter.cs
    Program.<Main>(string[])

”


PostmanCode:
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "url");
request.Headers.Add("Authorization", "Bearer token");
var content = new StringContent("--TestoLimite\r\nContent-Disposition: form-data; name=\"metadata\"\r\n\r\n{\"Metadata\":{\"Data\":{\"Name\":\"Najnowsza29\",\"Category\":\"5000025\",\"MainLang\":\"6\",\"AREQ\":\"2\",\"SendNotification\":\"2\",\"S01_1\":\"3000031\",\"S01_3\":\"3000263\",\"Ambito\":[\"3000756\",\"3000744\"]}},\"DeleteFile\":[],\"Files\":[]}\r\n--TestoLimite--", null, "multipart/form-data; boundary=TestoLimite");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
 
What does that C# code look like as compared to your C# code.

Don't distract them too much; they might forget to answer the question I asked :D

Let's get the working request bytes and the broken request bytes, fix the broken request so it works, work out which part of the code generated the broken part of the request, and fix the code
 

Latest posts

Back
Top Bottom