HTTP Request, SOAP Request, REST API Quellen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von Haudruferzappeltnoch.

    HTTP Request, SOAP Request, REST API Quellen

    Hallo,

    könnt ihr mir ein paar Tutorials, Links, andere Dinge empfehlen, die ich mir anschauen kann zum Erlernen obiger Themen und derer Umsetzung in .net?
    Ich finde da weder gute Quellen was Grundlagen, noch was Anwendungsbeispiele angeht. Von beiden in Kombination ganz zu schweigen.

    Viele Grüße

    *Topic verschoben*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Hallo,
    um in das Thema einzusteigen kann ich empfehlen, eine Anwendung zu schreiben, die eine öffentlich verfügbare API konsumiert. Gut eignet sich dafür z.B. die Chuck Norris Jokes und Star Wars API.
    Die Microsoft Doku ist für mich eigentlich immer die erste Anlaufstelle. Für dich könnte das z.B. der Http Client sein, der die Anfrage an die API stellt.
    Wenn du selbst eine API erstellen möchtest, bietet sich dieses Tutorial an; ebenfalls in den Microsoft Docs. Von hier aus solltest du genug Stichworte erhalten, die dir eine weitere spezielle Suche ermöglichen.
    Evtl. hilft es auch, in der Wikipedia nach den Begrifflichkeiten zu suchen.
    Ich habe folgendes gefunden, das für mich erstmal ganz verständlich ausschaut.
    Die in meiner Schnittstelle beschriebenen Header sind Properties der HttpWebRequest Klasse? Zumindest finde ich da die Headerbezeichnungen alle wieder.
    Dann habe ich geschaut wie ich mit x-www-form-urlencoded umgehen muss. Anscheinend sollen hier die einzelnen Felder, die da rein gehören, in einem String zusammengefügt werden.
    Das ist dann bei mir postData ist das der Request Body?

    VB.NET-Quellcode

    1. Private Sub Anmelden()
    2. Dim request As HttpWebRequest
    3. Dim enc As UTF8Encoding
    4. Dim postdata As String
    5. Dim postdatabytes As Byte()
    6. request = HttpWebRequest.Create(testurl)
    7. enc = New System.Text.UTF8Encoding()
    8. postdata = "username=testusr&password=!testpw&&message=test"
    9. postdatabytes = enc.GetBytes(postdata)
    10. request.Method = "POST"
    11. request.Accept = "application/json"
    12. request.ContentType = "application/x-www-form-urlencoded"
    13. request.ContentLength = postdatabytes.Length
    14. Using stream = request.GetRequestStream()
    15. stream.Write(postdatabytes, 0, postdatabytes.Length)
    16. End Using
    17. Dim result = request.GetResponse()
    18. End Sub

    Tja da ist jetzt die Frage, die Syntax läuft ja mit & und = als funktionale Zeichen. Was ist wenn mein Passwort davon eines enthält, wie hier? Wie escape ich das?

    Wie kann ich so eine Request außerhalb meiner Anwendung testen?
    Super das erklärt auch warum ich bei leerzeichen Fehlermeldungen hatte.

    Bei dem & muss dann ein %26 hin, das hat geklappt

    Da hätte ich noch eine Frage, an welcher Stelle passiert eigentlich die Abfrage?

    VB.NET-Quellcode

    1. Private Sub Anmelden()
    2. Dim request As HttpWebRequest
    3. Dim enc As UTF8Encoding
    4. Dim postdata As String
    5. Dim postdatabytes As Byte()
    6. request = HttpWebRequest.Create(testurl)
    7. enc = New System.Text.UTF8Encoding()
    8. postdata = "username=testusr&password=!testpw&&message=test"
    9. postdatabytes = enc.GetBytes(postdata)
    10. request.Method = "POST"
    11. request.Accept = "application/json"
    12. request.ContentType = "application/x-www-form-urlencoded"
    13. request.ContentLength = postdatabytes.Length
    14. Using stream = request.GetRequestStream()
    15. stream.Write(postdatabytes, 0, postdatabytes.Length)
    16. End Using
    17. Using result = request.GetResponse()
    18. Using receiveStream = result.GetResponseStream
    19. Using ReadStream As New IO.StreamReader(receiveStream, enc)
    20. Console.WriteLine(ReadStream.ReadToEnd)
    21. End Using
    22. End Using
    23. End Using
    24. Console.ReadKey()
    25. End Sub

    Spätestens bei .GetResponse muss ja was gesendet werden, aber anderesseits weiß ich dann nicht was der Stream soll, wenn die Daten schon alle irgendwo abgerufen worden wären.

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

    Du kannst den String auch durch die entsprechende .Net-Funktion jagen.
    learn.microsoft.com/de-de/dotn…web.httputility.urlencode

    Haudruferzappeltnoch schrieb:

    was der Stream soll

    GetResponse liest die Daten in eine internes Objekt des Typs System.Net.WebResponse.
    Dieses stellt einen Stream zur Verfügung, den kannst du mit einem StreamReader auslesen.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „petaod“ ()

    Also ist das so vorgesehen?

    VB.NET-Quellcode

    1. Dim User = HttpUtiliy.UrlEncode("testusr")
    2. Dim pw = HttpUtiliy.UrlEncode("!testpw&")
    3. Dim msg = HttpUtiliy.UrlEncode("test")
    4. Dim postData = $"username={User}&password={pw}&message={msg}"


    Ich habe mir jetzt alles mögliche zu den Methoden angeschaut, aber bei meinen Tests ergibt das alles keinen Sinn mehr. Ich versuche die Kommunikation zwischen Client und Server zu verstehen und vor allem in welchem Zusammenhang dies mit dem Code steht.

    VB.NET-Quellcode

    1. Using stream = request.GetRequestStream()
    2. stream.Write(postdatabytes, 0, postdatabytes.Length)
    3. End Using
    4. ''Netzwerkanschluss aus
    5. Using result = request.GetResponse()
    6. Using receiveStream = result.GetResponseStream
    7. Using ReadStream As New IO.StreamReader(receiveStream, enc)
    8. Console.WriteLine(ReadStream.ReadToEnd)
    9. End Using
    10. End Using
    11. End Using

    Soweit ich es verstanden habe, liest der StreamReader selbst noch Daten aus dem Netzwerk, dass heißt eigentlich müsste bis zum Schluss eine Verbindung zum Server bestehen. Ich kann ab Zeile 4 allerdings den Code vollständig mit getrennter Netzwerkverbindung ausführen und bekomme das erwartete Ergebnis. Kann es sein, dass die Datenmengen so klein sind, dass schon alles sofort übertragen wurde und mit einem größeren Test würde ich dann scheitern?

    Dieser Beitrag wurde bereits 7 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()

    Hallo,
    ich wiederhole mich immer wieder gern, aber HttpWebRequest soll nicht mehr für neue Entwicklungen verwendet werden. Siehe dazu die Doku bei MS.

    Der Code ist außerdem viel zu komplex. Vieles davon wird vom Framework abgenommen. Siehe dazu mein Beispiel

    VB.NET-Quellcode

    1. ​Async Sub Main
    2. Const uri As String = "https://localhost:7096/demo"
    3. Dim formData As Dictionary(Of String, String) = New Dictionary(Of String, String) From {
    4. {"username", "testusr"},
    5. {"password", "!testpw&"},
    6. {"message", "test"}
    7. }
    8. Using client As New System.Net.Http.HttpClient()
    9. Dim responseMessage = Await client.PostAsync(uri, New FormUrlEncodedContent(formData))
    10. responseMessage.EnsureSuccessStatusCode()
    11. If Not responseMessage.IsSuccessStatusCode Then Return
    12. Dim responseStream = Await responseMessage.Content.ReadAsStreamAsync()
    13. ' oder
    14. Dim responseString = Await responseMessage.Content.ReadAsStringAsync()
    15. ' oder
    16. Dim responseBytes = Await responseMessage.Content.ReadAsByteArrayAsync()
    17. ' Todo: Daten verarbeiten
    18. End Using
    19. End Sub

    Die Klasse FormUrlEncodedContent kümmert sich um das Encoding der Daten. Kein verwenden von Stringverkettung, kein manuelles umwandeln der Post-Daten.
    Danke, aber mein Punkt ist nicht eine Anwendung zu schreiben. Sondern Verständnis für diese Konzepte aufzubauen.
    Ich habe auf deinen Tipp hin in die HttpClient Klasse geschaut, diese hat noch einige Sachen die ich mir später angucken werde, welche aber nicht auf den Kern der Sache abzielen.
    Über die HttpWebRequest Klasse habe ich schonmal verstehen können wie es mit den Headern, Adressen und Typen läuft.

    Ich würde gerne noch erst den Kommunikationsprozess nachvollzogen haben bevor ich zur HttpClient Klasse übergehe geschweige denn tatsächlich sinnvollen Code schreibe.

    Haudrafzappeltnoch schrieb:

    dass heißt eigentlich müsste bis zum Schluss eine Verbindung zum Server bestehen

    Wann die Verbindung geschlossen wird hängt von der genutzten HTTP Version und ggf. gesetzten Connection Headern ab. Bis HTTP 1.0 war es üblich das der Server normalerweise die Verbindung schloss nach dem alle Daten gesendet wurden. Wenn du dich dafür interessierst kannst du mal nach der HTTP RFC googeln.

    Fakiz schrieb:

    die Verbindung schloss nach dem alle Daten gesendet wurden

    Ja wenn man das mal voraussetzt, dann müssten in meinem Fall ja schon alle Antwortdaten übergeben worden sein, nach dem Sendestream des Requests, richtig?
    Die Frage ist wie groß müssen die Daten werden, damit ich Exceptions provozieren kann durch getrennten Netzwerkzugriff während der Response Ermittlung. (Oder wenn keine Exceptions zumindest ein unvollständiges Ergebnis)

    rfc9110 schrieb:


    Any Content-Length field value greater than or equal to zero is valid. Since there is no predefined limit to the length of content,

    Quelle: HTTP RFC 8.6. Content-Length, mit anderen Worten die maximale Länge hängt vom Server bzw. der Programmiersprache ab.

    Für deine Tests könntest du dir entweder einen eigenen HTTP-Server erstellen oder einen fertigen Server verwenden wie z.B. Xampp oder Wamp. Wenn du einen eigenen Schreiben willst ist der HttpListener dein Freund.

    Wenn du einen fertigen Server verwendest dann könnte man ein kleines PHP Skript erstellen welches nach aufruf einfach einige Sekunden wartet bevor es etwas ausgibt. Wenn du in der Zeit den Server beendest kommt es zu Ausnahemen.

    Beispiel

    C#-Quellcode

    1. client.GetStringAsync("http://dev.fakiz.local/projects/VbParadiseTest/?sleep=10");


    PHP-Quellcode

    1. <?php
    2. if (isset($_GET['sleep']) && preg_match('/^\d+$/', $_GET['sleep']))
    3. sleep(intval($_GET['sleep']));
    4. die("Response after {$_GET['sleep']} seconds...");



    Gut ich bin nun bei HttpClient Klasse.

    AlterCode

    VB.NET-Quellcode

    1. Private Sub Anmelden()
    2. Dim User = "testusr"
    3. Dim pw = HttpUtility.UrlEncode("!testpw&")
    4. Dim request As HttpWebRequest
    5. Dim enc As UTF8Encoding
    6. Dim postdata As String
    7. Dim postdatabytes As Byte()
    8. request = WebRequest.Create("https://path/")
    9. enc = New System.Text.UTF8Encoding()
    10. postdata = $"username={User}&password={pw}&message=test"
    11. postdatabytes = enc.GetBytes(postdata)
    12. request.Method = "POST"
    13. request.Accept = "application/json"
    14. request.ContentType = "application/x-www-form-urlencoded"
    15. request.ContentLength = postdatabytes.Length
    16. Using stream = request.GetRequestStream()
    17. stream.Write(postdatabytes, 0, postdatabytes.Length)
    18. End Using
    19. Using result = request.GetResponse()
    20. Using receiveStream = result.GetResponseStream
    21. Using ReadStream As New IO.StreamReader(receiveStream, enc)
    22. Console.WriteLine(ReadStream.ReadToEnd)
    23. End Using
    24. End Using
    25. End Using
    26. End Sub


    NeuerCode

    VB.NET-Quellcode

    1. Private Async Sub Anmelden()
    2. Dim User = "testusr"
    3. Dim pw = HttpUtility.UrlEncode("!testpw&")
    4. Dim requestUrl = "https://path/"
    5. Dim postdata = $"username={User}&password={pw}&message=test"
    6. Using client As New HttpClient()
    7. client.DefaultRequestHeaders.Add("Accept", "application/json")
    8. client.DefaultRequestHeaders.Add("ContentType", "application/x-www-form-urlencoded") 'Ist das hier notwendig? Der Header ContentType ist im alten Code explizit deklariert.
    9. Using content = New StringContent(postdata, Encoding.UTF8, "application/x-www-form-urlencoded") 'Hier kann man aber auch einen Typ angeben
    10. Using response = Await client.PostAsync(requestUrl, content) 'Die PostAsync Methode übernimmt vermutlich automatisch das was oben explizit deklariert ist (request.Method = "POST")
    11. Dim responseString = Await response.Content.ReadAsStringAsync() '
    12. Console.WriteLine(responseString)
    13. End Using
    14. End Using
    15. End Using
    16. End Sub

    Der StringContent enthält nun die postData, welche im alten Code noch selbst gesendet wird. Der Header ContentType bestimmt den Typ dieser postData. Übernimmt der MediaType des StringContents diese Aufgabe oder muss der Header weiterhin deklariert bleiben?

    request.Method wird in der HttpClient Klasse auf verschiedene Mehoden aufgeteilt, daher entfällt diese Angabe, verstehe ich das richtig?

    Eine URI kann auch eine sogenannte "query" enthalten markiert mit dem ?, die Syntax einer solchen query sieht genauso aus wie das, was in der postData definiert ist (x=y&a=b). Ist die postData dasselbe wie die query eines URIs?

    @ISliceUrPanties bei deiner FormUrlEncodedContent Klasse wird alternativ einfach die Headers Property so eingestellt, wie die Header im alten Code deklariert sind?
    Ich gehe mal davon aus, die würden dann auch die DefaultRequestHeaders des HttpClients überschreiben?
    Deine Aussage verstehe ich nicht so recht. Warum sollte das denkbar ungeeignet zum Lernen sein? Wenn du alles manuell machen möchtest - also diverse Header setzen - kannst du ja trotzdem mit StringContent, dem ByteArrayContent oder einer anderen Content-Klasse arbeiten. Die Klasse FormUrlEncodedContent unterstützt dabei, Formdaten auch als solche zu senden - und setzt halt auch gleich entsprechende Header. Kannst du ja je nach Anwendungsfall entscheiden, wie du vorgehen möchtest.
    Wenn es dir nur um die Theorie hinter HTTP geht (bzgl. des Lernens), ist es dann nicht eher ungeeignet, das an Hand eines spezifischen Frameworks mit seinen Möglichkeiten zu erarbeiten?

    Übernimmt der MediaType des StringContents diese Aufgabe oder muss der Header weiterhin deklariert bleiben?

    Ohne es getestet zu haben, würde ich sagen das du den Header nicht selbst setzen musst.


    request.Method wird in der HttpClient Klasse auf verschiedene Mehoden aufgeteilt, daher entfällt diese Angabe, verstehe ich das richtig?

    Ja.


    Ist die postData dasselbe wie die query eines URIs?

    Nein, du kannst einen Querystring mit der POST Methode senden aber POST-Daten sind eher abstrakt. Z.B. wirst du in SOAP APIs oft mit XML-POST-Daten konfrontiert werden. Bei REST APIs mit JSON-POST-Daten und bei Logins mit URLEncoded QueryStrings diese starten aber bei der POST-Methode nicht mit ?.
    Ich habe es mir schon gedacht, die Header und content daten sind irgendwie im Hintergrund, ich hab es mir entsprechend aufgeteilt.

    @ISliceUrPanties Natürlich geht es mir am Anfang mehr um die Theorie. Allerdings wie gesagt finde ich es schwierig überhaupt einen brauchbaren Einstieg zu finden.
    Ich will es natürlich im Framework nutzen. Aber da eine fertige Methode zu nehmen für Typ X hilft mir genau 0 wenn ich Typ Y dann mal machen will. Daher muss ich an der Stelle ja schonmal verstehen was so ein Typ ist und wie dieser einzustellen ist.
    Hinterher ist es dann gut zu wissen, dass es Methoden gibt die dieses Wissen in Abkürzungen umgesetzt haben. Wie z.B. der Übergang von HttpWebRequest zu HttpClient

    In der FormUrlEncodedContent Klasse bzw. andere Contentklassen sind also gewisse Header immer vordefiniert (oder nur der ContentType Header)?

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

    Haudruferzappeltnoch schrieb:

    gewisse Header immer vordefiniert (oder nur der ContentType Header)?

    Das kommt auf die gewählte Klasse an. Bei FormUrlEncodedContent ist es nur der ContentType, bei anderen mag es anders aussehen. Da .NET ja Open Source ist, kann man sich das genau ansehen. Schau mal im Github-Repo vorbei. :)
    Schön ich habe das Muster so langsam verstanden, jetzt wollte ich mal ein echtes Beispiel testen. Das nutzt Json.
    Jetzt habe ich eine Klasse erstellt, für die eine JsonSerializer den entsprechenden ContentString erzeugen soll.
    Das alles sieht hier bei "Serialization example" sehr straight forward aus. Allerdings kriegt ich nur leere {} raus. Nix mit Properties

    Irgend ne Ahnung was ich da falsch mache?

    VB.NET-Quellcode

    1. Friend Class Query
    2. Friend Property gruppe As String
    3. Friend Property firma As String
    4. Friend Property ids As String()
    5. End Class
    6. Imports System.Text.Json
    7. ...
    8. Dim standardoptions = New JsonSerializerOptions
    9. Dim q = New Query With {.gruppe = "123", .firma = "test", .ids = {"111111"}}
    10. Dim jsoncontent = JsonSerializer.Serialize(q, standardoptions) '= "{}"

    Habs gefunden man muss die Properties Public deklarieren. Hätte nicht gedacht das mir die Gwohnheit mal in die Quere kommt ^^

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Haudruferzappeltnoch“ ()