HERBERS Excel-Forum - das Archiv

Thema: Http-Request (POST) für GraphQL API-Abfrage

Http-Request (POST) für GraphQL API-Abfrage
Marc
Hallo zusammen,

ich versuche mich seit Stunden daran einen HTTP-Request zu erstellen via VBA mit dem ich Daten von der freien API (GraphQL) der Deutschen Börse ziehen kann. Ich hab schon so ziemlich jede erdenkliche Variante probiert in bezug auf die Gestaltung des Request-Headers aber das Ergebnis ist immer das Gleiche.

Mein Code liefert zwar keine Fehlermeldung und ich bekomme eine Antwort vom Server aber ich bekomme trotzdem keine Daten. Dass die Daten verfügbar sind und geliefert werden habe ich bereits mit einem Python-Code getestet. Ich vermute, dass irgendetwas falsch ist an der Art, wie ich den Http-Header aufbaue bei meinem POST-Request. Ich bin noch nicht so fit mit HTTP-Requests und ich vermute, dass der Fehler trivial ist, aber ich komme nicht drauf woran es liegt.

Das ist mein Code:

Sub httppost()


Dim xmlhttp As Object, myurl As String, body As String, auth As String

Set xmlhttp = CreateObject("MSXML2.serverXMLHTTP")


myurl = "https://console.developer.deutsche-boerse.com/apis/accesstot7-referencedata/1.1.0/"

auth = "{ " & """X-DBP-APIKEY""" & " : " & """68cdafd2-c5c1-49be-8558-37244ab4f513""" & " }"

contype = """{ application/json }"""

xmlhttp.Open "POST", myurl, False

xmlhttp.setRequestHeader "Content-Type", contype

xmlhttp.setRequestHeader "Authorization", "Basic " + auth


body = """query {" & vbCrLf & _
"ProductInfos{" & vbCrLf & _
"data {" & vbCrLf & _
"ProductType" & vbCrLf & _
"}}}"""


xmlhttp.Send body

ThisWorkbook.Worksheets("Tabelle1").Range("A1").Value = xmlhttp.getAllResponseHeaders()


End Sub


Hier ist mal eine Beispielabfrage in Python, die das gewünschte Ergebnis problemlos liefert:

https://github.com/Finsinyur/EurexT7ReferenceData/blob/main/get_producttypes_eurex.py

Und hier der Link zur Dokumentation der Deutschen Börse:

https://console.developer.deutsche-boerse.com/apis/a518bf48-661d-468d-af6a-c107d0fc1511/api-details

Es wäre mega wenn jemand einen Tipp für mich hätte was an meinem HTTP-Header falsch ist.

Beste Grüße
Marc
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
Man möge es mir verzeihen, ich hatte eben versäumt noch den Output hinzuzufügen den mir die API liefert:

Http.responseText liefert folgendes Ergebnis:

/apis/accesstot7-referencedata/1.1.0

Ich habe jetzt auch realisiert, dass wohl die Bezeichnung meines Headers verkehrt war und habe dies wie folgt angepasst:

Sub httppost()


Dim xmlhttp As Object, myurl As String, body As String, auth As String

Set xmlhttp = CreateObject("MSXML2.serverXMLHTTP")


myurl = "https://console.developer.deutsche-boerse.com/apis/accesstot7-referencedata/1.1.0/"

auth = "{ " & """X-DBP-APIKEY""" & " : " & """68cdafd2-c5c1-49be-8558-37244ab4f513""" & " }"

MsgBox auth

contype = """{ application/json }"""


xmlhttp.Open "POST", myurl, False

xmlhttp.setRequestHeader "Content-Type", contype

xmlhttp.setRequestHeader auth, "Basic"

body = """query {" & vbCrLf & _
"ProductInfos{" & vbCrLf & _
"data {" & vbCrLf & _
"ProductType" & vbCrLf & _
"}}}"""


xmlhttp.Send body

ThisWorkbook.Worksheets("Tabelle1").Range("A3").Value = xmlhttp.responseText()

ThisWorkbook.Worksheets("Tabelle1").Range("A1").Value = xmlhttp.getAllResponseHeaders()


End Sub


Das Ergebnis bleibt leider das Gleiche.

AW: Http-Request (POST) für GraphQL API-Abfrage
Yal
Hallo Marc,

sehe ich das richtig, dass Du in einem öffentlichen Forum deine persönliche API-Key gepostet hast?

Wenn diese zu irgendetwas kostenpflichtig führt, würde ich sie umgehend blockieren lassen und eine neue erstellen.

Solche Forum für Ahnungslosen werden automatisiert gescannt. Von wem wohl...

VG
Yal
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
Hallo Yal,

Das siehst Du nicht richtig.

Das ist der freie API-Key der Deutschen Börse, den Du so auch auf deren Webseite vorfindest.

Den Link zur Doku hatte ich eingangs geposted.

VG
Marc
AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
Hi Yal,
das scheint ein "Demo-Key" zu sein. siehe hier:
https://console.developer.deutsche-boerse.com/apis/a518bf48-661d-468d-af6a-c107d0fc1511
So wirklich hilfreich ist das aber alles nicht für mich... Da müsst ich meinen IT-Master einschalten, aber der hat sicher besseres zu tun.
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
Irgendwas stimmt mit meinem "Authorization"-Header nicht, das ist mittlerweile klar.

Leider sehe ich keine Möglichkeit, wie ich sicherstellen kann, was das Request-Objekt tatsächlich genau übermittelt, also das Format.

Je nach Variante habe ich teilweise auch einen Fehlercode 400 "Invalid JSON PAYLOAD in graphqlauth error" (oder so ähnlich lautend ) erhalten.

Es ist vermutlich nur ein Detail. Auch ein Studium der Norm für den Authorization-Header hat mich nicht weiter gebracht.

AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
Also, ich denke, dass ich ein wenig weiter bin.
Jetzt müsste man den Output halt noch verarbeiten, wenn der das ist, was du wolltest.



Sub httppost()

'Verweise:
'Microsoft Scripting Runtime
'Microsoft XML, V6.0

Dim myurl As String, body As String

Dim xmlhttp As New MSXML2.XMLHTTP60

myurl = "https://api.developer.deutsche-boerse.com/accesstot7-referencedata-1-1-0/"

xmlhttp.Open "POST", myurl, False

Debug.Print myurl
xmlhttp.setRequestHeader "X-DBP-APIKEY", "68cdafd2-c5c1-49be-8558-37244ab4f513"
xmlhttp.setRequestHeader "Content-Type", "{application/json}"

body = "{ ""query"": ""query { ProductInfos" & _
"{data{ProductType}}}"" }"

xmlhttp.Send body

Debug.Print xmlhttp.ResponseText
Debug.Print xmlhttp.Status, xmlhttp.statusText

End Sub
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
@Oppawinni

MEGA!!!!!!!! Es läuft!!!!!!

Du hast mir so geholfen! Ich war echt am verzweifeln!

Tausend Dank!

LG
Marc

AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
Ja, so ein paar Zeilen Code können einen schon lange beschäftigen :)
Naja, aber so richtig fertig ist das ja nicht und etwas besser aufgeräumt könnte der Code schon auch sein.



Sub httppost()

'Verweise:
'Microsoft Scripting Runtime
'Microsoft XML, V6.0

Dim myurl As String, body As String, strResponse As String

Dim xmlhttp As New MSXML2.XMLHTTP60

myurl = "https://api.developer.deutsche-boerse.com/accesstot7-referencedata-1-1-0/"
body = "{ ""query"": ""query {ProductInfos{data{ProductType}}}"" }"

With xmlhttp
.Open "POST", myurl, False
.setRequestHeader "X-DBP-APIKEY", "68cdafd2-c5c1-49be-8558-37244ab4f513"
.setRequestHeader "Content-Type", "{application/json}"
.Send body
strResponse = .ResponseText
End With

'hier sollte man vielleicht noch den Status abfragen und im Fehlerfall mit Msg abbrechen.
'ansonsten müsste das JSON dann ggf. verarbeitet werden, also z.B. geparsed und ggf. in eine Tabelle geschrieben werden.

Debug.Print strResponse

End Sub
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
@Oppawinni

Ich gelobe Besserung! ;)

Einen JSON-Parser hab ich. Das Problem bestand tatsächlich nur darin diese Authorisierung ins richtige Format zu bekommen.

Wobei mein Body ja offensichtlich auch nicht korrekt formatiert war. Da hätte ich wahrscheinlich wieder Stunden mit gekämpft. =P

AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
Das mit dem early binding ist ja schön, solange man am Code arbeitet.
Ich würde das dann aber schon auf late binding umstellen also:

Dim xmlhttp As Object
Set xmlhttp = CreateObject("MSXML2.XMLHTTP.6.0")

Darf ich fragen, welchen Parser du verwendest ?
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
Jetzt bin ich etwas irritiert, denn ich war der Meinung was ich mache sei late Binding, da ich mit
Set httpreq = CreateObject("MSXML2.serverXMLHTTP")
ein Objekt erstelle.

Das ist ein JSON-Parser aus dem Netz. Der Schreiber hat ihn als besonders schnell und zuverlässig angepriesen. Das Teil erstellt ein Dictionary aus dem JSON-File und man kann die einzelnen Elemente dann über deren Index komfortabel ansprechen.

Der Parser kann hier heruntergeladen werden:

https://medium.com/swlh/excel-vba-parse-json-easily-c2213f4d8e7a



AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
Es ging darum, dass ich ja mit early binding gepostet hatte.

Der Parser scheint übrigens ganz ok. Ich weiß ja nicht, ob ich den mal brauche, aber es schadet ja nicht, den mal zu speichern.
Danke dafür.
AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
ich habe gerade noch ein wenig gespielt und meine, dass im body ein query überflüssig ist.
Das sollte genügen:
    body = "{ ""query"": ""{ProductInfos{data{ProductType}}}"" }"

AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
Vielleicht wäre das auch eine Lösung für dich:
https://www.cdata.com/kb/tech/graphql-odbc-excel-query.rst
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
Das hatte ich im Rahmen meiner Recherche tatsächlich auch entdeckt und das wäre im Grunde ideal.

Kostet aber Kohle und auf ein Abo hab ich keine Lust.

Da mach ich mir dann lieber Dir Mühe und lerne die GraphQL Queries um sie nach VBA zu übersetzen.

Aber tausend Dank! ;)
AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
Ja, es kostet.. Mühe herauszufinden wie das geht.
Ich hab mal in power query ne doofe url eingegeben, die etwas liefert, also sowas z.B. https://api.restful-api.dev/objects
dann hab ich den erweiterten Editor geöffnet und das eingegeben:

let
url = "https://api.developer.deutsche-boerse.com/accesstot7-referencedata-1-1-0/",
header = [#"Content-Type"="{application/json}", #"X-DBP-APIKEY"="68cdafd2-c5c1-49be-8558-37244ab4f513"],
Content = "{ ""query"": ""{ProductInfos{data{Product,ProductType}}}"" }",
webdata = Web.Contents(url, [Headers=header,Content = Text.ToBinary(content)]),
response = Json.Document(webdata)
in
response

Was halt etwa hier beschrieben wird:
https://community.fabric.microsoft.com/t5/Power-Query/Sending-a-POST-API-request-using-PowerQuery-on-Excel-PBI-with-a/td-p/2764875

Naja dann hab ich mich durch ein paar Einstellungen in Power Query gehangelt und dann hab im Prinzip auch das:
Userbild
Die komplette Abfrage
Oppawinni
Die sieht dann so aus (ich hatte im vorherigen Post das content einmal fälschlicher Weise groß geschrieben, Zeile 4) :
let
url = "https://api.developer.deutsche-boerse.com/accesstot7-referencedata-1-1-0/",
header = [#"Content-Type"="{application/json}", #"X-DBP-APIKEY"="68cdafd2-c5c1-49be-8558-37244ab4f513"],
content = "{ ""query"": ""{ProductInfos{data{Product,ProductType}}}"" }",
webdata = Web.Contents(url, [Headers=header,Content = Text.ToBinary(content)]),
response = Json.Document(webdata),
#"In Tabelle konvertiert" = Record.ToTable(response),
#"Erweiterte Value" = Table.ExpandRecordColumn(#"In Tabelle konvertiert", "Value", {"ProductInfos"}, {"Value.ProductInfos"}),
#"Erweiterte Value.ProductInfos" = Table.ExpandRecordColumn(#"Erweiterte Value", "Value.ProductInfos", {"data"}, {"Value.ProductInfos.data"}),
#"Value ProductInfos data" = #"Erweiterte Value.ProductInfos"{0}[Value.ProductInfos.data],
#"In Tabelle konvertiert1" = Table.FromList(#"Value ProductInfos data", Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Erweiterte Column1" = Table.ExpandRecordColumn(#"In Tabelle konvertiert1", "Column1", {"Product", "ProductType"}, {"Column1.Product", "Column1.ProductType"})
in
#"Erweiterte Column1"
AW: Die komplette Abfrage
Marc
Hi Oppawinni =)

Du hast Dir jetzt praktisch die Mühe gemacht und eine Excel-Query erstellt, die das Gleiche macht, was CData in ihrem Produkt anbieten?

Das ist stark! Tausend Dank!

Ich werde vermutlich trotzdem nicht drum herum kommen, mich intensiv mit GraphQL zu befassen, da man Details zu den Queries nur über _schema-Abfragen bekommt.

Aber trotzdem top!

AW: Die komplette Abfrage
Marc
@OppaWinni

Mittlerweile hab ich es geschafft mir in einem GraphQL-Editor, die Query zu basteln, die ich haben wollte. Diese gibt mir alle Basiswerte (Underlyings) aus, auf die Stock Options handelbar sind an der Eurex, mitsamt der Währung, dem Namen der Aktiengesellschaft und ISIN der Aktie.

Dabei habe ich einen Filter eingebaut in die GraphQL-Abfrage, der nach dem ProductType "Single Stock Options" filtert. Funktioniert in GraphQL selbst ganz wunderbar. Nun wollte ich den Request in VBA nachbilden, aber da bekomme ich einen Fehler wegen dem Filter.

Das ist mein Request-body:

body = "{""query"": ""query{ProductInfos(filter:{ProductType:{eq:""SINGLE STOCK OPTIONS""}}){date data{UnderlyingName, UnderlyingISIN, Product, ProductType, Currency}}}"" }"


Damit bekomme ich vom Server die folgende Fehlermeldung:

obj.errors(0).message --> Invalid JSON payload in GraphQLAuth POST request.
obj.errors(0).errorType --> MalformedHttpRequestException


Wenn ich den Filter weglasse, dann funktioniert die Abfrage. Der Request-body sieht dann so aus:

body = "{""query"": ""query{ProductInfos{date data{UnderlyingName, UnderlyingISIN, Product, ProductType, Currency}}}"" }"


Hast Du eine Idee, weshalb der Filter Probleme macht?
AW: Die komplette Abfrage
Marc
Hab gerade einen Post in einem anderen Forum gefunden, der die richtige Syntax für die Verwendung von HTTP-Requests im Zusammenhang mit GraphQL-Queries geliefert hat:

body = "{""query"": ""{ProductInfos(filter:{ProductType:{eq:\""SINGLE STOCK OPTIONS\""}}){date data{UnderlyingName, UnderlyingISIN, Product, ProductType, Currency}}}"" }"


Man muss ja auch mal Glück haben. =D

\""SINGLE STOCK OPTIONS\""

Die Backslashs waren die Lösung!
AW: Die komplette Abfrage
Oppawinni
Na, aber da hast du dir das nicht angeschaut,
wo ich das Beispiel von der API - Site gepostet hatte, da war auch ein Filter dabei..
Naja, Hauptsachte es läuft.
AW: Die komplette Abfrage
Marc
Doch doch, ich hatte in der Tat zunächst in Deinen vorherigen Posts geschaut, ob Du irgendwo einen Filter verwendet hattest, aber offenbar war es mir entgangen. =D
AW: Http-Request (POST) für GraphQL API-Abfrage
Oppawinni
ich wollte halt mal das Beispiel auf der API - Site machen und das hatte auch nicht gleich funktioniert:


body = "{ ""query"": ""{Contracts(filter:{Product:{eq: \""FDAX\""}}) {date,data {ISIN,Contract,ExpirationDate}}}"" }"
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
Zwischenzeitlich habe ich realisiert, dass die Antwort eine Collection sein muss und habe den Code entsprechend angepasst:

Sub httppost()


Dim xmlhttp As Object, myurl As String, body As String, auth As String, coll

Set coll = New Collection

Set xmlhttp = CreateObject("MSXML2.serverXMLHTTP")

myurl = "https://console.developer.deutsche-boerse.com/apis/accesstot7-referencedata/1.1.0/"

auth = "{ " & """X-DBP-APIKEY""" & " : " & """68cdafd2-c5c1-49be-8558-37244ab4f513""" & " }"

xmlhttp.Open "POST", myurl, False

xmlhttp.setRequestHeader "Content-Type", "application/json"

xmlhttp.setRequestHeader auth, "Basic"

body = "query {" & vbCrLf & _
"ProductInfos{" & vbCrLf & _
"data {" & vbCrLf & _
"ProductType" & vbCrLf & _
"}}}"

MsgBox body

xmlhttp.Send body

coll = xmlhttp.responseText

End Sub


Das Collection-Objekt enthält jedoch nach wie vor nur den folgenden String: "/apis/accesstot7-referencedata/1.1.0"
AW: Http-Request (POST) für GraphQL API-Abfrage
Marc
Nun habe ich in der http.response einen "308 Premanent Redirect" entdeckt und festgestellt, dass schon die verwendete URL nicht korrekt war! Der Python-Aufruf verwendete tatsächlich die korrekte URL, daher hat der auch funktioniert! (Wie schön, dass man bei der Deutschen Börse die eigene Webseite so aktuell hält! Unglaublich!)

Mit der neuen URL bekomme ich nun immerhin eine "401 Unauthorized"-Meldung.

Jetzt liegt es wohl tatsächlich am Http.Header.

Mein Code:

Sub httppost()


Dim xmlhttp As Object, myurl As String, body As String, auth As String, coll

Set coll = New Collection

Set xmlhttp = CreateObject("MSXML2.serverXMLHTTP")

myurl = "https://api.developer.deutsche-boerse.com/prod/accesstot7-referencedata/1.1.0/"

auth = "{ " & """X-DBP-APIKEY""" & " : " & """68cdafd2-c5c1-49be-8558-37244ab4f513""" & " }"


xmlhttp.Open "POST", myurl, False

xmlhttp.setRequestHeader "Content-Type", "application/json"

xmlhttp.setRequestHeader auth, "Basic"

body = "query {" & vbCrLf & _
"ProductInfos{" & vbCrLf & _
"data {" & vbCrLf & _
"ProductType" & vbCrLf & _
"}}}"

MsgBox body

xmlhttp.Send body

coll = xmlhttp.responseText

ThisWorkbook.Worksheets("Tabelle1").Range("A1").Value = xmlhttp.getAllResponseHeaders

ThisWorkbook.Worksheets("Tabelle1").Range("A3").Value = xmlhttp.responseText

End Sub