Сделать POST-запрос

Я пытаюсь отправить сообщение в веб-форму, определенную как:

<form name="frmdata" method='post' enctype ='multipart/form-data' action ="http://www.rzp.cz/cgi-bin/aps_cacheWEB.sh"> 
<input type ="hidden" name ="VSS_SERV" value="ZVWSBJXML"> 
<input type="file" name="filename"> 
<input type ='submit' name ='x' value ='ODESLI'> 
</form>

Здесь есть дополнительная документация по форме:
http://www.rzp.cz/docs/RZP02_XML_28.pdf

Моя последняя попытка:

using (WebClient client = new WebClient())
{   
    NameValueCollection vals = new NameValueCollection();               
    vals.Add("VSS_SERV", "ZVWSBJXML");

    string filecontent = @"<?xml version=""1.0"" encoding=""ISO-8859-2""?>";
    filecontent = filecontent + @"
<VerejnyWebDotaz 
elementFormDefault=""qualified"" 
targetNamespace=""urn:cz:isvs:rzp:schemas:VerejnaCast:v1"" 
xmlns=""urn:cz:isvs:rzp:schemas:VerejnaCast:v1"" version=""2.8"">";

    filecontent = filecontent + @"
<Kriteria> 
<IdentifikacniCislo>03358437</IdentifikacniCislo> 
<PlatnostZaznamu>0</PlatnostZaznamu></Kriteria>";

    filecontent = filecontent + @"</VerejnyWebDotaz>";
    vals.Add("filename", filecontent);

    client.Headers["ContentType"] = "multipart/form-data"; 
    byte[] responseArray = client.UploadValues(@"http://www.rzp.cz/cgi-bin/aps_cacheWEB.sh", "POST", vals);
    string str = Encoding.ASCII.GetString(responseArray);
}

Но я не могу пройти мимо этой ошибки:

‹KodChyby>-1‹/KodChyby> (имя файла xml не содержит xml, определенного пространством имен)

Как я могу отправить эти xml данные в форму, а точнее есть рабочая форма - http://stuff.petrovsky.cz/subdom/stuff/RZP/rzp-test-form.php - как вызывать и перехватывать данные xml? Я хотел бы сделать тот же запрос и получить xml.


c#
person Deadsheep39    schedule 14.10.2018    source источник
comment
В ожидании правильного решения вы можете использовать Wireshark, чтобы увидеть, в чем разница между работой и вашим HTTP-соединением.   -  person Dialecticus    schedule 25.10.2018
comment
Я пробовал fiddler, но мне не хватает знаний о публикации форм в csharp, а не о недостатках в параметрах этой формы :).   -  person Deadsheep39    schedule 25.10.2018
comment
Каков ожидаемый результат?   -  person Karthik Ganesan    schedule 25.10.2018
comment
xml-документ (схема запроса и ответа в pdf-документе)   -  person Deadsheep39    schedule 25.10.2018


Ответы (1)


Используя System.Net.Http, я смог создать запрос формы в качестве доказательства концепции, используя MultipartFormDataContent

Первоначально, когда я тестировал его, я получил ответ 403 Forbidden, но я предположил, что этого следовало ожидать, учитывая мое местоположение и что конечная точка может быть заблокирована по региону.

Необработанный ответ Fiddler

HTTP/1.1 403 Forbidden
Date: Sat, 27 Oct 2018 01:37:09 GMT
Server: IIS
Content-Length: 225
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /cgi-bin/aps_cacheWEB.sh
on this server.</p>
</body></html>

Я был неправ, и запрет оказался ответом по умолчанию на плохие запросы, поскольку вы прокомментировали, что получили ту же запрещенную ошибку из региона. Итак, я вернулся к чертежной доске.

Затем я скопировал образец HTML-формы локально, а затем приступил к сравнению запросов из формы (которая действительно работала) и моего кода. Постепенно внося изменения, чтобы соответствовать, я, наконец, смог получить ответ 200 OK, но тело ответа было пустым.

По-видимому, возникла проблема с интерпретацией сервером границы в заголовке типа контента, если он заключен в кавычки boundary="...".

После дополнительных настроек он начал возвращать сообщение на основе сгенерированного содержимого.

HTTP/1.1 200 OK
Date: Sat, 27 Oct 2018 19:55:11 GMT
Server: IIS
Serial: 10.145
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/plain; charset=ISO-8859-2
Content-Length: 169

Multiple definitions of VSS_SERV encountered in input.
        If you're trying to do this intentionally (such as with select),
        the variable must have a "List" suffix.

Получается, что XML API ожидает, что запрос будет в очень конкретном формате. Отклонитесь от этого, и запрос не будет выполнен.

MultipartFormDataContent неправильно генерировал запрос, и это приводило к тому, что сервер вел себя не так, как ожидалось. Другие заголовки помещались перед заголовками Content-Disposition частей, и параметры Content-Disposition также не заключались в кавычки. Таким образом, не включая тип содержимого в части и убедившись, что заголовки содержимого были сгенерированы правильно, в конечном итоге проблема была устранена.

Важно отметить порядок добавления заголовков к содержимому, чтобы заголовок Content-Disposition устанавливался первым для каждой части.

Рабочий код, который генерирует запрос в нужном формате и получает XML-данные.

[Test]
public async Task Post_Form() {
    //Arrange
    var stream = getXml();
    var fileContent = new StreamContent(stream);
    fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
        Name = @"""filename""",
        FileName = @"""req-details.xml""",
    };
    fileContent.Headers.ContentType = new MediaTypeHeaderValue("text/xml");
    
    var stringContent = new ByteArrayContent(Encoding.UTF8.GetBytes("ZVWSBJXML"));
    stringContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") {
        Name = @"""VSS_SERV""",
    };
    //could have let system generate it but wanteed to rule it out as a problem
    var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", NumberFormatInfo.InvariantInfo);
    var form = new MultipartFormDataContent(boundary);
    //FIX: boundary quote issue
    var contentType = form.Headers.ContentType.Parameters.First(o => o.Name == "boundary");
    contentType.Value = contentType.Value.Replace("\"", String.Empty);
    form.Add(stringContent);
    form.Add(fileContent);

    //var data = await form.ReadAsStringAsync(); //FOR TESTING PORPOSES ONLY!!

    var client = createHttpClient("http://www.rzp.cz/");

    //Act
    var response = await client.PostAsync("cgi-bin/aps_cacheWEB.sh", form);
    var body = await response.Content.ReadAsStringAsync();

    //Assert
    response.IsSuccessStatusCode.Should().BeTrue();
    body.Should().NotBeEmpty();
    var document = XDocument.Parse(body); //should be valid XML
    document.Should().NotBeNull();
}

Приведенный выше код сгенерировал следующий запрос, который я извлек с помощью fiddler (обратите внимание на рабочий формат)

POST http://www.rzp.cz/cgi-bin/aps_cacheWEB.sh HTTP/1.1
User-Agent: System.Net.Http.HttpClient
Accept-Language: en-US, en; q=0.9
Accept: text/xml, application/xml
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=---------------------------8d63c301f3e044f
Host: www.rzp.cz
Content-Length: 574
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

-----------------------------8d63c301f3e044f
Content-Disposition: form-data; name="VSS_SERV"

ZVWSBJXML
-----------------------------8d63c301f3e044f
Content-Disposition: form-data; name="filename"; filename="req-details.xml"
Content-Type: text/xml

<?xml version="1.0" encoding="iso-8859-2"?>
<VerejnyWebDotaz xmlns="urn:cz:isvs:rzp:schemas:VerejnaCast:v1" version="2.8">
  <Kriteria>
    <IdentifikacniCislo>75848899</IdentifikacniCislo>
    <PlatnostZaznamu>0</PlatnostZaznamu>
  </Kriteria>
</VerejnyWebDotaz>
-----------------------------8d63c301f3e044f--

На что удалось получить следующий ответ.

HTTP/1.1 200 OK
Date: Sat, 27 Oct 2018 21:17:50 GMT
Server: IIS
Serial: 10.145
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/xml;charset=ISO-8859-2
Content-Length: 931

<?xml version='1.0' encoding='iso-8859-2'?>

<VerejnyWebOdpoved xmlns="urn:cz:isvs:rzp:schemas:VerejnaCast:v1" version="2.8">
<Datum>27.10.2018</Datum>

<Kriteria>

<IdentifikacniCislo>75848899</IdentifikacniCislo>

<PlatnostZaznamu>0</PlatnostZaznamu>

</Kriteria>

<PodnikatelSeznam>
<PodnikatelID>212fbf8314e01506b0d7</PodnikatelID>
<ObchodniJmenoSeznam Popis="Jméno a příjmení:">Filip Zrůst</ObchodniJmenoSeznam>

<IdentifikacniCisloSeznam Popis="Identifikační číslo osoby:">75848899</IdentifikacniCisloSeznam>

<TypPodnikatele Popis="Typ podnikatele:">Fyzická osoba</TypPodnikatele>
<AdresaPodnikaniSeznam Popis="Adresa sídla:">Vlašská 358/7, 118 00,  Praha 1 - Malá Strana</AdresaPodnikaniSeznam>

<RoleSubjektu Popis="Role subjektu:">podnikatel</RoleSubjektu>

<EvidujiciUrad Popis="Úřad příslušný podle §71 odst.2 živnostenského zákona:">Úřad městské části Praha 1</EvidujiciUrad>
</PodnikatelSeznam>

</VerejnyWebOdpoved>

Оттуда будет несложно проанализировать полученный XML по мере необходимости.

Вспомогательный код

Сгенерируйте или загрузите поток XML для формы

private static Stream getXml() {
    var xml = @"<?xml version=""1.0"" encoding=""ISO-8859-2""?>
<VerejnyWebDotaz 
xmlns=""urn:cz:isvs:rzp:schemas:VerejnaCast:v1"" 
version=""2.8"">
<Kriteria> 
    <IdentifikacniCislo>75848899</IdentifikacniCislo> 
    <PlatnostZaznamu>0</PlatnostZaznamu>
</Kriteria>
</VerejnyWebDotaz>";
    var doc = XDocument.Parse(xml);//basically to validate XML
    var stream = new MemoryStream();
    doc.Save(stream);
    stream.Position = 0;
    return stream;
}

Постепенно смог сократить заголовки, необходимые для успешного запроса, после того, как нашел совпадение, которое сработало. Попробуйте удалять другие постепенно, чтобы проверить, можно ли безопасно удалить больше, чтобы уменьшить количество необходимого ненужного кода.

private static HttpClient createHttpClient(string baseAddress) {
    var handler = createHandler();
    var client = new HttpClient(handler);
    client.BaseAddress = new Uri(baseAddress);
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "System.Net.Http.HttpClient");
    client.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Language", "en-US,en;q=0.9");
    client.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/xml,application/xml");
    client.DefaultRequestHeaders.ExpectContinue = false;
    client.DefaultRequestHeaders.ConnectionClose = false;
    client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue() {
        MaxAge = TimeSpan.FromSeconds(0)
    };
    return client;
}

private static HttpClientHandler createHandler() {
    var handler = new HttpClientHandler();
    // if the framework supports automatic decompression set automatic decompression
    if (handler.SupportsAutomaticDecompression) {
        handler.AutomaticDecompression = System.Net.DecompressionMethods.GZip |
            System.Net.DecompressionMethods.Deflate;
    }
    return handler;
}

Хотя я решил использовать асинхронный API System.Net.Http, я обнаружил аналогичный вопрос.

Ссылка UploadFile со значениями POST с помощью WebClient

С ответом, который был сделан с использованием WebClient, который можно было бы адаптировать к вашему вопросу, чтобы запрос мог быть построен аналогично тому, что было создано выше.

Я тоже пытался протестировать это, но получил ту же запрещенную ошибку. Теперь, когда известен правильный формат, вы также сможете правильно создать рабочий запрос, используя WebClient/WebRequest.

person Nkosi    schedule 26.10.2018