POST-Webrequest in einem Task ausgelagert / asynchron

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

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von 100Volt.

    POST-Webrequest in einem Task ausgelagert / asynchron

    Hallo,

    ich möchte, daß meine Anwendung im Hintergrund einen POST-Webrequest durchführt und ich die Antwort dann auswerten kann, wenn sie vollständig vorliegt. Mit der GET-Methode funktioniert es problemlos, nur mit POST verzweifle ich.

    Dieses ist der Code mit dem zwar die Abfrage der Daten klappt, aber nicht in einem Task ausgelagert / asynchron:

    VB.NET-Quellcode

    1. Private Sub Test()
    2. Dim request As WebRequest = WebRequest.Create("https://machine1/api/geraetedaten")
    3. request.Credentials = New System.Net.NetworkCredential("webserver", "12345678")
    4. request.Method = "POST"
    5. request.ContentType = "application/x-www-form-urlencoded"
    6. Dim enc As UTF8Encoding
    7. enc = New System.Text.UTF8Encoding()
    8. Dim postdata As String
    9. Dim postdatabytes As Byte()
    10. postdata = "complete=true"
    11. postdatabytes = enc.GetBytes(postdata)
    12. request.ContentLength = postdatabytes.Length
    13. Using stream = request.GetRequestStream()
    14. stream.Write(postdatabytes, 0, postdatabytes.Length)
    15. End Using
    16. Dim response As WebResponse
    17. response = request.GetResponse()
    18. lblStatus.Text = CType(response, HttpWebResponse).StatusDescription
    19. lblResponse.Text = response.Headers.ToString
    20. response.Close()
    21. End Sub


    Über Hilfe würde ich mich sehr freuen!


    Carsten
    --------
    Lieber inkompetent als inkontinent
    Schau dir mal den HttpClient an der kann das alles schon in weniger und ist genau auf Async ausgelegt.
    Ich weiß das hat nichts mit deinem Code zu tun aber warum selber die Requests mühsam nu irgendwie Async machen
    wenn es dafür schon vorgefertigtes im Framework gibt?
    Grüße , xChRoNiKx

    Nützliche Links:
    Visual Studio Empfohlene Einstellungen | Try-Catch heißes Eisen
    Man kann aus einem Task/Thread nicht ohne weiteres auf Controls zugreifen. Hierzu muss Invoke/BeginInvoke verwendet werden. Wo/Was geht denn nicht? Bissl mehr Infos wären hilfreich.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Vielen Dank für Eure Antworten!
    Hier ein paar Hintergrundinfos zum Projekt, das hätte ich gleich schreiben sollen :)

    In einer Werkstatt stehen 25 Maschinen. Auf jeder Maschine läuft ein Webserver, von dem ich Statusdaten (ca. 1 KB alle paar Sekunden) und Logdaten (zwischen 10 und 900 KB jede halbe Stunde) auslesen möchte. Es muß https und TLS 1.2 verwendet werden.
    Eigentlich läuft alles via GET-Methode, nur in zwei Fällen muß POST verwendet werden. Ich verstehe zwar nicht weshalb POST, weil nur zwei Parameter/Argumente (Logdaten vollständig auslesen und Logdaten Löschen) existieren, und das auch per GET hätte übergeben werden können, aber es ist halt so.

    So habe ich es bislang mit der GET-Methode gelöst.

    VB.NET-Quellcode

    1. Friend Async Sub Start_Test()
    2. Dim tasks As List(Of Task(Of String)) = New List(Of Task(Of String))
    3. Dim req As System.Net.HttpWebRequest = CType(System.Net.HttpWebRequest.Create("machine1/api/geraetedaten/status"), System.Net.HttpWebRequest)
    4. req.Credentials = New System.Net.NetworkCredential("webserver", "12345678")
    5. tasks.Add(ReadResponse_Test(req))
    6. Await Task.WhenAll(tasks)
    7. End Sub
    8. Private Async Function ReadResponse_Test(req As System.Net.HttpWebRequest) As Task(Of String)
    9. Using resp As System.Net.WebResponse = Await req.GetResponseAsync
    10. Using sr As New IO.StreamReader(resp.GetResponseStream)
    11. Dim respBody As String = Await sr.ReadToEndAsync
    12. frmMainRef.txtRes.Text = respBody
    13. Return respBody
    14. End Using
    15. End Using
    16. End Function


    Ich gucke mir gleich mal den HttpClient an. Vielleicht ist das ja schon die Lösung.
    --------
    Lieber inkompetent als inkontinent
    Also, als erstes würde ich sagen das deine Funktion schon mal mist ist denn du greifst in der Funktion
    auf ein Control zu und weist diesem einem Wert zu und gibst dann den Wert zurück was einfach keinen Sinn macht
    bzw nicht dem Sinn einer Funktion entspricht.

    100Volt schrieb:

    Dieses ist der Code mit dem zwar die Abfrage der Daten klappt, aber nicht in einem Task ausgelagert / asynchron:

    Was kommt denn für eine Fehlermeldung? Visual Studio wird ja nicht einfach sagen "Du das geht einfach nicht".
    Solange wir da mal nicht genau wissen was dir da für Fehler also um die Ohren fliegen können wir hier nur raten.
    Der HttpClient war nur mal ein Vorschlag weil wie gesagt der da auf das Async zeugs ausgelegt ist.
    Nur wenn es der Fehler ist den ich denke ist es auch wieder egal ob du HttpClient nutzt oder nicht ;)
    Grüße , xChRoNiKx

    Nützliche Links:
    Visual Studio Empfohlene Einstellungen | Try-Catch heißes Eisen
    Der als zweites gepostete Code (GET) funktioniert problemlos. Auch das Schreiben in die Textbox geht, wobei mich das gewundert hat.

    Beim Webrequest mit POST habe ich gar keine konkrete Idee wie genau ich das umsetzen soll. Ich habe da ja zwei Objekte zu übergeben (WebRequest und PostDataBytes) und keine Idee wie ich das machen kann. Oder ist da schon mein Denkfehler?

    Vielleicht fehlt mir auch mangels Erfahrung der Blick fürs große Ganze und der Weg mit einzelnen Tasks endet in einer Sackgasse. Bislang habe ich nie etwas mit Netzwerken programmiert, und eigentlich auch die letzten Jahre insgesamt so gut wie gar nichts programmiert. Dieses Projekt muß auch schon längst fertig sein. Nun ist auch noch DSL ausgefallen und ich tippe auf dem Handy. Wie sagt man: Läuft.... ;)
    --------
    Lieber inkompetent als inkontinent
    Ich versucheaufgrund des Ratschlags mit dem HttpClient nun mit diesem zu arbeiten. Mit GET klappt es auch, und zwar so:

    VB.NET-Quellcode

    1. Sub Main_GET()
    2. Dim t As Task = New Task(AddressOf GET_Async)
    3. t.Start()
    4. Console.WriteLine("Downloading page...")
    5. Console.ReadLine()
    6. End Sub
    7. Async Sub GET_Async()
    8. Dim page As String = "https://machine1/api/geraetedaten"
    9. Dim h As System.Net.Http.HttpClientHandler = New System.Net.Http.HttpClientHandler()
    10. Dim c = New Net.NetworkCredential("webserver", "12345678")
    11. h.Credentials = c
    12. Using client As System.Net.Http.HttpClient = New System.Net.Http.HttpClient(h)
    13. Using response As System.Net.Http.HttpResponseMessage = Await client.GetAsync(page)
    14. Using content As System.Net.Http.HttpContent = response.Content
    15. Dim result As String = Await content.ReadAsStringAsync()
    16. If result IsNot Nothing And result.Length > 50 Then
    17. Console.WriteLine(result.Substring(0, 800) + "...")
    18. End If
    19. End Using
    20. End Using
    21. End Using
    22. End Sub


    Nun versuche ich den Code auf POST anzupassen und komme nicht weiter:

    VB.NET-Quellcode

    1. Sub Main_POST()
    2. Dim t As Task = New Task(AddressOf POST_Async)
    3. t.Start()
    4. Console.WriteLine("Start...")
    5. Console.ReadLine()
    6. End Sub
    7. Async Sub POST_Async()
    8. Dim page As String = "https://machine1/api/geraetedaten"
    9. Dim h As System.Net.Http.HttpClientHandler = New System.Net.Http.HttpClientHandler()
    10. Dim c = New Net.NetworkCredential("webserver", "12345678")
    11. h.Credentials = c
    12. Dim contType As System.Net.Http.Headers.MediaTypeHeaderValue = New System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded")
    13. Dim ct As System.Net.Http.HttpContent
    14. ct.Headers.ContentType = contType
    15. Dim enc As System.Text.UTF8Encoding
    16. enc = New System.Text.UTF8Encoding()
    17. Dim postdata As String
    18. Dim postdatabytes As Byte()
    19. postdata = "complete=true"
    20. postdatabytes = enc.GetBytes(postdata)
    21. Using client As System.Net.Http.HttpClient = New System.Net.Http.HttpClient(h)
    22. Using response As System.Net.Http.HttpResponseMessage = Await client.PostAsync(page, ct)
    23. Using content As System.Net.Http.HttpContent = response.Content
    24. Dim result As String = Await content.ReadAsStringAsync()
    25. If result IsNot Nothing And result.Length > 50 Then
    26. Console.WriteLine(result.Substring(0, 800) + "...")
    27. End If
    28. End Using
    29. End Using
    30. End Using
    31. End Sub


    Mindestens zwei Probleme gibt es noch:

    VB.NET-Quellcode

    1. ct.Headers.ContentType = contType

    Es kommt die Fehlermeldung: System.NullReferenceException: "Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt."
    Neues Objekt/Instanz erstellen:

    VB.NET-Quellcode

    1. Dim ct As System.Net.Http.HttpContent

    mit mit New gehts nicht. Aber wie denn dann?

    Meine postdata habe ich auch noch nirgendwo zuweisen können, was Problem Nr. 2 wäre.
    --------
    Lieber inkompetent als inkontinent
    Heyho,

    also für "application/x-www-form-urlencoded" brauchst du kein ContentType anlegen das würde das hier tun:

    VB.NET-Quellcode

    1. Dim values = New Dictionary(Of String, String) From {
    2. {"complete", "true"}
    3. }
    4. Dim content = New FormUrlEncodedContent(values)
    5. Dim response = Await client.PostAsync("https://deineUlr.com/asdasd", content)
    6. Dim responseString = Await response.Content.ReadAsStringAsync()


    Du kannst halt nicht direkt nen HttpContent erstellen. Dafür gibt es die anderen Content Klassen:
    docs.microsoft.com/en-us/dotne…t?view=netframework-4.7.2
    Wie halt im ByteArrayContent das FormUrlEncodedContent
    Grüße , xChRoNiKx

    Nützliche Links:
    Visual Studio Empfohlene Einstellungen | Try-Catch heißes Eisen
    Vielen Dank für Deine Hilfe!

    So sieht das jetzt aus:

    VB.NET-Quellcode

    1. Imports System.Threading.Tasks
    2. Public Class clsDemo
    3. Public Sub New(ByRef frmMainRef_ As frmMain)
    4. frmMainRef = frmMainRef_
    5. End Sub
    6. Friend Async Sub Main_GET()
    7. Dim str As String = ""
    8. Dim tasks As List(Of Task(Of String)) = New List(Of Task(Of String))
    9. tasks.Add(GET_Async(str))
    10. Await Task.WhenAll(tasks)
    11. End Sub
    12. Async Function GET_Async(ByVal str As String) As Task(Of String)
    13. Dim page As String = "https://machine1/api/geraetedaten/log.txt"
    14. Dim h As System.Net.Http.HttpClientHandler = New System.Net.Http.HttpClientHandler()
    15. Dim c = New Net.NetworkCredential("webserver", "12345678")
    16. h.Credentials = c
    17. Dim client As System.Net.Http.HttpClient = New System.Net.Http.HttpClient(h)
    18. client.Timeout = New TimeSpan(0, 0, 15)
    19. Dim response As System.Net.Http.HttpResponseMessage = Await client.GetAsync(page)
    20. Dim content As System.Net.Http.HttpContent = response.Content
    21. Dim result As String = Await content.ReadAsStringAsync()
    22. If result IsNot Nothing And result.Length > 50 Then
    23. HierSchreibIchWasRein(result)
    24. End If
    25. Return result
    26. End Function
    27. Friend Async Sub Main_POST()
    28. Dim str As String = ""
    29. Dim tasks As List(Of Task(Of String)) = New List(Of Task(Of String))
    30. tasks.Add(POST_Async(str))
    31. Await Task.WhenAll(tasks)
    32. End Sub
    33. Async Function POST_Async(ByVal str As String) As Task(Of String)
    34. Dim h As System.Net.Http.HttpClientHandler = New System.Net.Http.HttpClientHandler()
    35. Dim c = New Net.NetworkCredential("webserver", "12345678")
    36. h.Credentials = c
    37. Dim values = New Dictionary(Of String, String) From {{"complete", "true"}}
    38. Dim content = New System.Net.Http.FormUrlEncodedContent(values)
    39. Dim client As System.Net.Http.HttpClient = New System.Net.Http.HttpClient(h)
    40. client.Timeout = New TimeSpan(0, 0, 15)
    41. Dim response = Await client.PostAsync("https://machine1/api/geraetedaten", content)
    42. Dim responseString = response.Headers.ToString
    43. 'Dim responseString = Await response.Content.ReadAsStringAsync()
    44. HierSchreibIchWasRein(responseString)
    45. Return responseString
    46. End Function
    47. Private Sub HierSchreibIchWasRein(ByVal strDaten As String)
    48. frmMainRef.txtRes.Text = strDaten
    49. End Sub
    50. End Class


    Direkt auf die Textbox der Windows-Form zuzugreifen geht wohl seit VS 2012, so wie sich das hier liest:
    docs.microsoft.com/de-de/dotne…uide/concepts/async/index
    Zumindest funktioniert es soweit.
    --------
    Lieber inkompetent als inkontinent