2012-01-04 2 views
11

Мне нужно сделать HTTP POST с помощью C#. Он должен сделать обратную передачу так же, как и страницу IE6.HTTP Сообщение как IE6 с использованием C#

С документацией постбэк должна выглядеть

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-- 

Я думаю, им проблемы с граничными символами. Я попытался установить границу в данных сообщения, а скрипач показывает что-то подобное, но я возвращаю страницу с ошибкой «Invalid procedure call or argument». Content-disposition находится в теле, а не в заголовке, чтобы держать его в пределах границ. Я не уверен, что это правильно. Правильно ли я устанавливаю границу? Может ли кто-нибудь дать некоторые рекомендации о том, как сделать HTTP POST в стиле IE6 с помощью C#? Благодаря

Мой код

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; 

Скрипач Output (сырье)

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-- 
+0

Первое, что приходит на ум, - это то, что ваше имя файла находится на новой строке, которой это не должно быть. Не знаю, приведет ли это к упомянутой ошибке, но, вероятно, это, по меньшей мере, будет путать вещи. – Chris

+0

Довольно уверен, что это сделает его недействительным. – kamranicus

+0

Пробовал это с новой строкой. Тот же результат –

ответ

3

У меня есть blogged о способе загрузки нескольких файлов с помощью WebClient и возможность отправки параметров также. Вот соответствующий код:

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

, а затем способ выполнения загрузки:

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

который может быть использован как так:

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); 
} 
+0

Это сработало. Учитывая вас щедрость. Для бонусных очков вы могли бы объяснить, почему вы работали, а мой не сделал? Я заметил, что вы использовали «application/octet-stream», где я использовал текст. Вы также не задаете длину содержимого. –

0

Это не будет полный ответ , но вы можете посмотреть на использование сокета вместо WebRequest и выполнить запрос HTTP самостоятельно. Похоже, что многопроцессорный обработчик на вашем сервере является несоответствующим и ожидает, что запрос будет таким же, как и IE6, поэтому эмуляция того, что вы сами, будет лучшим подходом.

4

Я думаю, что у вас есть две потенциальные проблемы:

1) URL-адрес, который вы отправляете форматы параметр b_CustomerId иначе, чем реализация IE6. Если сайт, на который вы нацеливаетесь, не ожидает HTML-кодированного значения, это может быть очень легко источником сообщения об ошибке.

Ваш запрос:

Upload.asp?b_customerId=%5BO/M1234%5D 

запрос IE6:

Upload.asp?b_customerId=[O/M1234] 

Для того, чтобы исправить эту проблему, вы можете создать новую страницу с перегрузкой Uri class constructor, который был помечен как устаревший , но все равно работает правильно. Эта перегрузка позволяет указать, что строка уже была экранирована во втором параметре.

Для того, чтобы использовать этот конструктор, измените эту строку:

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

к этому:

request = (HttpWebRequest)WebRequest.Create(new Uri(url, true)); 

2) Content-расположение тега не отформатирована так же, как в запросе, как это находится в запросе IE6.

Ваш запрос:

Content-disposition: form-data; name="File1"; 
filename="provideTest.xml" 

IE6 запрос:

Content-disposition: form-data; name=”File1”; filename=”noColonsSpacesOrAmpersandsInHere” 

Эту проблему можно решить, изменив эти две строки:

"Content-disposition: form-data; name=\"File1\";" + Environment.NewLine + 
"filename=\"provideTest.xml\"" + Environment.NewLine + 

в:

"Content-disposition: form-data; name=\"File1\"; " + 
"filename=\"provideTest.xml\"" + Environment.NewLine + 
+0

Как вы остановили кодирование [O/M1234]? Я не мог найти способ с классами, которые я использую. –

+0

@TomSquires: я обновил ответ с дополнительной информацией о том, как вы могли это сделать (я проверил через скрипач, что он передает строку без сохранения). –

+0

Странно, что дает ту же ошибку, несмотря на то, что дает результат точно так же, как указано в документации. Однако метод Дарвина работал. (несмотря на то, что он не похож на документацию). Спасибо за вашу помощь. –