![]() |
Soap-Service: XML-Fehler
Hallo zusammen,
Ich bastele an einem SOAP-Client (MyDHLAPI von DHL Express). Beim Aufruf der aus der wdsl importieren Function getRateRequest erhalte ich diesen Fehler: Zitat:
Delphi-Quellcode:
In HTTPRIOBeforeExecute und HTTPRIOAfterExecute cheche ich, was in SOAPRequest bzw. in SOAPResponse drin steht. SOAPRequest ist in Ordnung, SOAPResponse ist leer.
procedure TDHLExpress.DoRateRequest;
var Request:RateRequest; // aus der wdsl Response:RateResponse; // aus der wdsl DHLExpressService:gblExpressRateBook; // aus der wdsl DhlExpressHeader:TDHLExpressSoapHeader; RIO : THTTPRIO; begin RIO := THTTPRIO.Create(nil); RIO.OnAfterExecute :=HTTPRIOAfterExecute; RIO.OnBeforeExecute:=HTTPRIOBeforeExecute; DHLExpressService:=GetgblExpressRateBook(false, '', RIO); DhlExpressHeader :=TDHLExpressSoapHeader.Create; // Für Authentication DHLExpressHeader.User:=fUser; DHLExpressHeader.Signature:=fSignature; Request:=RateRequestCreate; // Hier wird der Request zusammengebaut try (DHLExpressService as ISOAPHeaders).Send(DHLExpressHeader); Response:=DHLExpressService.getRateRequest(Request); // Hier kommt der Fehler finally Request.Free; DhlExpressHeader.Free; end; end; In einem ähnlichen Fall mit einer anderen API von DHL (nicht Express) funktioniert alles nach diesem Schema bestens. Was könnte da passiert sein? |
AW: Soap-Service: XML-Fehler
Also der Fehler "Ein XML-Dokument muss ein Element der obersten Ebene enthalten." kommt wahrscheinlich nur, weil der SOAP-Server eine leere bzw. gar keine Antwort gesendet hat. Das ist quasi so, als würde man eine leere Textdatei als XML-Dokument einlesen wollen.
Zur Lösungsfindung würde ich zuerst die komplette Aufruf-URL in den Browser werfen und sehen, ob da eine Antwort kommt. |
AW: Soap-Service: XML-Fehler
Delphi/Indy-Update?
Ich hab grade den Fall bei einer REST-Komponente. Alter Code läuft, aber im neuen Delphi nicht. Die Anfrage/Response ist anders, daher liefert der REST-Server nichts, bzw. was Falsches. Wir wollen JSON, aber jetzt sendet das Ding teilweise XML zurück, bzw. auch gern mal leere Daten.
Code:
[ XE ]
GET /user/getErpPublicKey HTTP/1.1 Content-Type: application/json; charset=UTF-8 Host: localhost:9999 Accept: text/html, */* Accept-Encoding: identity User-Agent: Mozilla/3.0 (compatible; Indy Library) [ 11 ] GET /user/getErpPublicKey HTTP/1.1 Content-Type: application/json; charset=UTF-8 Host: localhost:9999 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/3.0 (compatible; Indy Library) [ 11 + IdHTTP.Request.Accept := 'application/json'; ] GET /user/getErpPublicKey HTTP/1.1 Content-Type: application/json; charset=UTF-8 Host: localhost:9999 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/3.0 (compatible; Indy Library) |
AW: Soap-Service: XML-Fehler
Bei der Arbeit mit Indy hilft es kolossal hier
Delphi-Quellcode:
was aktuelleres zuzuweisen.
User-Agent: Mozilla/3.0 (compatible; Indy Library)
Z. B. das, was Dir diese ![]() Etliche Webserver liefern beim "OriginalIndyUserAgent" keine Antwort oder nur, dass ihnen der Browser etwas zu alt ist und man doch lieber was Neueres nehmen sollte. |
AW: Soap-Service: XML-Fehler
Zitat:
Alternativ zum Debugger könnte ein loggender HTTP Proxy wie Fiddler2 - ![]() Update: Zitat:
|
AW: Soap-Service: XML-Fehler
[QUOTE=mjustin;1518161]
Zitat:
Zitat:
|
AW: Soap-Service: XML-Fehler
Ich glaube, dieses Postman bringt mich ein Stück weiter:
Wenn ich meinen SOAPRequest damit abschicke, kommt auch dort kein Response an. Aber: Mein SOAPRequest enthält einen Header zur Authentifizierung:
Code:
Wenn ich den weglasse, kommt immerhin ein Response an, der logischerweise besagt, dass man Unauthorized ist:
<SOAP-ENV:Header>
<ns2:Security env:mustUnderstand="true"> <ns2:UsernameToken ns3:Id="UsernameToken-4C578AF5E8CBB3162A14952041422019"> <ns2:Username>******</ns2:Username> <ns2:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">******</ns2:Password> <ns2:Nonce EncodingType=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">SC4DEqvj1tX0yuY6sFXMvQ==</ns2:Nonce> </ns2:UsernameToken> </ns2:Security> </SOAP-ENV:Header>
Code:
Also stimmt da anscheinend etwas mit diesem Header nicht.
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header></env:Header> <env:Body> <env:Fault> <faultcode>env:Server</faultcode> <faultstring></faultstring> <detail fault:type="Unauthorized" xmlns:fault="http://www.dhl.com/soapfaults"></detail> </env:Fault> </env:Body> </env:Envelope> |
AW: Soap-Service: XML-Fehler
Code:
Was mir auffällt: ... Password Type="" ... und EncodingType="", also ein kodiertes " am Anfang, aber kein schliessendes " am Ende des Attributs.
<ns2:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">******</ns2:Password>
<ns2:Nonce EncodingType=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">SC4DEqvj1tX0yuY6sFXMvQ== Sind diese " am Anfang des Attributs so vorgesehen? |
AW: Soap-Service: XML-Fehler
Nein, die " waren falsch. Die gehören da nicht hin. Daran liegt es aber nicht. Ohne erhalte ich das selbe Ergebnis.
|
AW: Soap-Service: XML-Fehler
Vielleicht mit Anderen ähnlichen Problemen vergleichen?
![]() Es gibt da z.B. so Hinweise wie Zitat:
Einige SOAP-Services kann man auf JSON umstellen z.B.
Delphi-Quellcode:
accept: application/json,*/*
Dort sieht das Ganze eventuell viel einfacher und nicht so vernamespaced aus. |
AW: Soap-Service: XML-Fehler
Danke für die Tips.
Ich probiere momentan etwas mit SoapUI ![]() |
AW: Soap-Service: XML-Fehler
Auf der
![]() Wenn ich es mit mORMot umsetze, stehen mir viele Wege offen. Von holzschnittartig bis fein ziseliert. Deine Daten bekommst du am einfachsten mit einem TWinHttp.Get Aufruf. mORMot beherrscht SSL/TLS. Im Folgenden verwende ich Curl, weil der Quellcode aus dem DHL Beispiel kopiert werden kann. Der JSON Response wird mit der Funktion RecordLoadJson in eine Record Struktur deserialisiert. Möglich wäre auch ein Objekt, oder ohne vorherige Definition einer Struktur mit der Funktion InitJson ein DocVariant. Dies ist vorteilhaft, wenn nur einige Werte von Interesse sind. Mit der Funktion GetValueByPath ist ein Direktzugriff möglich. Informationen zu mORMot mit Curl findest du im ![]() Abgebildet ist der Fall Rating GET /rates (Retrieve Rates for a one piece Shipment):
Delphi-Quellcode:
Disclaimer: ohne Gewähr
uses
mormot.core.base, mormot.core.data, mormot.core.text, mormot.core.json, mormot.core.buffers, mormot.core.variants, mormot.core.os, mormot.lib.curl, mormot.net.client; type TRateRequestResult = packed record products: array of record productName: RawUtf8; productCode: RawUtf8; localProductCode: RawUtf8; localProductCountryCode: RawUtf8; networkTypeCode: RawUtf8; isCustomerAgreement: Boolean; weight: record volumetric: Double; provided: Double; unitOfMeasurement: RawUtf8; end; totalPrice: array of record currencyType: RawUtf8; priceCurrency: RawUtf8; price: Currency; end; totalPriceBreakdown: array of record currencyType: RawUtf8; priceCurrency: RawUtf8; priceBreakdown: array of record typeCode: RawUtf8; price: Currency; end; end; detailedPriceBreakdown: array of record currencyType: RawUtf8; priceCurrency: RawUtf8; breakdown: array of record name: RawUtf8; serviceCode: RawUtf8; localServiceCode: RawUtf8; typeCode: RawUtf8; serviceTypeCode: RawUtf8; price: Currency; isCustomerAgreement: Boolean; isMarketedService: Boolean; isBillingServiceIndicator: Boolean; priceBreakdown: array of record priceType: RawUtf8; typeCode: RawUtf8; price: Currency; rate: Double; basePrice: Currency; end; end; end; pickupCapabilities: record nextBusinessDay: Boolean; localCutoffDateAndTime: TDateTime; GMTCutoffTime: TDateTime; pickupEarliest: TDateTime; pickupLatest: TDateTime; originServiceAreaCode: RawUtf8; originFacilityAreaCode: RawUtf8; pickupAdditionalDays: Integer; pickupDayOfWeek: Integer; end; deliveryCapabilities: record deliveryTypeCode: RawUtf8; estimatedDeliveryDateAndTime: TDateTime; destinationServiceAreaCode: RawUtf8; destinationFacilityAreaCode: RawUtf8; deliveryAdditionalDays: Integer; deliveryDayOfWeek: Integer; totalTransitDays: Integer; end; items: array of record number: Integer; breakdown: array of record name: RawUtf8; serviceCode: RawUtf8; localServiceCode: RawUtf8; typeCode: RawUtf8; serviceTypeCode: RawUtf8; price: Currency; priceCurrency: RawUtf8; isCustomerAgreement: Boolean; isMarketedService: Boolean; isBillingServiceIndicator: Boolean; priceBreakdown: array of record priceType: RawUtf8; typeCode: RawUtf8; price: Currency; rate: Double; basePrice: Currency; end; end; end; pricingDate: TDateTime; end; exchangeRates: array of record currentExchangeRate: Double; currency: RawUtf8; baseCurrency: RawUtf8; end; warnings: array of RawUtf8; end; var hnd: TCurl; res: TCurlResult; headers: Pointer; responseData: RawByteString; responseSize: Int64; requestResult: TRateRequestResult; begin if not CurlIsAvailable then Exit; //=> hnd := curl.easy_init; if hnd <> Nil then begin curl.easy_setopt(hnd, coSSLVerifyPeer, 0); curl.easy_setopt(hnd, coSSLVerifyHost, 0); curl.easy_setopt(hnd, coWriteFunction, @CurlWriteRawByteString); curl.easy_setopt(hnd, coWriteData, @responseData); var docQuery: TDocVariantData; docQuery.Init(JSON_FAST_EXTENDED, dvObject); docQuery.U['accountNumber'] := '123456789'; docQuery.U['originCountryCode'] := 'CZ'; docQuery.U['originCityName'] := 'Prague'; docQuery.U['destinationCountryCode'] := 'CZ'; docQuery.U['destinationCityName'] := 'Prague'; docQuery.I['weight'] := 5; docQuery.I['length'] := 15; docQuery.I['width'] := 10; docQuery.I['height'] := 5; docQuery.U['plannedShippingDate'] := '2020-02-26'; docQuery.B['isCustomsDeclarable'] := False; docQuery.U['unitOfMeasurement'] := 'metric'; curl.easy_setopt(hnd, coURL, Pointer(docQuery.ToUrlEncode('https://api-mock.dhl.com/mydhlapi/rates'))); var docHeader: TDocVariantData; docHeader.Init(JSON_FAST_EXTENDED, dvObject); docHeader.U['Message-Reference'] := 'd0e7832e-5c98-11ea-bc55-0242ac13'; docHeader.U['Message-Reference-Date'] := 'Wed, 21 Oct 2015 07:28:00 GMT'; docHeader.U['Plugin-Name'] := ''; docHeader.U['Plugin-Version'] := ''; docHeader.U['Shipping-System-Platform-Name'] := ''; docHeader.U['Shipping-System-Platform-Version'] := ''; docHeader.U['Webstore-Platform-Name'] := ''; docHeader.U['Webstore-Platform-Version'] := ''; headers := Nil; var headerField: TDocVariantFields; headers := curl.slist_append(headers, Pointer(FormatUtf8('accept: %', [JSON_CONTENT_TYPE]))); headers := curl.slist_append(headers, Pointer(FormatUtf8('Authorization: Basic %', ['ZGVtby1rZXk6ZGVtby1zZWNyZXQ=']))); for headerField in docHeader.Fields do begin if headerField.Value <> Nil then headers := curl.slist_append(headers, Pointer(FormatUtf8('%: %', [headerField.Name^, headerField.Value^]))); end; curl.easy_setopt(hnd, coHttpHeader, headers); curl.easy_setopt(hnd, coCustomRequest, RawUtf8('GET')); res := curl.easy_perform(hnd); if res = crOk then begin FileFromString(responseData, MakePath([Executable.ProgramFilePath, 'ResponseData.json'])); curl.easy_getinfo(hnd, ciSizeDownloadT, responseSize); ShowMessage(Format('Download completed: %s', [KB(responseSize)])); if RecordLoadJson(requestResult, responseData, TypeInfo(TRateRequestResult)) then ShowMessage(Utf8ToString(RawUtf8ArrayToCsv(requestResult.warnings, sLineBreak))); end else ShowMessage(Format('Curl told us %d (%s)', [Ord(res), curl.easy_strerror(res)])); curl.slist_free_all(headers); curl.easy_cleanup(hnd); end; end; Auch für Query und Header Teil musst du keinen DocVariant verwenden, sondern kannst es als Record oder Objekt abbilden und mit UrlEncode/ToCSV Funktionen aufbereiten. Bis bald... Thomas |
AW: Soap-Service: XML-Fehler
Zitat:
|
AW: Soap-Service: XML-Fehler
Falls eine OpenAPI Doc vorliegt für den REST Service, kannst Du auch diese Objekte automatisch generieren.
|
AW: Soap-Service: XML-Fehler
Zitat:
![]() (Gefunden in ![]() |
AW: Soap-Service: XML-Fehler
Der genannte Generator erzeugt OpenApi docs.Gesucht ist ja der umgekehrte Weg, aus einer OpenAPI Definition Delphi Code zu erstellen.
Sorry, hatte den verwechselt, es sollte gehen. |
AW: Soap-Service: XML-Fehler
So, jetzt funktioniert es.
Der Fehler war, dass im XML ein Namespace nicht definiert war. Dass ist passiert, weil ich den <SOAP-ENV:Header> über einen TSoapHeader selbst ins XML eingefügt habe. Ansonsten erzeugt SOAP das XML ja automatisch. Daraufhin der der Server dann überhaupt nichts mehr zurückgegeben. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:23 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz