Abrufen von JSON Daten von einer Web-API

  • VB.NET

Es gibt 30 Antworten in diesem Thema. Der letzte Beitrag () ist von kafffee.

    Abrufen von JSON Daten von einer Web-API

    Hallo liebe Community,

    Bin gerade dabei mich reinzufuchsen in das Abfragen eines Web-API Endpoints mit einem VB-Clientprogramm und hoffe ich bin hier im richtigen Unterforum.

    Die Docs der Web-API findet ihr hier:

    getsongbpm.com/api

    Ich hab im Internet mal bisssle gesucht und das hier gefunden:

    VB.NET-Quellcode

    1. Imports System.Net.Http
    2. Public Class Form1
    3. Shared client As HttpClient = New HttpClient()
    4. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    5. Main()
    6. End Sub
    7. Private Shared Async Function Main() As Task
    8. ' Call asynchronous network methods in a try/catch block to handle exceptions.
    9. Try
    10. Dim response As HttpResponseMessage = Await client.GetAsync("curl -X GET https://api.getsongbpm.com/search/?api_key=YOURAPIKAYHERE8&type=artist&lookup=metallica")
    11. response.EnsureSuccessStatusCode()
    12. Dim responseBody As String = Await response.Content.ReadAsStringAsync()
    13. Form1.TextBox1.Text = responseBody
    14. Debug.WriteLine("fertig")
    15. Catch e As HttpRequestException
    16. Debug.WriteLine(Environment.NewLine & "Exception Caught!")
    17. Debug.WriteLine("Message :{0} ", e.Message)
    18. End Try
    19. End Function
    20. End Class


    Es passiert aber rein gar nichts, wenn ich den Code ausführe, nicht mal ne Fehlermeldung, aber "fertig" wird ausgegeben...

    Wie gesagt bin in dem Thema kompletter Anfänger. Benutze ich überhaupt die richtige Klasse?
    Weiss jemand obs zu dem Thema irgendwo ein gutes Tutorial gibt oder kann mir anders irgendwie helfen? Ich weiss ehrlich gesagt nicht nach welchem Thema ich genau suchen soll...

    kafffee schrieb:

    VB.NET-Quellcode

    1. Form1.TextBox1.Text = responseBody
    Ist Form1 der Name der Klasse oder der Name einer Variablen von einem von Form abgeleiteten Typ?
    Funktioniert das ganze synchron?
    Ist der Abfragestring korrekt? https://api.getsongbpm.com/search/?api_key=YOURAPIKAYHERE8&type=artist&lookup=metallica bringt bei mir den Fehler
    {"error":"Invalid API Key, or inactive."}
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Ist Form1 der Name der Klasse oder der Name einer Variablen von einem von Form abgeleiteten Typ?

    Das Programm ist nicht grösser als das, was du hier siehst. Ich hab da nur Form1 davorgeknallt, weil die Ansage kam:
    Auf einen Instanzmember einer Klasse kann nicht ohne explizite Instanz einer Klasse von einer/m freigegebenen Methode/Member aus verwiesen werden.

    RodFromGermany schrieb:

    Funktioniert das ganze synchron?

    Weiss ich noch nicht, wie das geht...

    RodFromGermany schrieb:

    Ist der Abfragestring korrekt?

    Also wenn bei dir genau diese Meldung kommt, dann simmt schon mal irgendwas...
    Statt YOURAPIKEYHERE muss man natürlich seinen API Key eingeben, aber den kann ich hier jetzt schlecht posten.

    Wie hast du das gemacht? Ich hab statt Form1.Textbox1.Text mal Debug.WriteLine genommen, da passiert ebenfalls nichts...

    kafffee schrieb:

    Auf einen Instanzmember einer Klasse kann nicht ohne explizite Instanz einer Klasse von einer/m freigegebenen Methode/Member aus verwiesen werden.
    Dann nimm mal das Shared aus der Deklaration der Async Function Main() raus.
    Der Prozedur-Name Main() wird vom System verwendet, ändere den mal.
    Probier das ganze mal mit WebRequest: docs.microsoft.com/de-de/dotne…sing-the-webrequest-class
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hab jetzt Folgendes:

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Net
    3. Public Class Form1
    4. Public Sub HoleBPMDaten()
    5. Dim AnfZeichen As String = """"
    6. Dim request As WebRequest =
    7. WebRequest.Create("https://api.getsongbpm.com/search/?api_key=xxxxxxxxx&type=artist&lookup=green+day")
    8. ' If required by the server, set the credentials.
    9. 'request.Credentials = CredentialCache.DefaultCredentials
    10. ' Get the response.
    11. Dim response As WebResponse = request.GetResponse()
    12. ' Display the status.
    13. Console.WriteLine(CType(response, HttpWebResponse).StatusDescription)
    14. ' Get the stream containing content returned by the server.
    15. ' The using block ensures the stream is automatically closed.
    16. Using dataStream As Stream = response.GetResponseStream()
    17. ' Open the stream using a StreamReader for easy access.
    18. Dim reader As New StreamReader(dataStream)
    19. ' Read the content.
    20. Dim responseFromServer As String = reader.ReadToEnd()
    21. ' Display the content.
    22. Debug.WriteLine(responseFromServer)
    23. End Using
    24. ' Clean up the response.
    25. response.Close()
    26. End Sub
    27. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    28. HoleBPMDaten()
    29. End Sub
    30. End Class


    Da bekomm ich in Zeile 8 diesen Fehler:
    Der Remoteserver hat einen Fehler zurückgegeben: (403) Unzulässig.

    Wenn ich stattdessen aber mache:

    VB.NET-Quellcode

    1. Dim AnfZeichen As String = """"
    2. Dim request As WebRequest =
    3. WebRequest.Create("curl -X GET " & AnfZeichen & "https://api.getsongbpm.com/search/?api_key=xxxxxxxxxxxxx&type=artist&lookup=green+day" & AnfZeichen)


    Kommt der Fehler:
    URI Schema ist ungültig....

    Kein Plan was es mit diesem curl -X GET auf sich hat, hab wie gesagt null Erfahrungwerte...
    @kafffee Ich hab noch keine Webseiten partiell ausgelesen, aber in "curl -X GET " sind immer noch "lose" Leerzeichen drinne.
    Was musst Du in Deinem Browser aus- bzw. eingeben, damit das funktioniert?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Das ist keine normale Website die man da bekommt sondern ein String im JSON Format. Schau dir dazu mal den Weblink in Post1 von mir an, das ist die Dokumentation davon.

    Im Screenshot hab ichs dir schon mal rausgesucht:
    Bilder
    • Screenshot_20220616-144732.png

      127,32 kB, 1.920×1.080, 82 mal angesehen
    Also wenn ich im Internet nach C# webrequest get suche, dann find ich, dass man das anders machen muss:

    VB.NET-Quellcode

    1. WebRequest.Create(URL)
    2. WebRequest.Method = "GET"
    3. WebRequest.GetResponse()


    Der Code muss also entsprechend umgeschrieben werden, um derm/der API klarzumachen, was man will.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @VaporiZed

    Wenn ich .Method auf "GET" festlege, kommt immer noch dieser Fehler Nummer 403: Unzulässig

    Wenn ich "curl -X GET" nehme, dann kommt:
    NULL oder leere Methoden können nicht auf Anforderung festgelegt werden. Parametername value

    Liegt wahrscheinlich daran, dass GET irgendwie als festgelegter Methodenname existiert. Die Docs sagen mir dazu Folgendes: s. Screenshot
    Bilder
    • Screenshot_20220616-153002.png

      145 kB, 1.920×1.080, 76 mal angesehen
    naja, soweit ich verstehe, musst Du bei Method eben eintragen: curl -X GET und Dein Api Key und das ganze Gesums. Also:

    VB.NET-Quellcode

    1. Dim request As WebRequest = WebRequest.Create("https://api.getsongbpm.com")
    2. request.Method = "curl -X GET " & AnfZeichen & "https://api.getsongbpm.com/search/?api_key=xxxxxxxxxxxxx&type=artist&lookup=green+day" & AnfZeichen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    kafffee schrieb:

    Wie hast du das denn gemacht?
    Ich habe besagten String in die Start-Zeile vom Firefox eingetragen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ja, die Doku lesen!

    Responses
    On success, the HTTP status code in the response header is 200 OK and the response body contains an array of values in JSON format.
    On error, the header status code is an error code and the response body contains an error.

    Wäre nun zu prüfen, was für ein Fehler gemeldet wird. Stell die request.Method auch auf "GET".
    @kafffee
    Ich hab zumindest einen Hinweis gefunden, hab's grad auch mal versucht, Cloudflare blockt den Request. Warum kann ich noch nicht sagen, aber ich probiere es morgen nochmal. Ich bekomme im 403 Fehlerfall jedenfalls eine Webseite von Cloudfare ausgespielt, das wird bei dir auch der Fall sein, speicher das mal als HTML Dokument und sieh dir das im Webbrowser an, ist einfacher zu lesen. Du könntest die Empfehlung welche dann zu lesen ist in die Tat umsetzen, die haben die API gebaut schützen sie mit Cloudflare, die sollten dir sagen können was fehlt, ich vermute ein Header, mit Cookies hab ich schon probiert. Du kannst mit einem Webbrowser Addon mitschneiden und nachlesen, was der Webbrowser sendet, notfalls würde ich Wireshark nutzen und den Verkehr genau untersuchen.
    Such mal nach einem HTTP Live Header Addon für deinen Browser.

    Aber ich denke das beste wird sein, der Empfehlung von Cloudflare folge zu leisten

    VB.NET-Quellcode

    1. Imports System.Collections.Specialized
    2. Imports System.IO
    3. Imports System.Net
    4. Public Class Form1
    5. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    6. GetData("https://api.getsongbpm.com/search/", "MEIN_KEY_GIBST_NICHT", New NameValueCollection From {{"type", "artist"}, {"lookup", "metallica"}})
    7. End Sub
    8. Private Sub GetData(baseUrl As String, apiKey As String, params As NameValueCollection)
    9. Dim request As HttpWebRequest = DirectCast(WebRequest.Create(baseUrl & BuildParams(apiKey, params)), HttpWebRequest)
    10. request.AutomaticDecompression = DecompressionMethods.Deflate Or DecompressionMethods.GZip
    11. Try
    12. Using response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
    13. Using streamreader As New StreamReader(response.GetResponseStream())
    14. ResultOutput(streamreader.ReadToEnd())
    15. End Using
    16. End Using
    17. Catch ex As WebException
    18. Using response As HttpWebResponse = DirectCast(ex.Response, HttpWebResponse)
    19. Using streamreader As New StreamReader(response.GetResponseStream())
    20. ResultOutput(streamreader.ReadToEnd(), True)
    21. End Using
    22. End Using
    23. End Try
    24. End Sub
    25. Private Function BuildParams(apiKey As String, params As NameValueCollection) As String
    26. Dim urlParams As String = $"&{apiKey}"
    27. For Each param As String In params
    28. urlParams &= $"&{param}={params(param)}"
    29. Next
    30. Return urlParams
    31. End Function
    32. Public Sub ResultOutput(result As String, Optional isError As Boolean = False)
    33. If isError Then
    34. MessageBox.Show(Me, result, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
    35. Else
    36. MessageBox.Show(Me, result, "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information)
    37. End If
    38. End Sub
    39. End Class

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „BitBrösel“ ()

    Hallo,
    ich erwähne es immer wieder, wenn ich es sehe. WebRequest soll nicht mehr verwendet werden. Die Vorgehensweise aus Post #1 mit dem HttpClient war schon die korrekte.
    CURL ist ein Kommandozeilenprogramm, um damit Http-Anfragen durchzuführen. Wird meistens zum Testen oder in irgendwelchen (Kommandozeilen-)Scripten verwendet. Der Part ist also schon mal überflüssig in deiner Uri an den HttpClient.
    Der HttpClient sollte in einem Using-Block verwendet werden.

    Beispiel

    VB.NET-Quellcode

    1. ​Async Function FindByArtist(ArtistName As String) As Task(Of String)
    2. Dim Uri = "https://api.getsongbpm.com/search/?api_key=xxxxxxxxx&type=artist&lookup=" + ArtistName
    3. Dim responseString = String.Empty
    4. Using client As New HttpClient
    5. Try
    6. responseString = Await client.GetStringAsync(Uri)
    7. ' todo: etwas mit dem json machen und die Funktion ein Objekt zurückgeben lassen und kein String
    8. Catch ex As Exception
    9. Console.WriteLine(ex.Message)
    10. End Try
    11. End Using
    12. Return responseString
    13. End Function


    Ich habe kein API-Schlüssel um das zu testen. Wenn deine Aufrufe funktionieren, solltest du später, um das ganze deutlich einfacher zu machen, die in der Doku beschriebenen Json-Antworten als Klassen anlegen und die Erweiterungsmethode GetFromJsonAsync des HttpClients verwenden. Das Ding serialisiert dir die Antwort gleich in die Objekte.
    Ich empfehle außerdem für das Arbeiten mit Web-APIs PostMan zum zusätzlichen Testen zu verwenden.
    @ISliceUrPanties Mit dem HttpClient kommt auch eine 403, wie kann man mit dem HttpClient wenn StatusCode != 200 ist die Message bekommen wie hier mit dem WebRequest? Ich sehe da nichts. Mit webrequest wird im Fehlerfall ja eine WebException geworfen, mit dessen HttpWebResponse bekomme ich ja noch mehr zu lesen.

    VB.NET-Quellcode

    1. Try
    2. Using response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
    3. Using streamreader As New StreamReader(response.GetResponseStream())
    4. ResultOutput(streamreader.ReadToEnd())
    5. End Using
    6. End Using
    7. Catch ex As WebException
    8. Using response As HttpWebResponse = DirectCast(ex.Response, HttpWebResponse)
    9. Using streamreader As New StreamReader(response.GetResponseStream())
    10. ResultOutput(streamreader.ReadToEnd(), True)
    11. End Using
    12. End Using
    13. End Try


    PS.
    @ISliceUrPanties
    Habs rausgefunden, mit der Exception die geworfen wurde konnte ich nicht viel anfangen, wenn man HttpResponseMessage.EnsureSuccessStatusCode() called wird eine Exception geworfen wenn der httpStatusCode !0 200 ist, hab die Zeile auskommentiert, nun wird keine Exception geworfen, trotzdem einfach lesen, nachdem einlesen kann man mit HttpResponseMessage.IsSuccessStatusCode prüfen ob 200 oder was anderes, dann jeweils auswerten.
    @kafffee
    hier geupdated:

    VB.NET-Quellcode

    1. Imports System.Collections.Specialized
    2. Imports System.Net.Http
    3. Public Class Form1
    4. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    5. GetData("https://api.getsongbpm.com/search/", "MEIN_KEY_GIBST_NICHT", New NameValueCollection From {{"type", "artist"}, {"lookup", "metallica"}})
    6. End Sub
    7. Async Sub GetData(baseUrl As String, apiKey As String, params As NameValueCollection)
    8. Using client As New HttpClient
    9. Try
    10. Dim response As HttpResponseMessage = Await client.GetAsync(baseUrl & BuildParams(apiKey, params))
    11. ' response.EnsureSuccessStatusCode()
    12. Dim result As String = response.Content.ReadAsStringAsync().Result
    13. ResultOutput(result, Not response.IsSuccessStatusCode)
    14. Catch ex As Exception
    15. End Try
    16. End Using
    17. End Sub
    18. Private Function BuildParams(apiKey As String, params As NameValueCollection) As String
    19. Dim urlParams As String = $"&{apiKey}"
    20. For Each param As String In params
    21. urlParams &= $"&{param}={params(param)}"
    22. Next
    23. Return urlParams
    24. End Function
    25. Public Sub ResultOutput(result As String, Optional isError As Boolean = False)
    26. If isError Then
    27. MessageBox.Show(Me, result, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
    28. Else
    29. MessageBox.Show(Me, result, "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information)
    30. End If
    31. End Sub
    32. End Class

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „BitBrösel“ ()

    Das geht ähnlich dem WebRequest. Das Problem ist aber ein anderes. Der Webseite muss ein UserAgent im Header mitgegeben werden. Ansonsten wird das nicht als "echter" Request gesehen. (Und der API-Key muss natürlich aktiviert sein) Ich habe die Funktion etwas angepasst und zum Testen mal eine andere Uri von der Doku-Seite verwendet.

    VB.NET-Quellcode

    1. Async Function FindByArtist(ArtistName As String) As Task(Of String)
    2. 'Dim Uri = "https://api.getsongbpm.com/search/?type=artist&lookup=" + ArtistName
    3. Dim Uri = "https://api.getsongbpm.com/song/?api_key=HEREAPIKEY&id=983pB"
    4. Dim responseString = String.Empty
    5. Using client As New HttpClient
    6. Try
    7. client.DefaultRequestHeaders.Add("User-Agent", "VB.NET App")
    8. Dim httpRequestMessage As New HttpRequestMessage(HttpMethod.Get, Uri)
    9. Dim headers = httpRequestMessage.Headers
    10. headers.Accept.Add(New MediaTypeWithQualityHeaderValue("*/*"))
    11. Dim response = Await client.SendAsync(httpRequestMessage)
    12. If Response.IsSuccessStatusCode Then
    13. responseString = Await response.Content.ReadAsStringAsync()
    14. ' todo: etwas mit dem json machen und die Funktion ein Objekt zurückgeben lassen und kein String
    15. End If
    16. Catch ex As Exception
    17. Console.WriteLine(ex.Message)
    18. End Try
    19. End Using
    20. Return responseString
    21. End Function


    Das zurückgelieferte Json:

    Quellcode

    1. {"song":{"id":"983pB","title":"Highway to Hell","uri":"https:\/\/getsongbpm.com\/song\/highway-to-hell\/983pB","artist":{"id":"qB3","name":"AC\/DC","uri":"https:\/\/getsongbpm.com\/artist\/ac-dc\/qB3","img":"https:\/\/i.scdn.co\/image\/fb26e1c0e5779ac46b225651494ac14b6b8ebba7","genres":["heavy metal","rock"],"from":"AU","mbid":"66c662b6-6e2f-4930-8610-912e24c63ed1"},"tempo":"117","time_sig":"4\/4","key_of":"","open_key":null}}
    Ja, so was hatte ich ja bereits vermutet(ein fehlender Header), ich hatte es genau mit den selben Headern/Einstellungen welche der Webbrowser sendet gemacht, Cloudflare hat das clever umgesetzt, die haben wohl festgestellt das ich einen Webbrowser imitiert hab. Aber einfach einen eigenen UserAgent zu senden ist smart, das merk ich mir. Die hätten das aber auch in die Doku schreiben können, ich kann mich jetzt nicht erinnern da was von gelesen zu haben.
    Ja ich hab da auch nichts von gelesen. Ausser im angehängten Screenshot, da steht irgendwas mit "Header Parameter", was ich so aber nicht verstanden hab...

    @ISliceUrPanties

    Die wollen ja, wenn man einen API Key anfordert, den Namen deines Programms wissen. Hast du da einfach "VB.NET App" genannt sehe ich das richtig? Dann würde das auch Sinn machen...
    Bilder
    • Screenshot_20220616-233321.png

      161,37 kB, 1.920×1.080, 66 mal angesehen

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „kafffee“ ()