using HttpWebRequest upload file (multipart/form-data) - c#

I was trying to upload file to web server through API but i got error
"The remote server returned an error: (403) Forbidden." I also try upload file via postman and it was successful.Below my code uploading file and postman preview.
public static void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc)
{
MessageBox.Show(string.Format("Uploading {0} to {1}", file, url));
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
var boundarybytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
var wr = (HttpWebRequest)WebRequest.Create(url);
wr.ContentType = "multipart/form-data; boundary=" + boundary;
wr.Method = "POST";
wr.KeepAlive = true;
wr.Credentials = CredentialCache.DefaultCredentials;
var rs = wr.GetRequestStream();
const string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
foreach (string key in nvc.Keys)
{
rs.Write(boundarybytes, 0, boundarybytes.Length);
var formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = Encoding.UTF8.GetBytes(formitem);
rs.Write(formitembytes, 0, formitembytes.Length);
}
rs.Write(boundarybytes, 0, boundarybytes.Length);
const string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
var header = string.Format(headerTemplate, paramName, file, contentType);
var headerbytes = Encoding.UTF8.GetBytes(header);
rs.Write(headerbytes, 0, headerbytes.Length);
var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
var buffer = new byte[4096];
var bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
rs.Write(buffer, 0, bytesRead);
}
fileStream.Close();
var trailer = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
rs.Write(trailer, 0, trailer.Length);
rs.Close();
WebResponse wresp = null;
try
{
wresp = wr.GetResponse();
var stream2 = wresp.GetResponseStream();
var reader2 = new StreamReader(stream2);
MessageBox.Show(string.Format("Response is: {0}", reader2.ReadToEnd()));
}
catch (Exception ex)
{
MessageBox.Show("Error uploading file" + ex);
if (wresp != null)
{
wresp.Close();
wresp = null;
}
}
finally
{
wr = null;
}
}
this is postman preview
POST / HTTP/1.1
Host: buckname.s3.amazonaws.com
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="key"
2014/6b9830b098c9871c6356a5e55af91bfd/${filename}
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="AWSAccessKeyId"
AKIAJNXK6LUBUSZBXJIQ
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="acl"
public-read
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="policy"
eyJleHBpcmF0aW9uIjoiMjAxNC0wOC0yMlQxMDo1MTow
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="signature"
ezYsOF/P6bGcOqQdyJeE7iApu2A=
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="success_action_status"
201
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="secure"
true
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="x-amz-storage-class"
REDUCED_REDUNDANCY
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="file"; filename="exp.png"
Content-Type: image/png
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="Content-Type"
image/jpeg
----WebKitFormBoundaryE19zNvXGzXaLvS5C
edit (code sending)
var nvc = new NameValueCollection
{
{"AWSAccessKeyId", fields.AWSAccessKeyId},
{"Content-Type", "image/jpeg"},
{"acl", fields.acl},
{"key", fields.key},
{"policy", fields.policy},
{"secure", secure.ToString()},
{"signature", signature},
{"success_action_status", fields.success_action_status},
{"x-amz-storage-class", "REDUCED_REDUNDANCY"}
};
HttpUploadHelper.HttpUploadFile(responsFieldsDeSerial.url.ToString(),#"C:\1.png", "file", "image/jpeg",nvc);
thank you!

My error was
<?xml version="1.0" encoding="UTF-8"?> <Error><Code>AccessDenied</Code><Message>Invalid according to Policy: Policy Condition failed: ["eq", "$Secure", "true"]</Message>
Problem was value of field (in my case : secure) case senstive i tried to post True but it must be true.

Related

Unable to upload file using HTTP POST

I'm trying to upload a file using HTTP Post but somehow there is no file to be found when I process the request on the server side. I was able to create a similar request and successfully upload file using Chrome's Postman extension, but somehow can't do the same programmatically.
Client code:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fullUrl);
request.Method = "POST";
using (Stream requestStream = request.GetRequestStream())
{
string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("--" + boundary + "\r\n");
byte[] trailer = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type:{2}\r\n\r\n";
string header = string.Format(headerTemplate, "Files", "myFile.xml", "text/xml");
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
requestStream.Write(boundarybytes, 0, boundarybytes.Length);
requestStream.Write(headerbytes, 0, headerbytes.Length);
requestStream.Write(uploadedFile, 0, uploadedFile.Length);
requestStream.Write(trailer, 0, trailer.Length);
}
The request looks like this (in Fiddler) :
POST https://host/myUrl
Content-Length: 1067
Expect: 100-continue
Connection: Keep-Alive
------------8d2942f79ab208e
Content-Disposition: form-data; name="Files"; filename="myFile.xml"
Content-Type:text/xml
<myFile>
Something
</myFile>
------------8d2942f79ab208e
Server side:
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count != 1)
return BadRequest("Didn't get the file.");
But I always get httpRequest.Files.Count to be zero. Why?
The following request (created using Postman) gives me httpRequest.Files.Count to be one, as expected.
POST myUrl HTTP/1.1
Host: host
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="Files"; filename="myFile.xml"
Content-Type: text/xml
----WebKitFormBoundaryE19zNvXGzXaLvS5C
What am I doing wrong?
Figured it out. Thanks to this blog
Made two changes:
1) Added ContentType :
request.ContentType = "multipart/form-data; boundary=" + boundary;
2) Modified how the boundary ends
byte[] trailer = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "--");
And it works now. Hope this helps someone.
Perhaps you need to set the request's content type to "multipart/form-data"
request.ContentType = "multipart/form-data";

Inspect received raw HTTP message

I'm developing an Android application and sending a multipart-form-data HTTP POST to my IIS server. I need to see the raw HTTP message that my server is receiving. For this, in visual studio 2013 I put an interrupt just after this line
public String Report([Bind(Include = "lasfotos,comentario,asunto")] HttpPostedFileBase lasfotos, string comentario, string asunto)
In local variables i can see a lot of information inside this>base>request about the http message like path, content lenght, etc but i can't find where the raw HTTP is.
edit: Why do i think that see the raw http is a solution? because i can easily find my problem here i think. But i will show you my base problem:
I have this function in java that send's the data to the server. i can see my variable values (in the server) of "comentario" and "asunto" but "lasfotos" = null
public static String Postear(ArrayList<File> files, String asunto, String detalle)
{
String respuesta;
try
{
String boundary = "qu1ckr3port_myb0undy";
String filesboundary = "boundy_4_files";
URL url = new URL("http://192.168.10.100/QuickReport/Uploads/Report");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.writeBytes("--"+boundary+"\r\n" +
"Content-Disposition: form-data; name=\"lasfotos\"\r\n" +
"Content-Type: multipart/mixed; boundary="+filesboundary+"\r\n\r\n");
for (byte i = 0; i < files.size(); i++)
{
outputStream.writeBytes("--"+filesboundary+"\r\n" +
"Content-Disposition: file; filename=\"" + files.get(i).getName() + "\"\r\n" +
"Content-Type: image/jpeg\r\n\r\n");
FileInputStream fileInputStream = new FileInputStream(files.get(i));
int bytesAvailable = fileInputStream.available();
int bufferSize = Math.min(bytesAvailable, 1024 * 1024);
byte[] buffer = new byte[bufferSize];
int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0)
{
outputStream.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, 1024 * 1024);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
fileInputStream.close();
outputStream.writeBytes("\r\n");
}
outputStream.writeBytes("--"+filesboundary+"--\r\n" +
"--"+boundary+"\r\n" +
"Content-Disposition: form-data; name=\"comentario\"\r\n\r\n" +
detalle + "\r\n" +
"--"+boundary+"\r\n");
outputStream.writeBytes("\r\n" +
"--"+boundary+"\r\n" +
"Content-Disposition: form-data; name=\"asunto\"\r\n\r\n" +
asunto + "\r\n" +
"--"+boundary+"--\r\n");
respuesta = "";
String linea;
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
while ((linea = reader.readLine()) != null) respuesta += linea;
reader.close();
outputStream.flush();
outputStream.close();
} catch (Exception e)
{
respuesta = "error";
e.printStackTrace();
}
return respuesta;
}
the problem was bad documentation i think. i followed the format documented in this page http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="submit-name"
Larry
--AaB03x
Content-Disposition: form-data; name="files"
Content-Type: multipart/mixed; boundary=BbC04y
--BbC04y
Content-Disposition: file; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--BbC04y
Content-Disposition: file; filename="file2.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary
...contents of file2.gif...
--BbC04y--
--AaB03x--
and that was the problem. i just changed my code and now is working perfectly. here is my new code:
public static String Postear(ArrayList<File> files, String asunto, String detalle)
{
String respuesta;
try
{
String boundary = "qu1ckr3port_myb0undy";
URL url = new URL("http://192.168.10.100/QuickReport/Uploads/Report");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
for (byte i = 0; i < files.size(); i++)
{
outputStream.writeBytes("--"+boundary+"\r\n" +
"Content-Disposition: form-data; name=\"lasfotos\" filename=\"" + files.get(i).getName() + "\"\r\n" +
"Content-Type: image/jpeg\r\n\r\n");
FileInputStream fileInputStream = new FileInputStream(files.get(i));
int bytesAvailable = fileInputStream.available();
int bufferSize = Math.min(bytesAvailable, 1024 * 1024);
byte[] buffer = new byte[bufferSize];
int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0)
{
outputStream.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, 1024 * 1024);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
fileInputStream.close();
outputStream.writeBytes("\r\n");
}
outputStream.writeBytes("--"+boundary+"\r\n" +
"Content-Disposition: form-data; name=\"comentario\"\r\n\r\n" +
detalle + "\r\n");
outputStream.writeBytes("\r\n" +
"--"+boundary+"\r\n" +
"Content-Disposition: form-data; name=\"asunto\"\r\n\r\n" +
asunto + "\r\n" +
"--"+boundary+"--\r\n");
respuesta = "";
String linea;
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
while ((linea = reader.readLine()) != null) respuesta += linea;
reader.close();
outputStream.flush();
outputStream.close();
} catch (Exception e)
{
respuesta = "error";
e.printStackTrace();
}
return respuesta;
}
hope this be usefull for someone...

C# CF Post Request | Internal server error 500

I want to keep it as simple as possible -
-We switched from an Godaddy windows hosting to Godaddy linux hosting
-After switching i get internal server error 500 when iam using POST from an c#
program(compact framework) with the content type - multipartformdata,
However i can still use a normal html form that sits on the server to post with no issues.
-Everything worked absolutly fine before the switch! I can still do GET requests,
and even normal(Not multipart/form-data) POST request from my C# app .
Headers and content from the request / response using the code below :
Request headers -
POST /users/test.php HTTP/1.1
Content-Type: multipart/form-data; boundary=----------------------------8d0cb969d25c418
Host: mydomain(i censor)
Content-Length: 403
Expect: 100-continue
Connection: Keep-Alive
Request content -
------------------------------8d0cb969d25c418
Content-Disposition: form-data; name="title";
test
------------------------------8d0cb969d25c418
Content-Disposition: form-data; name="pwd";
testpwd
------------------------------8d0cb969d25c418
Content-Disposition: form-data; name="file"; filename=test.txt
Content-Type: text/html
testfile
------------------------------8d0cb969d25c418
Response headers -
HTTP/1.1 500 Internal Server Error
Date: Fri, 20 Dec 2013 10:32:01 GMT
Server: Apache mod_fcgid/2.3.10-dev
Content-Length: 662
Keep-Alive: timeout=5
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
This is my C# Code(Note - I must use compact framework only) :
openFileDialog1.ShowDialog();
NameValueCollection formsdata = new NameValueCollection();
formsdata.Add("title", textBox3.Text);
formsdata.Add("pwd", textBox4.Text);
string[] names = new string[1] { openFileDialog1.FileName };
UploadFilesToRemoteUrl("http://mydomain/users/writefile.php", names, "notused", formsdata, openFileDialog1.SafeFileName);
and for the method UploadFilesToRemoteUrl -
public static void UploadFilesToRemoteUrl(string url, string[] files, string
logpath, NameValueCollection nvc, string filename)
{
long length = 0;
string boundary = "----------------------------" +
DateTime.Now.Ticks.ToString("x");
HttpWebRequest httpWebRequest2 = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest2.ContentType = "multipart/form-data; boundary=" +
boundary;
httpWebRequest2.Method = "POST";
httpWebRequest2.KeepAlive = true;
httpWebRequest2.Credentials =
System.Net.CredentialCache.DefaultCredentials;
Stream memStream = new System.IO.MemoryStream();
byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" +
boundary + "\r\n");
string formdataTemplate = "\r\n--" + boundary +
"\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";
foreach (string key in nvc.Keys)
{
string formitem = string.Format(formdataTemplate, key, nvc[key]);
byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
memStream.Write(formitembytes, 0, formitembytes.Length);
}
memStream.Write(boundarybytes, 0, boundarybytes.Length);
string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=" + filename + "\r\n Content-Type: text/html\r\n\r\n";
for (int i = 0; i < files.Length; i++)
{
//string header = string.Format(headerTemplate, "file" + i, files[i]);
string header = string.Format(headerTemplate, "file", files[i]);
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
memStream.Write(headerbytes, 0, headerbytes.Length);
FileStream fileStream = new FileStream(files[i], FileMode.Open,
FileAccess.Read);
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
{
memStream.Write(buffer, 0, bytesRead);
}
memStream.Write(boundarybytes, 0, boundarybytes.Length);
fileStream.Close();
}
httpWebRequest2.ContentLength = memStream.Length;
Stream requestStream = httpWebRequest2.GetRequestStream();
memStream.Position = 0;
byte[] tempBuffer = new byte[memStream.Length];
memStream.Read(tempBuffer, 0, tempBuffer.Length);
memStream.Close();
requestStream.Write(tempBuffer, 0, tempBuffer.Length);
requestStream.Close();
WebResponse webResponse2 = httpWebRequest2.GetResponse();
Stream stream2 = webResponse2.GetResponseStream();
StreamReader reader2 = new StreamReader(stream2);
MessageBox.Show(reader2.ReadToEnd());
webResponse2.Close();
httpWebRequest2 = null;
webResponse2 = null;
}
Any help is really highly appreciated!

Login to website with multipart/form-data

I have been trying for awhile now to log into a website using a WebRequest with no luck and I'm hoping that somebody can help me out. The login page uses "multipart/form-data; boundary" for its post data which I have never encountered before and I haven't been able to get it to work.
A successful post looks like this (courtesy of Tamper):
-----------------------------13327156328034
Content-Disposition: form-data; name="cc_session_id"
0vtgfe4bbhlh94f4vmmptctr06
-----------------------------13327156328034
Content-Disposition: form-data; name="cc_action"
cca_login
-----------------------------13327156328034
Content-Disposition: form-data; name="cc_failure_url"
https://www.fanduel.com/p/LoginPp
-----------------------------13327156328034
Content-Disposition: form-data; name="cc_success_url"
https://www.fanduel.com/
-----------------------------13327156328034
Content-Disposition: form-data; name="email"
*********
-----------------------------13327156328034
Content-Disposition: form-data; name="password"
*********
-----------------------------13327156328034
Content-Disposition: form-data; name="login"
Log in
-----------------------------13327156328034--
My connection method looks like this:
private static WebResponse Connect()
{
var manager = new SessionIDManager();
var boundary = "---------------------------" + DateTime.Now.Ticks;
var newLine = Environment.NewLine;
var propFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" + newLine + newLine +
"{1}" + newLine;
var req = WebRequest.Create("https://www.fanduel.com/c/CCAuth");
var session = manager.CreateSessionID(HttpContext.Current);
req.Method = "POST";
req.ContentType = "multipart/form-data; boundary=" + boundary;
string formParams = string.Format(propFormat, "cc_session_id", session);
formParams += string.Format(propFormat, "cc_action", "cca_login");
formParams += string.Format(propFormat, "cc_failure_url", "https://www.fanduel.com/p/LoginPp");
formParams += string.Format(propFormat, "cc_success_url", "https://www.fanduel.com/");
formParams += string.Format(propFormat, "email", Credentials.ToInsecureString(Credentials.DecryptString(Settings.FanDuel.UserName)));
formParams += string.Format(propFormat, "password", Credentials.ToInsecureString(Credentials.DecryptString(Settings.FanDuel.Password)));
formParams += string.Format(propFormat, "login", "Log in");
formParams += "--" + boundary + "--";
byte[] bytes = Encoding.ASCII.GetBytes(formParams);
req.ContentLength = bytes.Length;
using (Stream os = req.GetRequestStream())
{
os.Write(bytes, 0, bytes.Length);
}
return req.GetResponse();
}
It produces the following post data:
-----------------------------635209175732301763
Content-Disposition: form-data; name="cc_session_id"
lptqmgshh2givmblbna4yeql
-----------------------------635209175732301763
Content-Disposition: form-data; name="cc_action"
cca_login
-----------------------------635209175732301763
Content-Disposition: form-data; name="cc_failure_url"
https://www.fanduel.com/p/LoginPp
-----------------------------635209175732301763
Content-Disposition: form-data; name="cc_success_url"
https://www.fanduel.com/
-----------------------------635209175732301763
Content-Disposition: form-data; name="email"
********
-----------------------------635209175732301763
Content-Disposition: form-data; name="password"
********
-----------------------------635209175732301763
Content-Disposition: form-data; name="login"
Log in
-----------------------------635209175732301763--
Unfortunately, the response's cookie isn't that of a successful login. At this point I don't know what else to do and I would appreciate any help anybody would be willing to offer me to get this working. Thanks.
Update: I Tried getting an initial response and then using that session id for my post, but still no luck. Does anybody have any ideas?
I've managed to get this working and optimized the method to the best of my ability.
private CookieContainer cookies = new CookieContainer();
private string loginCookie;
public bool IsLoggedIn { get; set; }
public void Login()
{
var boundary = "---------------------------" + DateTime.Now.Ticks;
var newLine = Environment.NewLine;
var propFormat = "--" + boundary + newLine +
"Content-Disposition: form-data; name=\"{0}\"" +
newLine + newLine + "{1}" + newLine;
var req = WebRequest.Create("https://www.fanduel.com/c/CCAuth") as HttpWebRequest;
req.CookieContainer = cookies;
req.Method = "POST";
req.ContentType = "multipart/form-data; boundary=" + boundary;
string formParams = string.Format(propFormat, "cc_session_id", new SessionIDManager().CreateSessionID(HttpContext.Current));
formParams += string.Format(propFormat, "cc_action", "cca_login");
formParams += string.Format(propFormat, "cc_failure_url", "https://www.fanduel.com/p/LoginPp");
formParams += string.Format(propFormat, "cc_success_url", "https://www.fanduel.com/");
formParams += string.Format(propFormat, "email", Credentials.ToInsecureString(Credentials.DecryptString(FanDuel.Default.UserName)));
formParams += string.Format(propFormat, "password", Credentials.ToInsecureString(Credentials.DecryptString(FanDuel.Default.Password)));
formParams += string.Format(propFormat, "login", "Log in");
formParams += "--" + boundary + "--";
byte[] bytes = Encoding.ASCII.GetBytes(formParams);
req.ContentLength = bytes.Length;
using (Stream os = req.GetRequestStream())
{
os.Write(bytes, 0, bytes.Length);
}
var res = req.GetResponse();
res.Close();
loginCookie = req.Headers["Cookie"];
IsLoggedIn = res.ResponseUri == new Uri("https://www.fanduel.com/p/Home");
}
Then pass loginCookie to any other relevant WebRequest:
void SomeMethod()
{
var req = WebRequest.Create("SomeUri");
req.Headers.Add("Cookie", loginCookie);
var res = req.GetResponse();
}
I hope this helps somebody in the future.

HTTP Post as IE6 using C#

I need to do a HTTP POST using C#. It needs to do a postback the same way as an IE6 page.
From the documentation the postback should look like
POST /.../Upload.asp?b_customerId=[O/M1234] HTTP/1.1
Content-length: 12345
Content-type: multipart/form-data; boundary=vxvxv
Host: www.foo.com
--vxvxv
Content-disposition: form-data; name=”File1”; filename=”noColonsSpacesOrAmpersandsInHere”
Content-type: text/xml
<?xml version=”1.0” encoding=”UTF-8”?>
...
<bat:Batch ...
.......
</bat:Batch>
--vxvxv--
I think im having trouble with the boundary characters. I tried setting the boundary in the post data and fiddler shows something similar but I get a page back with the error "Invalid procedure call or argument". the Content-disposition is in the body rather than the header to keep it within the boundaries. Im not sure that is right. Am I setting the boundary the correct way? Can anyone give some guidance on how to do an IE6 style HTTP POST using C# ? Thanks
My Code
data = "--vxvxv" + Environment.NewLine +
"Content-disposition: form-data; name=\"File1\";" + Environment.NewLine +
"filename=\"provideTest.xml\"" + Environment.NewLine +
"Content-type: text/xml" + Environment.NewLine +
#"<?xml version=""1.0"" encoding=""UTF-8""?>" + Environment.NewLine +
data + Environment.NewLine +
"--vxvxv--";
var encoding = ASCIIEncoding.UTF8;
HttpWebRequest request;
var postData = encoding.GetBytes(data);
request = (HttpWebRequest)WebRequest.Create(url);
request.ContentLength = postData.Length;
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=vxvxv";
request.Host = "www.foo.com";
request.ContentLength = postData.Length;
X509Certificate2Collection certCollect = new X509Certificate2Collection();
X509Certificate2 cert = new X509Certificate2(#"C:\a\cert.pfx", "password");
certCollect.Add(cert);
request.ClientCertificates = certCollect;
using (Stream writeStream = request.GetRequestStream()) {
writeStream.Write(postData, 0, postData.Length); }
WebResponse webResponse = request.GetResponse();
string output = new StreamReader(webResponse.GetResponseStream()).ReadToEnd();
LogEntry.Write("Recieved : " + output);
return output;
Fiddler Output (raw)
POST https://../Upload.asp?b_customerId=%5BO/M1234%5D HTTP/1.1
Content-Type: multipart/form-data; boundary=vxvxv
Host: www.foo.com
Content-Length: 5500
Expect: 100-continue
Connection: Keep-Alive
--vxvxv
Content-disposition: form-data; name="File1";
filename="provideTest.xml"
Content-type: text/xml
<?xml version="1.0" encoding="UTF-8"?>
...SNIP...
</bat:Batch>
--vxvxv--
I think that you have two potential problems:
1) The URL that you are sending formats the b_CustomerId parameter differently than the IE6 implementation. If the site you are targeting does not expect an HTML-encoded value, this could very easily be the source of the error message.
Your request:
Upload.asp?b_customerId=%5BO/M1234%5D
The IE6 request:
Upload.asp?b_customerId=[O/M1234]
In order to fix this issue, you can create a new Url from an overload of the Uri class constructor that has been marked as obsolete, but still works correctly. This overload allows you to specify that the string has already been escaped in the second parameter.
In order to use this constructor, change this line:
request = (HttpWebRequest)WebRequest.Create(url);
to this:
request = (HttpWebRequest)WebRequest.Create(new Uri(url, true));
2) The Content-disposition tag is not formatted the same way in your request as it is in the IE6 request.
Your request:
Content-disposition: form-data; name="File1";
filename="provideTest.xml"
IE6 request:
Content-disposition: form-data; name=”File1”; filename=”noColonsSpacesOrAmpersandsInHere”
This can be resolved by changing these two lines:
"Content-disposition: form-data; name=\"File1\";" + Environment.NewLine +
"filename=\"provideTest.xml\"" + Environment.NewLine +
to:
"Content-disposition: form-data; name=\"File1\"; " +
"filename=\"provideTest.xml\"" + Environment.NewLine +
I have blogged about a way of uploading multiple files using a WebClient and the possibility to send parameters as well. Here's the relevant code:
public class UploadFile
{
public UploadFile()
{
ContentType = "application/octet-stream";
}
public string Name { get; set; }
public string Filename { get; set; }
public string ContentType { get; set; }
public Stream Stream { get; set; }
}
and then a method to perform the upload:
public byte[] UploadFiles(string address, IEnumerable<UploadFile> files, NameValueCollection values)
{
var request = WebRequest.Create(address);
request.Method = "POST";
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
request.ContentType = "multipart/form-data; boundary=" + boundary;
boundary = "--" + boundary;
using (var requestStream = request.GetRequestStream())
{
// Write the values
foreach (string name in values.Keys)
{
var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.ASCII.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"{1}{1}", name, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(values[name] + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
}
// Write the files
foreach (var file in files)
{
var buffer = Encoding.ASCII.GetBytes(boundary + Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}", file.Name, file.Filename, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
buffer = Encoding.ASCII.GetBytes(string.Format("Content-Type: {0}{1}{1}", file.ContentType, Environment.NewLine));
requestStream.Write(buffer, 0, buffer.Length);
file.Stream.CopyTo(requestStream);
buffer = Encoding.ASCII.GetBytes(Environment.NewLine);
requestStream.Write(buffer, 0, buffer.Length);
}
var boundaryBuffer = Encoding.ASCII.GetBytes(boundary + "--");
requestStream.Write(boundaryBuffer, 0, boundaryBuffer.Length);
}
using (var response = request.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var stream = new MemoryStream())
{
responseStream.CopyTo(stream);
return stream.ToArray();
}
}
which could be used like so:
using (var stream1 = File.Open("test.txt", FileMode.Open))
using (var stream2 = File.Open("test.xml", FileMode.Open))
using (var stream3 = File.Open("test.pdf", FileMode.Open))
{
var files = new[]
{
new UploadFile
{
Name = "file",
Filename = "test.txt",
ContentType = "text/plain",
Stream = stream1
},
new UploadFile
{
Name = "file",
Filename = "test.xml",
ContentType = "text/xml",
Stream = stream2
},
new UploadFile
{
Name = "file",
Filename = "test.pdf",
ContentType = "application/pdf",
Stream = stream3
}
};
var values = new NameValueCollection
{
{ "key1", "value1" },
{ "key2", "value2" },
{ "key3", "value3" },
};
byte[] result = UploadFiles("http://localhost:1234/upload", files, values);
}
This won't be a complete answer, but you could look at using a socket instead of WebRequest and performing the HTTP request yourself. It seems that the multipart handler on your server is non-conformi g and expects the request to be the same as IE6 would make, so emulating that yourself would be the best approach.

Categories

Resources