Search code examples
c#constantcontact

400 - Bad Request when performing bulk import of contacts


I'm attempting to perform a bulk import using a Multipart content type, although, regardless what I try, I continually get error 400 - Bad Request. Unfortunately, this doesn't tell me much, and the documentation for bulk importing of contacts only states this error happens when there is an error when validating a contact. This is the method which I've currently got into place:

public void SendContactFile(string contactListId, FileInfo listFile)
{
    using (var client = new HttpClient())
    {
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        using (var content = new MultipartFormDataContent())
        {
            var contentPieces = new []
            {
                new KeyValuePair<string, string>("file_name", listFile.Name),
                new KeyValuePair<string, string>("lists", contactListId), 
            };
            //var data = new StreamContent(listFile.OpenRead()); //I've tried using both StreamContent and ByteArrayContent; both returned the same failure.
            var data = new ByteArrayContent(File.ReadAllBytes(listFile.FullName));
            data.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
            {
                FileName = listFile.Name
            };//I've tried with & without this header.

            foreach (KeyValuePair<string, string> pair in contentPieces)
            {
                content.Add(new StringContent(pair.Value), pair.Key);
            }
            content.Add(data);

            var result = client.PostAsync(FullApiAddress, content).Result;
            Console.WriteLine("Status Code: {0} - {1}, Request Message: {3}", 
                result.StatusCode, 
                result.ReasonPhrase,
                result.RequestMessage);
        }
    }
}

Lastly, the information displayed by my Console.WriteLine() is:

Status Code: BadRequest - Bad Request, 
Request Message: Method: POST, 
RequestUri: 'https://api.constantcontact.com/v2/activities/addcontacts?api_key=My_API_Key',
Content: System.Net.Http.MultipartFormDataContent, 
Headers:
{
  Authorization: Bearer My-Access-Token
  Accept: application/json
  Content-Type: multipart/form-data; boundary="ead4e036-5921-4a98-847b-12h4sgre4
69c"
  Content-Length: 556
}

EDIT

Thanks to Amy's suggestions in the comments below, I've used the Postman extension in Chrome to perform the Bulk Import directly from the browser. By doing so, I can now rule out file formatting as an possible cause of my error (and have removed those examples), and also know that I'm going about this in the right way. Therefore, something is not quite right with my code...any ideas from anyone?

One difference between the in-browser request that was successful and the Headers shown above is that the Content-Type: mulitpart/form-data; has the boundary info with it, whereas the info from the browser had that info on each part. Also, the code above doesn't show each part. Example of the Postman request preview:

Content-Type: multipart/form-data
Cache-Control: no-cache

----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="file_name"

ContactList.txt
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="lists"

Solution

  • Many thanks goes to Amy, whose help has been invaluable during this process!

    I found out that there is nothing necessarily wrong with most of the code I used above, with exception to these lines:

    data.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
    {
        FileName = listFile.Name
    };//I've tried with & without this header.
    

    This was something I was experimenting with at the time of asking, and it breaks the request. My main problem turned out to be that C#, when composing the Content-Disposition headers for the 3 parts I was uploading, does not add quotations around the name value. This, in turn, caused a 400 - Bad Request error to be returned. This is how the working code now looks:

    using (var content = new MultipartFormDataContent())
    {
        var fileNameContent = new StringContent(listFile.Name);
        content.Add(fileNameContent, "\"file_name\"");
    
        var listContent = new StringContent(contactListId);
        content.Add(listContent, "\"lists\"");
    
        var dataContent = new ByteArrayContent(File.ReadAllBytes(listFile.FullName));
        content.Add(dataContent, "\"data\"");
    
        result = client.PostAsync(FullApiAddress, content).Result;
    }
    

    Notice the double quotations around each name value; this simple change was all that I needed to make to get the request to work. I hope this is helpful to anyone in the future.