无法使用 WebRequest 中的 BinaryReader 正确读取数据

Can't read data correctly with BinaryReader from WebRequest

嗨!

我正在开发 C# SOAP 服务来与我们的一项政府服务进行通信。他们也使用 SOAP 进行通信,但现在他们引入了两个新的端点,它们处理请求的方式非常不同。我注意到的第一件事是,当我从客户端应用程序的 PHP 代码调用 $SoapClient->Request(...) 时,它因错误 Looks like we got no XML document 而失败。获得实际响应的唯一方法是尝试捕获 SoapFault 然后调用 $response = $SoapClient->__getLastResponse() 来读取它。它确实不是 xml,而是二进制格式,有点像电子邮件附件的来源。

想通了,下载了PDF文件,还是乱码。首先我认为 PHP 搞砸了一些东西,但后来我查看了 WebService 的日志并且它已经是格式错误的。我用 StreamReader 尝试了许多不同的编码,但没有成功。然后我将它更改为 BinaryReader 这两个端点。它现在更接近我正在寻找的响应,但仍然包含看似随机的垃圾字符,如屏幕截图所示:

左边是我要找的,右边是我得到的。

我的代码:

string soapResult;
try
{
    using (HttpWebResponse webResponse = (HttpWebResponse)httpRequest.EndGetResponse(asyncResult))
    {
        if (request == "edoc/getDocument" || request == "edoc/downloadFile")
        {
            using (BinaryReader rd = new BinaryReader(webResponse.GetResponseStream()))
            {
                soapResult = Encoding.Default.GetString(rd.ReadBytes(10000000));
            }
        }
        else
        {
            using (StreamReader rd = new StreamReader(webResponse.GetResponseStream()))
            {
                soapResult = rd.ReadToEnd();
            }
        }
    }
}
...

我该如何克服这个问题?我现在已经苦苦挣扎了好几天,但不知道出了什么问题...

编辑: 实际响应看起来完全像这样:

--MIME_Boundary
Content-ID: <c907fb5b-7aa7-4556-88fd-5074f43aafbb>
Content-Type: application/xop+xml; type="text/xml"; charset=UTF-8
Content-Transfer-Encoding: 8bit

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><env:Header xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><wsse:Security soap:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsu:Timestamp wsu:Id="Timestamp-JAuOpC673QOitIDD7tlAuA22" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"><wsu:Created>2021-08-19T12:00:14Z</wsu:Created><wsu:Expires>2021-08-19T12:05:14Z</wsu:Expires></wsu:Timestamp></wsse:Security></env:Header><env:Body xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><ns10:getDocument xmlns="http://eeszt.gov.hu/ns/common/ws/messageHeaderTypes/v3" xmlns:ns2="http://eeszt.gov.hu/ns/common/cdm/v3" xmlns:ns3="http://eeszt.gov.hu/ns/common/ws/messageTypes/v3" xmlns:ns4="http://eeszt.gov.hu/ns/eDoc/ws/eDocService/v1" xmlns:ns5="http://eeszt.gov.hu/ns/eDoc/ws/eDocService/messages/v1" xmlns:ns6="http://eeszt.gov.hu/ns/eDoc/ws/eDocService/Request/v1" xmlns:ns7="http://eeszt.gov.hu/ns/documentrenderer/ws/common/messages/v1" xmlns:ns8="http://eeszt.gov.hu/ns/common/ws/queryTypes/v3" xmlns:ns9="http://eeszt.gov.hu/ns/common/ws/errorTypes/v3" xmlns:ns10="http://eeszt.gov.hu/ns/eDoc/ws/eDocService/Response/v1" xmlns:ns11="http://eeszt.gov.hu/ns/common/ws/versionInfoTypes/v3" xmlns:ns12="http://eeszt.gov.hu/ns/documentrenderer/ws/DocumentRendererService"><ns10:getDocumentResponse><ns3:businessMessageHeader><initiator><userId>O60547</userId><userName>Dr. Psyklon</userName><clientUserId>O60547</clientUserId><applicationId>DokiRex.NET:v1.57</applicationId><applicationName>DokiRex.NET</applicationName><applicationFunction>edoc_get_document</applicationFunction><organizationId>E200158</organizationId><organizationName>Asd</organizationName><organizationUnitId>002001581</organizationUnitId><organizationUnitName>Eeszt Dev</organizationUnitName></initiator><representedUser><userId>O60547</userId><userName>Orvos 1 DEV69</userName><clientUserId>O60547</clientUserId></representedUser><initiatorCitizen><ns2:citizenIDs><ns2:citizenID><ns2:type>1</ns2:type><ns2:id>900200018</ns2:id></ns2:citizenID></ns2:citizenIDs></initiatorCitizen><representedCitizen><ns2:citizenIDs><ns2:citizenID><ns2:type>1</ns2:type><ns2:id>900200018</ns2:id></ns2:citizenID></ns2:citizenIDs></representedCitizen><logging><submittedAt>2021-08-19T14:00:14.156+02:00</submittedAt><EESZTLogging><receivedAt>2021-08-19T14:00:14.147+02:00</receivedAt><processedAt>2021-08-19T14:00:14.234+02:00</processedAt><EESZTtransactionId>0aa47ec3-ad01-4a37-b75b-121c2f645a1c</EESZTtransactionId></EESZTLogging></logging></ns3:businessMessageHeader><ns3:status>OK</ns3:status><ns5:businessMessageContent><ns5:eDoc><ns4:id>11953</ns4:id><ns4:docType>ack</ns4:docType><ns4:docTypeName>Kézbesítés igazolás</ns4:docTypeName><ns4:docNev>Kézbesítés igazolás</ns4:docNev><ns4:docStatusz>REND</ns4:docStatusz><ns4:docStatuszName>Renderelt</ns4:docStatuszName><ns4:docFeltoltoIntezmeny>E200006</ns4:docFeltoltoIntezmeny><ns4:docFeltoltoSzervezetiEgyseg>002000061</ns4:docFeltoltoSzervezetiEgyseg><ns4:docFeltoltoUser>O60017</ns4:docFeltoltoUser><ns4:docFelelosUser>O60017</ns4:docFelelosUser><ns4:allampolgar><ns2:type>1</ns2:type><ns2:id>900200018</ns2:id></ns4:allampolgar><ns4:feltoltesIdeje>2021-06-23T19:12:52.308+02:00</ns4:feltoltesIdeje><ns4:tarolasiIdo>2021-10-01T19:12:52.000+02:00</ns4:tarolasiIdo><ns4:forrasDocId>11952</ns4:forrasDocId><ns4:parentDocId>11952</ns4:parentDocId><ns4:attachments><ns4:attachmentResponse><ns4:id>29625</ns4:id><ns4:name>kezbesitesi_jelentes.pdf</ns4:name><ns4:mimeType>application/pdf</ns4:mimeType><ns4:file><inc:Include href="cid:5bed3685-037d-438e-a306-b96d1c1ac435-1158030@eeszt.gov.hu" xmlns:inc="http://www.w3.org/2004/08/xop/include"/></ns4:file></ns4:attachmentResponse></ns4:attachments><ns4:recipients/><ns4:linkedDocuments/></ns5:eDoc></ns5:businessMessageContent></ns10:getDocumentResponse></ns10:getDocument></env:Body></soapenv:Envelope>
--MIME_Boundary
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
Content-ID: <5bed3685-037d-438e-a306-b96d1c1ac435-1158030@eeszt.gov.hu>

%PDF-1.4
%ª«¬­
1 0 obj
<<
/Creator (Apache FOP Version 2.3)
/Producer (Apache FOP Version 2.3)
/CreationDate (D:20210623191252+02'00')
>>
endobj
2 0 obj
<<
  /N 3
  /Length 3 0 R
  /Filter /FlateDecode
>>
stream
xœí™gPTY€ï{
ÝM“¡ÉI¢„$ç$A²¨@w“i¡ÉAQdpFIŠ ¢€ŽAFQÅ€(( ¢N#ƒ€2Ž"**KãÙ­
...

您收到的是多部分响应,因此您需要对其进行解析以获取各个部分。我将提供一个使用 Microsoft.AspNet.WebApi.Client NuGet package that includes extension methods for easier processing of such content. Since you use the old now obsolete HttpWebRequest you need first to transform the stream you get to a HttpContent 对象的示例解决方案,然后您可以使用扩展方法 ReadAsMultipartAsync 从中获取一个 MultipartMemoryStreamProvider 对象。此对象有一个内容数组,因此您可以阅读每个部分。

您的代码已更改为将 PDF 部分读取为字符串。

string soapResult;
using (HttpWebResponse webResponse = (HttpWebResponse)httpRequest.EndGetResponse(asyncResult))
{
   StreamContent streamContent = new StreamContent(webResponse.GetResponseStream());
   streamContent.Headers.Add("Content-Type", webResponse.ContentType);

   MultipartMemoryStreamProvider? multipart = await streamContent.ReadAsMultipartAsync();

   soapResult = await multipart.Contents[1].ReadAsStringAsync(); // read second content that has index 1
}

我建议您更改代码以使用较新的 HttpClient 来执行网络请求,然后此代码会变得更加简单。

string soapResult;
using (HttpClient httpClient = new HttpClient()) // For best performance you should create one HttpClinet for all the application and reuse it for all calls not create new for each call.
{
   using (HttpResponseMessage? httpResponseMessage = await httpClient.GetAsync("https://localhost:5001/api/multipart"))
   {
      MultipartMemoryStreamProvider? multipart = await httpResponseMessage.Content.ReadAsMultipartAsync();

      soapResult = await multipart.Contents[1].ReadAsStringAsync();
   }
}


  

不要通过任何 Encoding 将二进制内容作为字符串读取。原始 PDF 内容不符合任何编码,因此您的字符串结果肯定会损坏。

如果您需要 return 二进制内容作为 SOAP 消息中的文本,请改用 base64 编码:

// binary content: as base64
if (webResponse.ContentType == "application/octet-stream")
    soapResult = Convert.ToBase64String(br.ReadBytes((int)webResponse.ContentLength));

并且您的 SOAP 消息的接收者可以通过 Convert.FromBase64String 恢复原始二进制内容。