IRS-A2A Сообщение BulkRequestTransmitter не сформировано должным образом и / или не может быть интерпретировано
Я получаю следующую ошибку при попытке отправки через веб-службу BulkRequestTransmitter. Руководство по составлению менее чем полезно в том, что касается этого сообщения, и когда я сравниваю свой SOAP XML с SOAP из Руководства по составлению, они кажутся яблоками. Я надеюсь, что другой глаз может увидеть, в чем проблема.
Сообщение не было правильно отформатировано и / или не может быть интерпретировано. Пожалуйста, ознакомьтесь со стандартами XML, изложенными в Разделе 3 Руководства по составлению и справочному представлению AIR, расположенного по адресу https://www.irs.gov/for-Tax-Pros/Software-Developers/Information-Returns/Affordable-Care-Act-Information-Return-AIR-Program, исправьте все ошибки и попробуйте снова.
Что я пробовал:
- Попытка отправки с (и без) пробелами в конверте SOAP.
- Попытка отправки с данными формы XML в формате XML.
Попытка отправки с данными формы вbase64string
формат (как это было представлено).Добавилds
префикс к элементам подписи. Использовал этот пост SO, чтобы добавить префикс к элементам Signature.- Добавлены данные формы в формате "Pretty Print" и в соответствии с обновленным Руководством по составлению (v4.2).
- Скопировал форматирование
MIME
дляBulkTransmitterService
запрос изложен в разделе 10.3 Руководства по составлению. - Создано два решения: 1.) Вручную создайте XML, необходимый для запросов SOAP, и отправьте через
HttpWebRequest
объект; 2.) Отправка запроса на отправку черезWSDL
импортируется в проект какService Reference
, используя пользовательские кодеры дляGZip and Mtom Encoding
и создание вручную XML, необходимого для запроса статуса SOAP (отправляется черезHttpWebRequest
).
Обновление № 1
Обновлен запрос на основе некоторых новых дополнений.
Добавилds
префикс к элементам подписи.- Добавлены данные формы в формате "Pretty Print" и в соответствии с обновленным Руководством по составлению (v4.2: раздел 5.4.2).
Обновление № 2
Я начал вручную создавать SOAP
XML-файл в новом экземпляре Visual Studio, импортирующий ссылки на схемы по мере необходимости. Я делаю это вне любого вида создания приложений.
При этом я смог найти некоторые дополнительные ошибки в SOAP
Я создавал через мое приложение (спасибо за intellisense!). Ошибки, которые я обнаружил, были в Manifest XML, поскольку они не соответствовали схеме IR S.
Я буду смотреть на них в течение следующих 24 часов и обновлять соответственно.
urn:MailingAddressGrp
должен иметь ребенка либоurn:USAddressGrp
или жеurn:ForeignAddressGrp
, Этот потом должен содержать правильные элементы адреса. В моем коде в данный момент отсутствует прямой потомокurn:MailingAddressGrp
,Значение дляurn1:DocumentSystemFileNm
изForm1094C_Request_[TCC]_yyyyMMddThhmmssfffZ.xml
это неверно. Я не совсем уверен, что это должно быть просто пока.urn1:BulkExchangeFile
элемент, имеющий проблему, связанную сxop:Include
элемент у меня внутри. Схема хочет тип base64Binary.
Обновление № 2.5
- Обновлен мой процесс генерации XML, чтобы включить
USAddressGrp
элемент. - Обнаружил, что у меня был один дополнительный символ в миллисекундах (четыре вместо трех). Как только я исправил это, наряду с удалением строки "Форма" из начала имени файла, значение для
urn1:DocumentSystemFileNm
удалось успешно проверить схему.
Обновление № 3
- Обновлен полный запрос на основе сделанных мной обновлений. На данный момент я не могу понять, что не так с моей просьбой. Если кто-то видит что-то ослепительное, помогите!
Обновление № 4
Обновлен полный запрос на основе дополнительных обновлений. Удалил
ds
префикс из подписи на основе отзывов других пользователей SO. Этот пользователь получил эти запросы для работы без добавленияds
Префикс к подписи после факта и пересчитать подпись.Пользователь SO также подтвердил, что его запросы работают с
<inc:Include>
элемент настраивается как дочерний элемент<BulkExchangeFile>
элемент.- Подтвердил
MIME
заголовки правильны согласно образцу в разделе 10.3 Руководства по составлению.
Обновление № 5
В настоящее время у меня есть два решения: одно - отправка вручную, создание XML, необходимого для запросов SOAP, и отправка через
HttpWebRequest
; и тот, который используетWSDL Service Reference
для запроса на отправку, используя пользовательские кодеры, описанные ниже, и вручную создавая XML, необходимый для SOAP-запроса статуса.Начиная с этого обновления, Решение 1 выдает мне ошибку, указанную выше, при отправке запроса на отправку, и дает мне ошибку, указанную ниже, при отправке запроса о состоянии. Однако при использовании Решения 2 оба запроса (Отправка и Статус) дают мне ошибку ниже.
Я изучаю возможные проблемы с сертификатами, чтобы узнать, достигнут ли они какой-либо прогресс с любым из этих решений.
Обновление № 6
Я столкнулся с рядом проблем, из-за которых меня задержали. Я избавлю вас от мельчайших подробностей, однако суть в том, что у нас не было зарегистрировано Сертификата безопасности в системе IR S, а также у нас не был установлен Сертификат должным образом, чтобы я мог получить доступ к информации через X509Store
, Наконец эти вещи были сделаны, и я смог проверить отправку данных в IR S с сервера (по сравнению с моей локальной машиной, которая не имела надлежащего сертификата). К сожалению, я все еще получаю ошибку WS-Security, подробно описанную ниже. Я обновил полный запрос тем, что сейчас отправляю.
Произошла ошибка с сообщением: заголовок безопасности WS в сообщении недействителен. Пожалуйста, ознакомьтесь с инструкциями по передаче, изложенными в разделе 5 Руководства по составлению и справочному представлению AIR по адресу https://www.irs.gov/for-Tax-Pros/Software-Developers/Information-Returns/Affordable-Care-Act-Information-Return-AIR-Program, исправьте все ошибки и попробуйте снова.
Все разрывы строк в заголовках MIME выполнены как есть, и я считаю, что разрывы строк - это то, что ожидается. Вложение Form Data отправляется как Pretty Print, а конверт SOAP - нет; Конверт SOAP в этом посте отформатирован для удобства чтения.
ОБНОВЛЕНИЕ № 7:
Спасибо пользователям: jstill и FatherOfWine за то, что они опубликовали ниже, и Бон за более раннюю помощь в этом проекте. Я прорвался сквозь одну стену, чтобы получить представление о работе. Сейчас работает. Запрос статуса также работает. Однако мне нужно выяснить, как его обработать, чтобы извлечь из него статус и вложение (файл данных об ошибках).
Полный запрос:
Content-Encoding: gzip
Accept-Encoding: gzip, deflate
Content-Type: multipart/related; type="application/xop+xml"; start="<rootpart>"; start-info="text/xml"; boundary="MIME_boundary"
SOAPAction: BulkRequestTransmitter
MIME-Version: 1.0
Host: la.www4.irs.gov
--MIME_Boundary
Content-Type: application/xop+xml; charset=UTF-8; type="text/xml"
Content-Transfer-Encoding: 8bit
Content-Id: <root_part>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Security xmlns:h="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Signature Id="SIG-E77c57b78ebc54e989bfc9e43604a04a4" xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<Reference URI="#TS-Eb4799bee41bb4df0a72f52832d283ef7">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>[TimestampDigestValue]</DigestValue>
</Reference>
<Reference URI="#id-E5f1ed32aab8f4578adeee5debd851a62">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>[ACABusinessHeaderDigestValue]</DigestValue>
</Reference>
<Reference URI="#id-E4a71164001994d7f865fc7ddb8055350">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>[ManifestDigestValue]</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>[SignatureValue]</SignatureValue>
<KeyInfo Id="KI-E2309cb142e1a4076a2e71373e6e6b75f">
<SecurityTokenReference d6p1:Id="STR-E2751169ee468470290fe5e8bfb34589e" xmlns:d6p1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">[KeyIdentifier]</KeyIdentifier>
</SecurityTokenReference>
</KeyInfo>
</Signature>
<a:Timestamp a:Id="TS-Eb4799bee41bb4df0a72f52832d283ef7" xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<a:Created>2016-05-18T09:51:05.856Z</a:Created>
<a:Expires>2016-05-18T10:01:05.856Z</a:Expires>
</a:Timestamp>
</Security>
<ACATransmitterManifestReqDtl a:Id="id-E4a71164001994d7f865fc7ddb8055350" xmlns:h="urn:us:gov:treasury:irs:ext:aca:air:7.0" xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns="urn:us:gov:treasury:irs:ext:aca:air:7.0">
<PaymentYr>2015</PaymentYr>
<PriorYearDataInd>0</PriorYearDataInd>
<EIN xmlns="urn:us:gov:treasury:irs:common">000000301</EIN>
<TransmissionTypeCd>O</TransmissionTypeCd>
<TestFileCd>T</TestFileCd>
<OriginalReceiptId />
<TransmitterNameGrp>
<BusinessNameLine1Txt />
</TransmitterNameGrp>
<CompanyInformationGrp>
<CompanyNm>Selitestthree</CompanyNm>
<MailingAddressGrp>
<USAddressGrp>
<AddressLine1Txt>6689 Willow Court</AddressLine1Txt>
<CityNm xmlns="urn:us:gov:treasury:irs:common">Beverly Hills</CityNm>
<USStateCd>CA</USStateCd>
<USZIPCd xmlns="urn:us:gov:treasury:irs:common">90211</USZIPCd>
</USAddressGrp>
</MailingAddressGrp>
<ContactNameGrp>
<PersonFirstNm>Rose</PersonFirstNm>
<PersonLastNm>Lincoln</PersonLastNm>
</ContactNameGrp>
<ContactPhoneNum>5559876543</ContactPhoneNum>
</CompanyInformationGrp>
<VendorInformationGrp>
<VendorCd>I</VendorCd>
<ContactNameGrp>
<PersonFirstNm>ContactFirstName</PersonFirstNm>
<PersonLastNm>ContactLastName</PersonLastNm>
</ContactNameGrp>
<ContactPhoneNum>ContactPhoneNumber</ContactPhoneNum>
</VendorInformationGrp>
<TotalPayeeRecordCnt>3</TotalPayeeRecordCnt>
<TotalPayerRecordCnt>1</TotalPayerRecordCnt>
<SoftwareId>PPACA</SoftwareId>
<FormTypeCd>1094/1095C</FormTypeCd>
<BinaryFormatCd xmlns="urn:us:gov:treasury:irs:common">application/xml</BinaryFormatCd>
<ChecksumAugmentationNum xmlns="urn:us:gov:treasury:irs:common">6b2512ce28f603f76261923d297738e5</ChecksumAugmentationNum>
<AttachmentByteSizeNum xmlns="urn:us:gov:treasury:irs:common">14076</AttachmentByteSizeNum>
<DocumentSystemFileNm>1094C_Request_[TCC]_20160518T215105716Z.xml</DocumentSystemFileNm>
</ACATransmitterManifestReqDtl>
<ACABusinessHeader a:Id="id-E5f1ed32aab8f4578adeee5debd851a62" xmlns:h="urn:us:gov:treasury:irs:msg:acabusinessheader" xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns="urn:us:gov:treasury:irs:msg:acabusinessheader">
<UniqueTransmissionId xmlns="urn:us:gov:treasury:irs:ext:aca:air:7.0">51958882-c653-4eab-8dfb-287ecc555aaa:SYS12:[TCC]::T</UniqueTransmissionId>
<Timestamp xmlns="urn:us:gov:treasury:irs:common">2016-05-18T14:51:05.8568594-07:00</Timestamp>
</ACABusinessHeader>
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ACABulkRequestTransmitter xmlns="urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter" version="1.0">
<BulkExchangeFile xmlns="urn:us:gov:treasury:irs:common">
<inc:Include href="cid:1094C_Request_BB0S4_20160518T215105716Z.xml" xmlns:inc="http://www.w3.org/2004/08/xop/include" />
</BulkExchangeFile>
</ACABulkRequestTransmitter>
</s:Body>
</s:Envelope>
--MIME_Boundary
Content-Type: text/xml; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Id: <1094C_Request_BB0S4_20160518T215105716Z.xml>
Content-Disposition: attachment; name="1094C_Request_[TCC]_20160518T215105716Z.xml"
[PrettyPrintFormDataXml]
--MIME_boundary--
6 ответов
Не знаю, решит ли это вашу проблему, но, тем не менее, я попробую. Иногда помощь приходит из очень неожиданных источников:)
- Прежде всего, поля меток времени имеют неправильный формат: одно в businessheader НЕ ДОЛЖНО содержать миллисекунд. Я знаю это точно.
- В метке времени заголовка безопасности только 3 цифры могут представлять миллисекунды.
- Удалите пустые элементы, такие как "OriginalReceiptId" из элемента ACATransmitterManifestReqDtl: им они не нравятся.
- Я надеюсь, что вы предоставляете им надлежащий программный идентификатор, потому что он у вас пуст в полезной нагрузке, но я уверен, что они хотели бы иметь его, imho.:)
И я думаю, что сообщение, которое вы получили в ответе, также имеет отношение к элементу подписи. Я думаю, что они хотят, чтобы элемент Signature имел какой-то префикс (я думаю, желательно "ds"). Но тут я не уверен на 100%.
Видишь ли, я сражаюсь в той же битве, что и ты. И моя временная метка безопасности сообщений имеет префикс "u", и они не жалуются на это. Хотя им никогда не нравился двоичный код безопасности.:) Я изо всех сил пытаюсь сгенерировать подпись по вкусу IRS. WCF очень скрытен и не позволяет легко менять префикс на мыльном конверте или выбирать алгоритм CanonicalizationMethod для подписи.
ОБНОВЛЕНИЕ: удалось успешно отправить запрос в службу. Скажу сразу: префиксы не важны. Что было важно: тег CorrectedInd должен присутствовать в Form1095BUpstreamDetail и атрибуты recordType="String" lineNum="0" также должны присутствовать.
ОБНОВЛЕНИЕ 2: Еще одна вещь, которую я изменил, я поместил ACABusinessHeader перед ManifestDtl. Вот мои настройки: я использую WCF в качестве носителя и SignedXml для генерации подписи. Также я использую пользовательский кодер gZip (по понятным причинам0 и пользовательский MtomEncoder для чтения ответа от службы (да, да, это MTOMed:)). Можете ли вы поверить этим покемонам?!?!?), И это еще не все: они отправляют ответ как составной документ только с 1 частью!:)) Я должен был настроить мой кодер, чтобы справиться с этим. И вуаля, сервис начал себя вести. Надеюсь, это поможет.
ОБНОВЛЕНИЕ3 Прежде всего убедитесь, что данные в файле вложения соответствуют сценарию тестирования, который вы используете в качестве морской свинки. Я, наверное, звучу как битая пластинка, но это ДЕЙСТВИТЕЛЬНО важно. Теперь я отрежу вещи и представлю то, что у меня есть. Это немного грубо, но это делает свое дело.:)
1. Вот часть файла конфигурации:
1.1.Убедитесь, что элемент system.serviceModel содержит следующую часть:
<extensions>
<bindingElementExtensions>
<add name="gzipMessageEncoding" type="<namespaceWhereEncoderLives>.GZipMessageEncodingElement, GZipEncoder, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
1.2. Убедитесь, что обязательный элемент содержит это:
<customBinding>
<binding name="BulkRequestTransmitterBinding">
<gzipMessageEncoding innerMessageEncoding="textMessageEncoding" />
<httpsTransport />
</binding>
</customBinding>
1.3. Измените привязку конечной точки BulkRequestTransmitterPort в клиентском элементе на "customBinding"(а также измените имя привязки на имя настраиваемой привязки) и убедитесь, что она содержит следующую часть:
<identity>
<dns value="domain from cert" />
</identity>
Также клиентский элемент должен содержать следующую часть:
<metadata>
<policyImporters>
<extension type="NamespaceToToTheLocationOf.GZipMessageEncodingBindingElementImporter, GZipMessageEncoder, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</policyImporters>
</metadata>
Кодировщик GZip вы можете получить по следующей ссылке: https://msdn.microsoft.com/en-us/library/cc138373(v=vs.90).aspx Просто скачайте пример WCF и полностью переместите весь проект GZipMessageEncoder под свой проект.
Получить MTOMEncoder(который я переименовал из SwaEncoder для ясности) по этой ссылке: Soap-with-Attachments Переместите следующие классы в проект GZipMessageEncoder:
MimeContent, MimeParser, MimePart, MTOMEncoderИзмените класс GZipMessageEncoder следующим образом:
4.1. Добавьте следующий код в начале класса://------------------- MTOM related stuff. Begin. --------------------- const string ATTCHMNT_PROP = "attachment_file_content"; const string ATTCHMNT_CONTENT_ID = "Here goes content id"; private string _ContentType; private string _MediaType; protected MimeContent _MyContent; protected MimePart _SoapMimeContent; protected MimePart _AttachmentMimeContent; protected GZipMessageEncoderFactory _Factory; protected MimeParser _MimeParser; private void SetupMTOM(GZipMessageEncoderFactory factory) { // _ContentType = "multipart/related"; _MediaType = _ContentType; // // Create owned objects // _Factory = factory; _MimeParser = new MimeParser(); // // Create object for the mime content message // _SoapMimeContent = new MimePart() { ContentTypeStart = "application/xop+xml", ContentType = "text/xml", ContentId = "Here goes envelope MIME id from HTTP Content-Type header", // TODO: make content id dynamic or configurable? CharSet = "UTF-8", // TODO: make charset configurable? TransferEncoding = "8bit" // TODO: make transfer-encoding configurable? }; _AttachmentMimeContent = new MimePart() { ContentType = "application/xml", // TODO: AttachmentMimeContent.ContentType configurable? ContentId = ATTCHMNT_CONTENT_ID, // TODO: AttachmentMimeContent.ContentId configurable/dynamic? TransferEncoding = "7bit" // TODO: AttachmentMimeContent.TransferEncoding dynamic/configurable? }; _MyContent = new MimeContent() { Boundary = "here goes boundary id" // TODO: MimeContent.Boundary configurable/dynamic? }; _MyContent.Parts.Add(_SoapMimeContent); _MyContent.Parts.Add(_AttachmentMimeContent); _MyContent.SetAsStartPart(_SoapMimeContent); } //------------------- MTOM related stuff. End. ----------------------
4.2. Измените метод WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) следующим образом:
public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
{
ArraySegment<byte> buffer = innerEncoder.WriteMessage(message, maxMessageSize, bufferManager, 0);
var requestSOAPEnvelopeXml = System.Text.Encoding.UTF8.GetString(buffer.Array);
//Here you create Security node and sign the request. For ex:
requestSOAPEnvelopeXml = SigngEnvelope(requestSOAPEnvelopeXml);
//Here you are getting 1094\1095 forms xml payload.
string fileContent = GetAttachmentFileContent();
//Here comes the MTOMing...
_SoapMimeContent.Content = System.Text.Encoding.UTF8.GetBytes(requestSOAPEnvelopeXml);
_AttachmentMimeContent.Content = System.Text.Encoding.UTF8.GetBytes(fileContent);
_MyContent.Parts.Where(m=> m.ContentId!=null && m.ContentId.Equals(ATTCHMNT_CONTENT_ID)).Single().ContentDisposition = GetFileName(envelope);
// Now create the message content for the stream
byte[] MimeContentBytes = _MimeParser.SerializeMimeContent(_MyContent);
int MimeContentLength = MimeContentBytes.Length;
// Write the mime content into the section of the buffer passed into the method
byte[] TargetBuffer = bufferManager.TakeBuffer(MimeContentLength + messageOffset);
Array.Copy(MimeContentBytes, 0, TargetBuffer, messageOffset, MimeContentLength);
// Return the segment of the buffer to the framework
return CompressBuffer(new ArraySegment<byte>(TargetBuffer, messageOffset, MimeContentLength), bufferManager, messageOffset);
}
4,3. Переопределите еще пару методов, как это:
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
ArraySegment<byte> decompressedBuffer = DecompressBuffer(buffer, bufferManager);
MtomEncoder mtomEncoder = new MtomEncoder(innerEncoder, _Factory);
Message returnMessage = mtomEncoder.ReadMessage(buffer, bufferManager, contentType);
returnMessage.Properties.Encoder = mtomEncoder;
return returnMessage;
}
public override bool IsContentTypeSupported(string contentType)
{
return true;
}
4.4. Убедитесь, что конструктор GZipMessage выглядит так:
internal GZipMessageEncoder(MessageEncoder messageEncoder, GZipMessageEncoderFactory factory)
: base()
{
if (messageEncoder == null)
throw new ArgumentNullException("messageEncoder", "A valid message encoder must be passed to the GZipEncoder");
innerEncoder = messageEncoder;
SetupMTOM(factory);
}
5. Убедитесь, что класс GZipMessageEncodingBindingElement имеет следующий метод:
public override void ApplyConfiguration(BindingElement bindingElement)
{
GZipMessageEncodingBindingElement binding = (GZipMessageEncodingBindingElement)bindingElement;
PropertyInformationCollection propertyInfo = this.ElementInformation.Properties;
if (propertyInfo["innerMessageEncoding"].ValueOrigin != PropertyValueOrigin.Default)
{
switch (this.InnerMessageEncoding)
{
case "textMessageEncoding":
binding.InnerMessageEncodingBindingElement =
new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
break;
case "binaryMessageEncoding":
binding.InnerMessageEncodingBindingElement = new BinaryMessageEncodingBindingElement();
break;
}
}
}
Изменить класс MTOMEncoder. Убедитесь, что следующий метод выглядит следующим образом:
public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType) { VerifyOperationContext(); if (contentType.ToLower().StartsWith("multipart/related")) { byte[] ContentBytes = new byte[stream.Length]; stream.Read(ContentBytes, 0, ContentBytes.Length); MimeContent Content = _MimeParser.DeserializeMimeContent(contentType, ContentBytes); if (Content.Parts.Count >= 1) { MemoryStream ms = new MemoryStream(Content.Parts[0].Content); //At least for now IRS is sending SOAP envelope as 1st part(and only part(sic!) of MULTIpart response) as xml. Message Msg = ReadMessage(ms, int.MaxValue, "text/xml");//Content.Parts[0].ContentType); if( Content.Parts.Count>1 ) Msg.Properties.Add(ATTCHMNT_PROP, Content.Parts[1].Content); return Msg; } else { throw new ApplicationException("Invalid mime message sent! Soap with attachments makes sense, only, with at least 2 mime message content parts!"); } } else if (contentType.ToLower().StartsWith("text/xml")) { XmlReader Reader = XmlReader.Create(stream); return Message.CreateMessage(Reader, maxSizeOfHeaders, MessageVersion); } else { throw new ApplicationException( string.Format( "Invalid content type for reading message: {0}! Supported content types are multipart/related and text/xml!", contentType)); } }
Конструктор класса GZipMessageEncoderFactory должен выглядеть следующим образом:
public GZipMessageEncoderFactory(MessageEncoderFactory messageEncoderFactory) { if (messageEncoderFactory == null) throw new ArgumentNullException("messageEncoderFactory", "A valid message encoder factory must be passed to the GZipEncoder"); encoder = new GZipMessageEncoder(messageEncoderFactory.Encoder, this); }
Вот как я называю сервис:
var requestClient = new BulkRequestTransmitterPortTypeClient("BulkRequestTransmitterPort"); requestClient.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None; #if DEBUG var vs = requestClient.Endpoint.Behaviors.Where((i) => i.GetType().Namespace.Contains("VisualStudio")); if( vs!=null ) requestClient.Endpoint.Behaviors.Remove((System.ServiceModel.Description.IEndpointBehavior)vs.Single()); #endif using (var scope = new OperationContextScope(requestClient.InnerChannel)) { //Adding proper HTTP Header to an outgoing requqest. HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty(); requestMessage.Headers["Content-Encoding"] = "gzip"; requestMessage.Headers["Content-Type"] = @"multipart/related; type=""application/xop+xml"";start=""<Here goes envelope boundary id>"";start-info=""text/xml"";boundary=""here goes boundary id"""; OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage; response = requestClient.BulkRequestTransmitter(request.ACASecurityHeader, request.Security, ref request.ACABusinessHeader, request.ACATransmitterManifestReqDtl, request.ACABulkRequestTransmitter); }
Изменить часть Mime:
9.1. Добавить новый метод:
public void GetHeader(StringBuilder Builder)
{
if (string.IsNullOrEmpty(ContentId) && string.IsNullOrEmpty(ContentType) && string.IsNullOrEmpty(TransferEncoding))
return;
if (!string.IsNullOrEmpty(ContentTypeStart))
{
Builder.Append(string.Format("Content-Type: {0}", ContentTypeStart));
Builder.Append(string.Format("; type=\"{0}\"", ContentType));
}
else
Builder.Append(string.Format("Content-Type: {0}", ContentType));
if (!string.IsNullOrEmpty(CharSet)) Builder.Append(string.Format("; charset={0}", CharSet));
Builder.Append(new char[] { '\r', '\n' });
Builder.Append(string.Format("Content-Transfer-Encoding: {0}", TransferEncoding));
Builder.Append(new char[] { '\r', '\n' });
Builder.Append(string.Format("Content-Id: {0}", ContentId));
Builder.Append(new char[] { '\r', '\n' });
if (!string.IsNullOrEmpty(ContentDisposition))
Builder.Append(string.Format("Content-Disposition: attachment; filename=\"{0}\"", ContentDisposition));
}
9.2. Добавить недвижимость:
public string ContentDisposition { get; set; }
Измените метод MimeParser SerializeMimeContent(): замените этот блок кода:
Builder.Append(string.Format("Content-Type: {0}", item.ContentType)); if (!string.IsNullOrEmpty(item.CharSet)) Builder.Append(string.Format("; charset={0}", item.CharSet)); Builder.Append(new char[] { '\r', '\n' }); Builder.Append(string.Format("Content-Transfer-Encoding: {0}", item.TransferEncoding)); Builder.Append(new char[] { '\r', '\n' }); Builder.Append(string.Format("Content-Id: {0}", item.ContentId));
с этим:
item.GetHeader(Builder);
И это должно быть так! Скинь туфли и копай блюз!:)))
Во-первых, быстрый отказ от ответственности. Этот ответ стал возможен благодаря замечательным материалам, предоставленным папой Отфайном, Руссом и Боном в этом и других SO-вопросах. Все, что я действительно делал, это объединял кучу вещей из них и взламывал проблемы, которые у меня были до тех пор, пока все не заработало. Что еще более важно, код, предоставленный здесь, является ПЛОХОЙ и, вероятно, не должен использоваться как есть. Я планирую исправить это немного теперь, когда я знаю, что работает, и я рекомендую всем, кто использует это, сделать то же самое. Большая вещь, которая, вероятно, бросится в глаза всем, кто смотрит на это, - это множество статических переменных, которые я использовал как быстрый взлом, чтобы добраться до вещей по всему конвейеру. Серьезно, не используйте это как есть в производстве, это результат многих часов, когда вы просто бросаете вещи в стену, пока что-то не застряло, но это должно стать хорошей отправной точкой для улучшения ситуации.
Здесь слишком много кода, чтобы действительно включать все это, поэтому я просто рассмотрю некоторые основные моменты и общие открытия, а затем добавлю ссылку на решение VS.
- Убедитесь, что у вас уже есть настройки TCC и другие идентификаторы, что вы приобрели правильный вид сертификата ( страница 41 этого документа) и правильно зарегистрировали сертификат ( см. Этот документ)
- Я обнаружил, что удаление всех CR-LF в мыльном конверте было необходимо, чтобы получить сообщение, которое будет принято. С их помощью я бы обнаружил ошибки в некоторых элементах "неожиданных дочерних элементов" или что-то в этом роде.
- Документация противоречит сама себе в нескольких местах (см. Стр. 74 и 84 и посмотрите, что, по их словам, элемент BulkExchangeFile должен содержать в качестве примера), и wsdl/xsds также просто неверны. Может быть, у меня просто были старые, как-то, но мне приходилось вносить изменения и пробовать что-то, пока я не нашел то, что сервис на их стороне мог бы принять.
- Очень важно, чтобы вы правильно добавили информацию о ключах в раздел Signxml, чтобы все ваши ссылки были правильно построены и содержали соответствующие списки InclusiveNamespaces, и чтобы после вызова ComputeSignature единственное изменение, которое вы вносите в конверт, - это добавление подписи. элемент к этому.
- Говоря об элементе подписи, если он появляется после элемента временной метки внутри элемента безопасности, система IRS возвратит ошибку. Это должно быть первым.
- Поскольку префиксы пространства имен были очень важны, когда речь шла о генерации ссылок на подписи, я пошел по пути создания конверта xml вручную, чтобы быть уверенным, что все точно соответствует тому, что они хотят. Даже тогда было несколько элементов, чьи префиксы я должен был изменить, когда тестировал, потому что то, что XSD или какая-то страница в документации говорили, что это не то, чего на самом деле хотел их сервис. К счастью, ошибки, возвращаемые службой, на самом деле оказали некоторую помощь, указав, из какого пространства имен оно ожидает значение.
Вне того, чтобы получить все настройки gzip и MTOM (опять же, спасибо миллиону папе OfWine за эту помощь), большая часть того, что в итоге сработало для меня, делается в одном классе общего пользования (который я ловко назвал "General"). Опять же, это плохой код, и он был результатом просто необходимости заставить что-то (что-нибудь!) Работать должным образом. Я пойду дальше и включу это здесь в ответ, хотя в случае, если это быстро "ах!" для всех, кто работает над этой проблемой.
using System; using System.IO; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.ServiceModel; using System.ServiceModel.Channels; using System.Text; using System.Xml; using IrsAcaClient.ACABulkRequestTransmitterService; namespace IrsAcaClient { public class General { /***************************************************** * * What I'm doing here (with static vars) is VERY BAD but this whole thing is just a dirty hack for now. * Hopefully I can clean this up later. * - JRS 2016-05-10 * *****************************************************/ public const string SecurityTimestampStringFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffZ"; public const string EnvelopeContentID = "<rootpart>"; public static string AttachmentFilePath; public static string AttachmentFileName { get { return Path.GetFileName(General.AttachmentFilePath); } } public static string AttachmentContentID {get { return string.Format("<{0}>", General.AttachmentFileName); }} public const string MIMEBoundary = "MIME_boundary"; public static string TCCode; public static Guid TransmissionGuid; public static string UniqueTransmissionId { get { return string.Format("{0}:SYS12:{1}::T", TransmissionGuid, TCCode); } } public static string SecurityTimeStampWsuId; public static string ManifestWsuId; public static string BusinessHeaderWsuId; public static string SignatureWsuId; public static string CertificatePath; public static string CertificatePassword; public static DateTime SecurityTimestampUTC; private static string _replacementSoapEnvelope; public static string ReplacementSoapEnvelope{get { return _replacementSoapEnvelope; }} private static void GenerateReference(string elementID, string inclusivePrefixList, SignedXmlWithId xSigned) { var reference = new Reference() { Uri = "#" + elementID }; XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform(); env.InclusiveNamespacesPrefixList = inclusivePrefixList; reference.AddTransform(env); xSigned.AddReference(reference); } public static string GetAttachmentFileContent() { //probably not ideal return File.ReadAllText(AttachmentFilePath); } public static string GetFileName() { //TODO: this may need to be tweaked slightly from the real filename return General.AttachmentFileName; } public static string GenerateWsuId(string prefix) { return string.Format("{0}-{1}", prefix, Guid.NewGuid().ToString().Replace("-", "").ToUpper()); } internal static void GenerateReplacementSoapEnvelope(ACABulkRequestTransmitterService.SecurityHeaderType securityHeader, ACABulkRequestTransmitterService.ACABulkBusinessHeaderRequestType businessHeader, ACABulkRequestTransmitterService.ACATrnsmtManifestReqDtlType manifest, ACABulkRequestTransmitterService.ACABulkRequestTransmitterType bulkTrans) { //load the base envelope xml var doc = new XmlDocument(); doc.PreserveWhitespace = false; doc.Load("BaseSoapEnvelope.xml"); /* Need a bunch of namespaces defined * xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" * xmlns:urn="urn:us:gov:treasury:irs:ext:aca:air:7.0" * xmlns:urn1="urn:us:gov:treasury:irs:common" * xmlns:urn2="urn:us:gov:treasury:irs:msg:acabusinessheader" * xmlns:urn3="urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter" * xmlns:wsa="http://www.w3.org/2005/08/addressing" * xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" * xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" * xmlns:ds="http://www.w3.org/2000/09/xmldsig#" * xmlns:xop="http://www.w3.org/2004/08/xop/include" */ XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); nsMgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/"); nsMgr.AddNamespace("urn", "urn:us:gov:treasury:irs:ext:aca:air:7.0"); nsMgr.AddNamespace("urn1", "urn:us:gov:treasury:irs:common"); nsMgr.AddNamespace("urn2", "urn:us:gov:treasury:irs:msg:acabusinessheader"); nsMgr.AddNamespace("urn3", "urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter"); nsMgr.AddNamespace("wsa", "http://www.w3.org/2005/08/addressing"); nsMgr.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); nsMgr.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); nsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); nsMgr.AddNamespace("xop","http://www.w3.org/2004/08/xop/include"); //start replacing values in it //for securityHeader, should have the following /* * securityHeader.Signature.Id * securityHeader.Timestamp.Id * securityHeader.Timestamp.Created.Value * securityHeader.Timestamp.Expires.Value */ //doc.SelectSingleNode("//wsse:Security/ds:Signature", nsMgr).Attributes["Id"].Value = securityHeader.Signature.Id; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp", nsMgr).Attributes["wsu:Id"].Value = securityHeader.Timestamp.Id; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Created", nsMgr).InnerText = securityHeader.Timestamp.Created.Value; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Expires", nsMgr).InnerText = securityHeader.Timestamp.Expires.Value; //for businessHeader, should have the following /* * businessHeader.UniqueTransmissionId * businessHeader.Timestamp * businessHeader.Id */ doc.SelectSingleNode("//urn2:ACABusinessHeader", nsMgr).Attributes["wsu:Id"].Value = businessHeader.Id; doc.SelectSingleNode("//urn2:ACABusinessHeader/urn:UniqueTransmissionId", nsMgr).InnerText = businessHeader.UniqueTransmissionId; doc.SelectSingleNode("//urn2:ACABusinessHeader/urn1:Timestamp", nsMgr).InnerText = businessHeader.Timestamp.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"); //for manifest, should have the following, some of which will need some conversions /* * manifest.Id * manifest.BinaryFormatCd - convert from enum * manifest.PaymentYr * manifest.PriorYearDataInd - convert from enum * manifest.EIN * manifest.TransmissionTypeCd - convert from enum * manifest.TestFileCd * manifest.TransmitterNameGrp.BusinessNameLine1Txt * manifest.CompanyInformationGrp.CompanyNm * manifest.CompanyInformationGrp.MailingAddressGrp.Item.AddressLine1Txt * manifest.CompanyInformationGrp.MailingAddressGrp.Item.CityNm * manifest.CompanyInformationGrp.MailingAddressGrp.Item.USStateCd - convert from enum * manifest.CompanyInformationGrp.MailingAddressGrp.Item.USZIPCd * manifest.CompanyInformationGrp.ContactNameGrp.PersonFirstNm * manifest.CompanyInformationGrp.ContactNameGrp.PersonLastNm * manifest.CompanyInformationGrp.ContactPhoneNum * manifest.VendorInformationGrp.VendorCd * manifest.VendorInformationGrp.ContactNameGrp.PersonFirstNm * manifest.VendorInformationGrp.ContactNameGrp.PersonLastNm * manifest.VendorInformationGrp.ContactPhoneNum * manifest.TotalPayeeRecordCnt * manifest.TotalPayerRecordCnt * manifest.SoftwareId * manifest.FormTypeCd - convert from enum * manifest.ChecksumAugmentationNum * manifest.AttachmentByteSizeNum * manifest.DocumentSystemFileNm */ doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl", nsMgr).Attributes["wsu:Id"].Value = manifest.Id; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:PaymentYr", nsMgr).InnerText = manifest.PaymentYr; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:PriorYearDataInd", nsMgr).InnerText = manifest.PriorYearDataInd.GetXmlEnumAttributeValueFromEnum(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:EIN", nsMgr).InnerText = manifest.EIN; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TransmissionTypeCd", nsMgr).InnerText = manifest.TransmissionTypeCd.ToString(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TestFileCd", nsMgr).InnerText = manifest.TestFileCd; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TransmitterNameGrp/urn:BusinessNameLine1Txt", nsMgr).InnerText = manifest.TransmitterNameGrp.BusinessNameLine1Txt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:CompanyNm", nsMgr).InnerText = manifest.CompanyInformationGrp.CompanyNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn:AddressLine1Txt", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).AddressLine1Txt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn1:CityNm", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).CityNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn:USStateCd", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).USStateCd.ToString(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn1:USZIPCd", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).USZIPCd; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:ContactNameGrp/urn:PersonFirstNm", nsMgr).InnerText = manifest.CompanyInformationGrp.ContactNameGrp.PersonFirstNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:ContactNameGrp/urn:PersonLastNm", nsMgr).InnerText = manifest.CompanyInformationGrp.ContactNameGrp.PersonLastNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:ContactPhoneNum", nsMgr).InnerText = manifest.CompanyInformationGrp.ContactPhoneNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:VendorCd", nsMgr).InnerText = manifest.VendorInformationGrp.VendorCd; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:ContactNameGrp/urn:PersonFirstNm", nsMgr).InnerText = manifest.VendorInformationGrp.ContactNameGrp.PersonFirstNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:ContactNameGrp/urn:PersonLastNm", nsMgr).InnerText = manifest.VendorInformationGrp.ContactNameGrp.PersonLastNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:ContactPhoneNum", nsMgr).InnerText = manifest.VendorInformationGrp.ContactPhoneNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TotalPayeeRecordCnt", nsMgr).InnerText = manifest.TotalPayeeRecordCnt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TotalPayerRecordCnt", nsMgr).InnerText = manifest.TotalPayerRecordCnt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:SoftwareId", nsMgr).InnerText = manifest.SoftwareId; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:FormTypeCd", nsMgr).InnerText = manifest.FormTypeCd.GetXmlEnumAttributeValueFromEnum(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:BinaryFormatCd", nsMgr).InnerText = manifest.BinaryFormatCd.GetXmlEnumAttributeValueFromEnum(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:ChecksumAugmentationNum", nsMgr).InnerText = manifest.ChecksumAugmentationNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:AttachmentByteSizeNum", nsMgr).InnerText = manifest.AttachmentByteSizeNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:DocumentSystemFileNm", nsMgr).InnerText = manifest.DocumentSystemFileNm; //for bulkTrans, should have the following /* * bulkTrans.BulkExchangeFile.Include.href */ doc.SelectSingleNode("//urn3:ACABulkRequestTransmitter/urn1:BulkExchangeFile/xop:Include", nsMgr).Attributes["href"].Value = bulkTrans.BulkExchangeFile.Include.href; //now do some more security setup var cert = new X509Certificate2(CertificatePath, CertificatePassword, X509KeyStorageFlags.MachineKeySet); var exported = cert.Export(X509ContentType.Cert, CertificatePassword); var base64 = Convert.ToBase64String(exported); //now compute all the signing stuff var xSigned = new SignedXmlWithId(doc); xSigned.Signature.Id = securityHeader.Signature.Id; // Add the key to the SignedXml document. xSigned.SigningKey = cert.PrivateKey; xSigned.Signature.Id = SignatureWsuId; xSigned.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl; var keyInfo = new KeyInfo { Id = GenerateWsuId("KI") }; //need to get the keyinfo into the signed xml stuff before we compute sigs, and because it is using some stuff that //doesn't appear to be supported out of the box we'll work around it by adding a node directly var sbKeyInfo = new StringBuilder(); sbKeyInfo.Append("<root xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">"); sbKeyInfo.Append("<wsse:SecurityTokenReference wsu:Id=\"" + GenerateWsuId("STR") + "\">"); sbKeyInfo.Append("<wsse:KeyIdentifier EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3\">" + base64.ToString()); sbKeyInfo.Append("</wsse:KeyIdentifier>"); sbKeyInfo.Append("</wsse:SecurityTokenReference>"); sbKeyInfo.Append("</root>"); XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(sbKeyInfo.ToString()); keyInfo.AddClause(new KeyInfoNode((XmlElement)tempDoc.FirstChild.FirstChild)); xSigned.KeyInfo = keyInfo; GenerateReference(SecurityTimeStampWsuId, "wsse wsa soapenv urn urn1 urn2 urn3", xSigned); GenerateReference(BusinessHeaderWsuId, "wsa soapenv urn urn1 urn3", xSigned); GenerateReference(ManifestWsuId, "wsa soapenv urn1 urn2 urn3", xSigned); // Compute the Signature. xSigned.ComputeSignature(); //signing stuff must come before the timestamp or the IRS service complains doc.SelectSingleNode("//wsse:Security", nsMgr).InsertBefore(xSigned.GetXml(), doc.SelectSingleNode("//wsse:Security", nsMgr).FirstChild); // _replacementSoapEnvelope = doc.OuterXml; } public static ACABulkRequestTransmitterResponseType Run(ACABulkRequestTransmitterService.SecurityHeaderType securityHeader, ACABulkRequestTransmitterService.ACABulkBusinessHeaderRequestType businessHeader, ACABulkRequestTransmitterService.ACATrnsmtManifestReqDtlType manifest, ACABulkRequestTransmitterService.ACABulkRequestTransmitterType bulkTrans) { //had some issues early on with the cert on the IRS server, this should probably be removed and retested without it ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; var acaSecurityHeader = new ACABulkRequestTransmitterService.TransmitterACASecurityHeaderType(); //leave this empty for transmitting via ISS-A2A var requestClient = new ACABulkRequestTransmitterService.BulkRequestTransmitterPortTypeClient("BulkRequestTransmitterPort"); requestClient.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None; //var vs = requestClient.Endpoint.Behaviors.Where((i) => i.GetType().Namespace.Contains("VisualStudio")); //if (vs != null) // requestClient.Endpoint.Behaviors.Remove((System.ServiceModel.Description.IEndpointBehavior)vs.Single()); //generate the real envelope we want GenerateReplacementSoapEnvelope(securityHeader, businessHeader, manifest, bulkTrans); using (var scope = new OperationContextScope(requestClient.InnerChannel)) { //Adding proper HTTP Header to an outgoing requqest. HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty(); requestMessage.Headers["Content-Encoding"] = "gzip"; requestMessage.Headers["Content-Type"] = string.Format(@"multipart/related; type=""application/xop+xml"";start=""{0}"";start-info=""text/xml"";boundary=""{1}""", General.EnvelopeContentID, General.MIMEBoundary); OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage; var response = requestClient.BulkRequestTransmitter(acaSecurityHeader, securityHeader, ref businessHeader, manifest, bulkTrans); //we got a response! now do something with it return response; } } }
Вот полное решение, для которого нужны только все ваши собственные данные (включая полный файл вложений со всеми записями получателя и получателя, что выходит за рамки этого, но должно быть довольно легко сгенерировано). Также обратите внимание, что это только отправка форм, а не проверка статуса. Когда я получу эту работу, я постараюсь не забыть вернуться и обновить этот ответ (но если кто-то уже имеет его и хочет поделиться им, это тоже было бы неплохо).
Изменить для службы статуса
Я объединил очищенную версию классов, сгенерированных из wsdl, и мой собственный нежелательный код, чтобы получать сообщения и обрабатывать ответы. Обратите внимание, что это еще не проверено на 100%, требуется проверка работоспособности и т. Д., Но, как и предыдущий материал, должен, по крайней мере, помочь всем остальным, кто борется с этим беспорядком. Использование здесь довольно просто:
var statusResponse = StatusService.CheckStatus(receipt, tCCode, certificatePath, certificatePassword, "https://la.www4.irs.gov/airp/aca/a2a/1095BC_Status_Request_AATS2016");
А вот полный класс (с пространством имен сгенерированных бонусов):
Смотрите мой второй ответ для статуса сервисного кода
Ответ на секунды включает включение службы статуса, а не просто еще одну ссылку, которая может исчезнуть.
Вот основной класс:
using System;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
namespace IrsAcaClient
{
public class StatusService
{
private const string SecurityTimestampStringFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffZ";
public static ACABulkRequestStatusService.ACABulkRequestTransmitterStatusDetailResponseType CheckStatus(string receiptID, string tCCode, string certificatePath, string certificatePassword, string statusServiceUrl)
{
//go ahead and generate some of the ids and timestamps we'll need
var securityTimeStampWsuId = GenerateWsuId("TS");
var businessHeaderWsuId = GenerateWsuId("id");
var detailRequestWsuId = GenerateWsuId("id");
var signatureWsuId = GenerateWsuId("SIG");
var securityTimestampUTC = DateTime.UtcNow;
var securityTimestampCreated = securityTimestampUTC.ToString(SecurityTimestampStringFormat);
var securityTimestampExpires = securityTimestampUTC.AddMinutes(10).ToString(SecurityTimestampStringFormat);
//build the envelope
//load the base envelope xml
var doc = new XmlDocument();
doc.PreserveWhitespace = false;
doc.Load("BaseStatusRequestEnvelope.xml");
/* Need a bunch of namespaces defined
* xmlns:oas1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
* xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
* xmlns:urn="urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest"
* xmlns:urn1="urn:us:gov:treasury:irs:ext:aca:air:7.0"
* xmlns:urn2="urn:us:gov:treasury:irs:common"
* xmlns:urn3="urn:us:gov:treasury:irs:msg:acasecurityheader"
* xmlns:wsa="http://www.w3.org/2005/08/addressing"
* xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
* xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
* xmlns:ds="http://www.w3.org/2000/09/xmldsig#");
*/
XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable);
nsMgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
nsMgr.AddNamespace("urn", "urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest");
nsMgr.AddNamespace("urn1", "urn:us:gov:treasury:irs:ext:aca:air:7.0");
nsMgr.AddNamespace("urn2", "urn:us:gov:treasury:irs:common");
nsMgr.AddNamespace("urn3", "urn:us:gov:treasury:irs:msg:acasecurityheader");
nsMgr.AddNamespace("wsa", "http://www.w3.org/2005/08/addressing");
nsMgr.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
nsMgr.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
nsMgr.AddNamespace("oas1", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
nsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#");
//start replacing values in it
//for securityHeader, should have the following
/*
* securityHeader.Timestamp.Id
* securityHeader.Timestamp.Created.Value
* securityHeader.Timestamp.Expires.Value
*/
doc.SelectSingleNode("//wsse:Security/wsu:Timestamp", nsMgr).Attributes["wsu:Id"].Value = securityTimeStampWsuId;
doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Created", nsMgr).InnerText = securityTimestampCreated;
doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Expires", nsMgr).InnerText = securityTimestampExpires;
//for businessHeader, should have the following
/*
* businessHeader.UniqueTransmissionId
* businessHeader.Timestamp
* businessHeader.Id
*/
doc.SelectSingleNode("//urn:ACABusinessHeader", nsMgr).Attributes["wsu:Id"].Value = businessHeaderWsuId;
doc.SelectSingleNode("//urn:ACABusinessHeader/urn1:UniqueTransmissionId", nsMgr).InnerText = GetUniqueTransmissionId(Guid.NewGuid(), tCCode);
doc.SelectSingleNode("//urn:ACABusinessHeader/urn2:Timestamp", nsMgr).InnerText = securityTimestampUTC.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ");
//for ACABulkRequestTransmitterStatusDetailRequest, should have the following
/*
* ACABulkRequestTransmitterStatusDetailRequest.Id
* ACABulkRequestTransmitterStatusDetailRequest.ACABulkReqTrnsmtStsReqGrpDtl.ReceiptId
*/
doc.SelectSingleNode("//urn:ACABulkRequestTransmitterStatusDetailRequest", nsMgr).Attributes["wsu:Id"].Value = detailRequestWsuId;
doc.SelectSingleNode("//urn:ACABulkRequestTransmitterStatusDetailRequest/urn1:ACABulkReqTrnsmtStsReqGrpDtl/urn2:ReceiptId", nsMgr).InnerText = receiptID;
//now do some more security setup
var cert = new X509Certificate2(certificatePath, certificatePassword, X509KeyStorageFlags.MachineKeySet);
var exported = cert.Export(X509ContentType.Cert, certificatePassword);
var base64 = Convert.ToBase64String(exported);
//now compute all the signing stuff
var xSigned = new SignedXmlWithId(doc);
// Add the key to the SignedXml document.
xSigned.SigningKey = cert.PrivateKey;
xSigned.Signature.Id = signatureWsuId;
xSigned.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl;
var keyInfo = new KeyInfo
{
Id = GenerateWsuId("KI")
};
//need to get the keyinfo into the signed xml stuff before we compute sigs, and because it is using some stuff that
//doesn't appear to be supported out of the box we'll work around it by adding a node directly
var sbKeyInfo = new StringBuilder();
sbKeyInfo.Append("<root xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">");
sbKeyInfo.Append("<wsse:SecurityTokenReference wsu:Id=\"" + GenerateWsuId("STR") + "\">");
sbKeyInfo.Append("<wsse:KeyIdentifier EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3\">" + base64.ToString());
sbKeyInfo.Append("</wsse:KeyIdentifier>");
sbKeyInfo.Append("</wsse:SecurityTokenReference>");
sbKeyInfo.Append("</root>");
XmlDocument tempDoc = new XmlDocument();
tempDoc.LoadXml(sbKeyInfo.ToString());
keyInfo.AddClause(new KeyInfoNode((XmlElement)tempDoc.FirstChild.FirstChild));
xSigned.KeyInfo = keyInfo;
GenerateReference(securityTimeStampWsuId, "wsse wsa oas1 soapenv urn urn1 urn2 urn3", xSigned);
GenerateReference(businessHeaderWsuId, "wsa oas1 soapenv urn1 urn2 urn3", xSigned);
GenerateReference(detailRequestWsuId, "oas1 soapenv urn1 urn2 urn3", xSigned);
// Compute the Signature.
xSigned.ComputeSignature();
//signing stuff must come before the timestamp or the IRS service complains
doc.SelectSingleNode("//wsse:Security", nsMgr).InsertBefore(xSigned.GetXml(), doc.SelectSingleNode("//wsse:Security", nsMgr).FirstChild);
//get the completed envelope
var envelope = doc.OuterXml;
//start the webrequest
//get the request object
var request = CreateWebRequest(statusServiceUrl);
//get the request stream and then get a writer on it
using (var stream = request.GetRequestStream())
using (var gz = new GZipStream(stream, CompressionMode.Compress))
using (var writer = new StreamWriter(gz))
{
//start by writing the soap envelope to the stream
writer.WriteLine(envelope);
writer.Close();
stream.Close();
}
//get the response
WebResponse response;
//let an exception get thrown up the stack
response = request.GetResponse();
//get the response stream, get a reader on it, and read the response as text
using (var responseStream = response.GetResponseStream())
using (var reader = new StreamReader(responseStream, Encoding.UTF8))
{
var responseText = reader.ReadToEnd();
//rip the one element (and children) we need out
var match = Regex.Match(responseText, @"<(?'prefix'[\w\d]*):ACABulkRequestTransmitterStatusDetailResponse.*<\/\k<prefix>:ACABulkRequestTransmitterStatusDetailResponse>");
return Deserialize<ACABulkRequestStatusService.ACABulkRequestTransmitterStatusDetailResponseType>(match.ToString());
}
}
private static string GetUniqueTransmissionId(Guid transmissionGuid, string tCCode)
{
return string.Format("{0}:SYS12:{1}::T", transmissionGuid, tCCode);
}
private static string GenerateWsuId(string prefix)
{
return string.Format("{0}-{1}", prefix, Guid.NewGuid().ToString().Replace("-", "").ToUpper());
}
private static void GenerateReference(string elementID, string inclusivePrefixList, SignedXmlWithId xSigned)
{
var reference = new Reference()
{
Uri = "#" + elementID
};
XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform();
env.InclusiveNamespacesPrefixList = inclusivePrefixList;
reference.AddTransform(env);
xSigned.AddReference(reference);
}
/// <summary>
/// creates a webrequest object and prefills some required headers and such
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
private static HttpWebRequest CreateWebRequest(string url)
{
//setup a web request with all the headers and such that the service requires
var webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ProtocolVersion = HttpVersion.Version11;
webRequest.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip");
webRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate");
webRequest.ContentType = "text/xml;charset=UTF-8";
webRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
webRequest.Headers.Add("SOAPAction", "RequestSubmissionStatusDetail");
webRequest.KeepAlive = true;
return webRequest;
}
/// <summary>
/// deserializes the xml string into an object
/// </summary>
/// <param name="xmlString"></param>
/// <returns></returns>
public static T Deserialize<T>(string xmlString) where T : class
{
//if the string is empty, just return null
if (xmlString.Length <= 0)
{
return null;
}
//create a serializer
var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
T output;
//create the reader that the serializer will read from, passing it the string
using (var reader = new System.IO.StringReader(xmlString))
{
//rebuild the list object
output = (T)serializer.Deserialize(reader);
}
//return the list
return output;
}
}
}
Вот соответствующая база XML:
<?xml version="1.0" encoding="utf-8" ?>
<soapenv:Envelope
xmlns:oas1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn="urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest"
xmlns:urn1="urn:us:gov:treasury:irs:ext:aca:air:7.0" xmlns:urn2="urn:us:gov:treasury:irs:common"
xmlns:urn3="urn:us:gov:treasury:irs:msg:acasecurityheader">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Timestamp wsu:Id="XXXXXXXXXXXXXXXXXX">
<wsu:Created>XXXXXXXXXXXXXXXXXX</wsu:Created>
<wsu:Expires>XXXXXXXXXXXXXXXXXX</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
<urn:ACABusinessHeader wsu:Id="XXXXXXXXXXXXXXXXXX" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<urn1:UniqueTransmissionId>
XXXXXXXXXXXXXXXXXX
</urn1:UniqueTransmissionId>
<urn2:Timestamp>XXXXXXXXXXXXXXXXXX</urn2:Timestamp>
</urn:ACABusinessHeader>
<urn3:ACASecurityHeader />
<wsa:Action>RequestSubmissionStatusDetail</wsa:Action>
</soapenv:Header>
<soapenv:Body>
<urn:ACABulkRequestTransmitterStatusDetailRequest version="1.0" wsu:Id="XXXXXXXXXXXXXXXXXX" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<urn1:ACABulkReqTrnsmtStsReqGrpDtl>
<urn2:ReceiptId>XXXXXXXXXXXXXXXXXX</urn2:ReceiptId>
</urn1:ACABulkReqTrnsmtStsReqGrpDtl>
</urn:ACABulkRequestTransmitterStatusDetailRequest>
</soapenv:Body>
</soapenv:Envelope>
Для этого основным изменением, которое мне нужно было внести в сгенерированные WSDL классы, было следующее:
[System.SerializableAttribute()]
[XmlRoot("ACABulkRequestTransmitterStatusDetailResponse", Namespace = "urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest")]
public class ACABulkRequestTransmitterStatusDetailResponseType
{
private ACABulkRequestTransmitterResponseType aCABulkRequestTransmitterResponseField;
private ACABulkReqTrnsmtStsRespGrpDtlType aCABulkReqTrnsmtStsRespGrpDtlField;
private string versionField;
public ACABulkRequestTransmitterStatusDetailResponseType()
{
this.versionField = "1.0";
}
[System.Xml.Serialization.XmlElementAttribute(Namespace = "urn:us:gov:treasury:irs:ext:aca:air:7.0", Order = 0)]
public ACABulkRequestTransmitterResponseType ACABulkRequestTransmitterResponse
{
get
{
return this.aCABulkRequestTransmitterResponseField;
}
set
{
this.aCABulkRequestTransmitterResponseField = value;
}
}
[System.Xml.Serialization.XmlElementAttribute(Namespace = "urn:us:gov:treasury:irs:ext:aca:air:7.0", Order = 1)]
public ACABulkReqTrnsmtStsRespGrpDtlType ACABulkReqTrnsmtStsRespGrpDtl
{
get
{
return this.aCABulkReqTrnsmtStsRespGrpDtlField;
}
set
{
this.aCABulkReqTrnsmtStsRespGrpDtlField = value;
}
}
[System.Xml.Serialization.XmlAttributeAttribute()]
public string version
{
get
{
return this.versionField;
}
set
{
this.versionField = value;
}
}
}
Добавление префикса ds - это то, что нарушает это. Элемент SignatureValue содержит хэш x509 сериализованного элемента SignedInfo (здесь имеет значение наличие согласованного пробела).
При изменении элемента SignedInfo аутентификация не будет выполнена, когда IRS сравнивает хэш SignatureValue, который вы отправляете, с их вычислением хэша SignedInfo, используя загруженный сертификат и связанный с предоставленным TCC.
Просто удалите свою модификацию элемента SignedInfo, и все должно быть хорошо. Меня устраивает.
Это изменение было внесено в вышеприведенный пост, который добавил дополнительную информацию в пост jstill. К сожалению, рецензенты отклонили это.
В дополнение к изменениям, внесенным в "Статус" Reference.cs
файл, я также должен был включить BulkExchangeFileType
а также IncludeFileType
Обновления, которые он сделал для представления Reference.cs
файл для того, чтобы получить Deserializer
метод функционировать частично.
Deserializer
метод вернет TransmissionStatusCd
и ReceiptId
элементы, как ожидается, однако, он не будет заполнять ErrorDataFile
элемент правильно.
Поскольку в настоящее время я не могу получить ErrorDataFile
объект заполнен правильно, я не использую ACABulkRequestTransmitterStatusDetailResponseType
объект для захвата ответа, возвращенного веб-службой состояния. Вместо этого я решил прочитать ResponseStream
в string
возразить и проанализировать (до) двух MIME
части ответа, и обработайте их по мере необходимости.
Дополнительные изменения в Status' Reference.cs
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34283")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "urn:us:gov:treasury:irs:common")]
public partial class BulkExchangeFileType : object, System.ComponentModel.INotifyPropertyChanged
{
private IncludeType includeField;
/// <remarks/>
[System.Xml.Serialization.XmlElement(Order = 0, Namespace = "http://www.w3.org/2004/08/xop/include")]
public IncludeType Include
{
get { return this.includeField; }
set
{
this.includeField = value;
this.RaisePropertyChanged("Include");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34283")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.w3.org/2004/08/xop/include")]
public partial class IncludeType : object, System.ComponentModel.INotifyPropertyChanged
{
private System.Xml.XmlNode[] anyField;
private string hrefField;
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
[System.Xml.Serialization.XmlAnyElementAttribute(Order = 0)]
public System.Xml.XmlNode[] Any
{
get { return this.anyField; }
set
{
this.anyField = value;
this.RaisePropertyChanged("Any");
}
}
/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute(DataType = "string")]
public string href
{
get { return this.hrefField; }
set
{
this.hrefField = value;
this.RaisePropertyChanged("href");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
Для тех, кто сталкивается со следующей ошибкой:
AIRMF3002 Отклоненная передача. Невозможно обработать ваш запрос из-за сбоя проверки в номере размера байта вложения.
Кажется, есть проблема на стороне IRS с размером, который они ожидают (согласно Документации) и размером, который они фактически принимают. Первоначально у меня был следующий код:
// Size in Bytes of File: This code returns the "Size" located on the File's Property Page.
// Result: TRANSMISSION REJECTED ON INCORRECT FILE SIZE!
manifestHeader.AttachmentByteSizeNum = new FileInfo(FormDataFilePath).Length.ToString();
Я заменил приведенный выше код следующим, и ошибка, которую я получал, была решена.
// Read the contents of the file, and retrieve the length of the content of the file itself..
// Result: TRANSMISSION WAS ACCEPTED USING THIS FILE SIZE.
manifestHeader.AttachmentByteSizeNum = File.ReadAllText(FormDataFilePath).Length.ToString();
Похоже, что веб-служба фактически ожидает размер содержимого файла, а не размер самого файла. Разница в размерах, относящихся к тестовым сценариям, составляла приблизительно 3 байта. Я предполагаю, что это потому, что получение размера файла добавляет некоторую дополнительную информацию, связанную с файлом, не относящуюся к фактическому содержимому.
Я уведомил IRS об этой проблеме относительно их документации.