发出POST请求

时间:2018-10-14 19:01:19

标签: c#

我正在尝试发布到定义为以下内容的网络表单:

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

但是我无法克服这个错误:

  

-1 (xml文件名不包含名称空间定义的xml)

如何将这个xml数据发送到表单,或者有一个有效的表单-http://stuff.petrovsky.cz/subdom/stuff/RZP/rzp-test-form.php-如何调用和捕获xml数据?我想执行相同的请求并获取xml。

1 个答案:

答案 0 :(得分:4)

使用System.Net.Http,我可以使用MultipartFormDataContent构造表单请求作为概念证明

现在,当我最初对其进行测试时,我收到了403禁止响应,但我猜想这是我所期望的位置,并且端点可能是区域锁定的。

原始提琴手的反应

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

上面的代码生成了以下请求,我使用提琴手 (请密切注意工作格式)

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

当我选择使用System.Net.Http的异步API时,我发现了一个类似的问题

引用UploadFile with POST values by WebClient

使用WebClient完成的答案可以适应您的问题,从而可以构造与上面产生的请求类似的请求。

我也尝试测试那个,但是遇到了同样的禁止错误。现在知道正确的格式,您还应该能够使用WebClient/WebRequest