function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
SimonHoldernessSimonHolderness 

HTTP 400 on POST request

I am trying to use a post request to start a batch upsert.  I have managed to get this to work using Curl, but I always get a 400 Bad Response when using the native HttpWebRequest method

 

I am passing the postData as UTF-8 encoded, and include the following for the contentType and headers

 

Content-Type: application/xml; charset=UTF-8
X-SFDC-Session: 00D700000008rXl!AQMAQA1rjn2jPYeSQuRoChquzvncucoqSNHVSUydo1eM0eSV3PKmSYE0QoD8gkSxBb2tuaT3wd8Dd1fr_YahMGhtxYO7m7u2 

 

Below is the code; when I look at the request that works with Curl and my request in tcpTrace, they look identical.

 

public static string PostData(string url, byte[] postData, string contentType, KeyValuePair<string, string>[] headers, ITaskProgress progress)

{

int length = postData.Length;

Uri uri = new Uri(url);

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

request.Method = "POST";request.ServicePoint.Expect100Continue =

false;

request.ContentType = contentType;

request.ContentLength = length;

request.ReadWriteTimeout = Timeout;

request.Timeout = Timeout;

request.AllowWriteStreamBuffering = false;

request.Accept = "application/xml";request.UserAgent =

"Salesforce Web Service Connector"; if (headers != null)

{

foreach (KeyValuePair<string, string> header in headers)

request.Headers.Add(header.Key, header.Value);

}

if (progress != null)

progress.CurrentTaskProgress = 0;

using (Stream writeStream = request.GetRequestStream())

{

int bytesSent = 0; while (bytesSent < length)

{

int bytesToSend = Math.Min(ChunkSize, length - bytesSent);

writeStream.Write(postData, bytesSent, bytesToSend);

bytesSent += bytesToSend;

if (progress != null)progress.CurrentTaskProgress = (int)((long)bytesSent * 100) / length;

}

}

if (progress != null)

progress.CurrentTaskProgress = 100;

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())

...

 

Here is the actual post sent

 

POST /services/async/18.0/job HTTP/1.1
Content-Type: application/xml; charset=UTF-8
Accept: application/xml
User-Agent: Salesforce Web Service Connector
X-SFDC-Session: 00D700000008rXl!AQMAQA1rjn2jPYeSQuRoChquzvncucoqSNHVSUydo1eM0eSV3PKmSYE0QoD8gkSxBb2tuaT3wd8Dd1fr_YahMGhtxYO7m7u2
Host: localhost:8080
Content-Length: 626
Connection: Keep-Alive

<?xml version="1.0" encoding="utf-8"?><jobInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.force.com/2009/06/asyncapi/dataload">  <operation>upsert</operation>  <object>Leads</object>  <contentType>CSV</contentType>  <numberBatchesQueued>0</numberBatchesQueued>  <numberBatchesInProgress>0</numberBatchesInProgress>  <numberBatchesCompleted>0</numberBatchesCompleted>  <numberBatchesFailed>0</numberBatchesFailed>  <numberBatchesTotal>0</numberBatchesTotal>  <numberRecordsProcessed>0</numberRecordsProcessed>  <numberRetries>0</numberRetries></jobInfo>

 

Very very strange

Best Answer chosen by Admin (Salesforce Developers) 
SimonHoldernessSimonHolderness

Hi Simon,

 

I did as you suggested and pulled the exact response coming back from salesforce using TCPTrace (having worked out that I can send to http as well as https, otherwise things get a bit mangled)

 

This is what I got back

 

HTTP/1.1 400 Bad Request
Via: 1.1 APTWARISA
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Transfer-Encoding: chunked
Date: Thu, 25 Mar 2010 08:45:21 GMT
Content-Type: application/xml
Server:

00de
<?xml version="1.0" encoding="UTF-8"?><error
   xmlns="http://www.force.com/2009/06/asyncapi/dataload">
 <exceptionCode>InvalidJob</exceptionCode>
 <exceptionMessage>Unable to find object: Leads</exceptionMessage>
</error>
0

 

So I am getting back exactly the same as CUrl, but dotnet is consuming the problem by throwing an exception on the 400 Bad Request

 

Thanks for the help on this,

 

SImon

All Answers

SuperfellSuperfell
Can you capture/post the entire response you get back ?
SuperfellSuperfell
SimonHoldernessSimonHolderness
Hi Simon, I did think that I would remove the "number" tags, but I defined them in my schema as ints, and so they come through. However, if I send the same request via Curl I get a valid response back, it is only when I use the native HttpWebRequest that I get the 400 bad response. Simon
SimonHoldernessSimonHolderness

Hi Simon,

 

I did as you suggested and pulled the exact response coming back from salesforce using TCPTrace (having worked out that I can send to http as well as https, otherwise things get a bit mangled)

 

This is what I got back

 

HTTP/1.1 400 Bad Request
Via: 1.1 APTWARISA
Connection: Keep-Alive
Proxy-Connection: Keep-Alive
Transfer-Encoding: chunked
Date: Thu, 25 Mar 2010 08:45:21 GMT
Content-Type: application/xml
Server:

00de
<?xml version="1.0" encoding="UTF-8"?><error
   xmlns="http://www.force.com/2009/06/asyncapi/dataload">
 <exceptionCode>InvalidJob</exceptionCode>
 <exceptionMessage>Unable to find object: Leads</exceptionMessage>
</error>
0

 

So I am getting back exactly the same as CUrl, but dotnet is consuming the problem by throwing an exception on the 400 Bad Request

 

Thanks for the help on this,

 

SImon

This was selected as the best answer
SimonHoldernessSimonHolderness

For the record, I had to catch the Exception, and then continue to read from response stream within the WebException class

 

try

{

using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())

{

using (Stream responseStream = response.GetResponseStream())

{

using (StreamReader readStream = new StreamReader(responseStream, Encoding.UTF8))

{

return readStream.ReadToEnd();

}

}

}

}

catch(WebException webException)

{

using (Stream responseStream = webException.Response.GetResponseStream())

{

using (StreamReader streamReader = new StreamReader(responseStream))

{

return streamReader.ReadToEnd();

}

}

}

SuperfellSuperfell
There's no Leads object, its called Lead, (and i'd suspect that's what your working curl version is doing).
talgiladitalgiladi

I didn't know you could do that!

Very nice...