HTTP-сообщение как IE6 с использованием С#

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

Из документации обратная передача должна выглядеть как

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 с помощью С#? Спасибо

Мой код

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

Ответ 1

У меня 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);
}

Ответ 2

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

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

Ваш запрос:

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

Запрос IE6:

Upload.asp?b_customerId=[O/M1234]

Чтобы исправить эту проблему, вы можете создать новый Url из перегрузки конструктор класса Uri, который был помечен как устаревший, но все еще работает правильно. Эта перегрузка позволяет указать, что строка уже была экранирована во втором параметре.

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

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

:

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

2) Тег Content-disposition не отформатирован таким же образом в вашем запросе, как и в запросе 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 + 

Ответ 3

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