Festlegen des Content-Type-Headers bei einer HttpReqestMessage

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

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

    Festlegen des Content-Type-Headers bei einer HttpReqestMessage

    Hallo miteinander :)

    Ich bin gerade dabei, mich programmatisch bei der Discogs API zu authentifizieren (OAuth Flow) und stosse auf ein Problem. Hab zwar Einiges im Internet gefunden und mehrere Sachen ausprobiert, aber nichts will so wirklich klappen...

    Mein Code sieht so aus:

    VB.NET-Quellcode

    1. Dim Klient As New HttpClient
    2. Dim request = New HttpRequestMessage With {
    3. .Method = HttpMethod.[Get],
    4. .RequestUri = New Uri("https://api.discogs.com/oauth/request_token")}
    5. '.Content = New StringContent("some json", Encoding.UTF8, New System.Net.Http.FormUrlEncodedContent(),
    6. Dim AnfZ As String = """"
    7. request.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
    8. request.Headers.Add("Authorization", "OAuth oauth_consumer_key=" & AnfZ & "xyz" & AnfZ & ",
    9. oauth_nonce=" & AnfZ & DateTime.Now.ToString & AnfZ & ",
    10. oauth_signature=" & AnfZ & "zyx&" & AnfZ & ",
    11. oauth_signature_method=" & AnfZ & "PLAINTEXT" & AnfZ & ",
    12. oauth_timestamp=" & AnfZ & DateTime.Now.ToString & AnfZ & ",
    13. oauth_callback=" & AnfZ & "your_callback" & AnfZ)
    14. request.Headers.Add("User-Agent", "MeineApp/3.0")
    15. Dim response = Await Klient.SendAsync(request).ConfigureAwait(False)
    16. 'response.EnsureSuccessStatusCode()
    17. Dim responseBody = Await response.Content.ReadAsStringAsync().ConfigureAwait(False)


    Dabei kommt es in Zeile 9 zu diesem Fehler:

    System.InvalidOperationException
    HResult=0x80131509
    Nachricht = Falsch verwendeter Headername. Stellen Sie sicher, dass Anforderungsheader mit "HttpRequestMessage"-Objekten, Antwortheader mit "HttpResponseMessage"-Objekten und Inhaltsheader mit "HttpContent"-Objekten verwendet werden.

    Ich bin (fast) neu bei dieser Art der Datenabfage und das Ganze ist ziemlich verwirrend für mich...

    Hat jemand dazu vielleicht was zu sagen?
    Hallo,

    DateTime.Now.ToString Prüf mal, ob da ein Doppelpunkt drin ist. Prüf deinen ganzen zusammengebauten String nach unvorteilhaften Zeichen.
    Muss es vielleicht ContentType heißen, ohne Bindestrich?




    Noch was anderes

    Es wird auf vielen Internetseiten, insbesondere bei Microsoft, empfohlen, den HttpClient global zu deklarieren

    VB.NET-Quellcode

    1. Private Shared ReadOnly Client As HttpClient = New HttpClient()



    Ein Await vor SendAsync(request) und dann auch vor ReadAsStringAsync kann zu einem Deadlock führen. Schreibe dir lieber eine Funktion, die den Request abschickt, das Ergebnis als String zurückgibt, und ruf diese Funktion asynchron auf.
    Hier ein Beispiel von mir

    C#-Quellcode

    1. string result = await Task.Run(() => CallApiAuthAsync(Endpoint + "/basic-auth/user/passwd", "user", "passwd"));


    C#-Quellcode

    1. private string CallApiAuthAsync(string url, string username, string password)
    2. {
    3. string responseString = null;
    4. System.Text.Encoding Ascii = System.Text.Encoding.ASCII;
    5. using (HttpRequestMessage httpRequestMessage = new HttpRequestMessage()
    6. {
    7. Method = HttpMethod.Get,
    8. RequestUri = new Uri(url),
    9. Headers =
    10. {
    11. { HttpRequestHeader.Authorization.ToString(), "Basic " + Convert.ToBase64String(Ascii.GetBytes($"{username}:{password}")) },
    12. { HttpRequestHeader.Accept.ToString(), "text/html,application/xhtml+xml,application/xml" }
    13. }
    14. })
    15. {
    16. using (HttpResponseMessage response = Client.SendAsync(httpRequestMessage).Result)
    17. {
    18. responseString = response.Content.ReadAsStringAsync().Result;
    19. }
    20. }
    21. return responseString;
    22. }


    Und bitte Using verwenden, damit du keinen Memory leak riskierst.

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

    Hallo,

    kafffee schrieb:

    Ich bin (fast) neu bei dieser Art der Datenabfage und das Ganze ist ziemlich verwirrend für mich...

    In deiner Fehlermeldung steht doch genau, was dein Problem ist :). Content-Type ist kein Request, sondern ein Content-Header und muss entsprechend im Header des Contents gesetzt werden.
    request.Content.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
    @ISliceUrPanties

    Ah okay das meinte ich mit verwirrend :-).

    Hab das mal ausprobiert, da kommt dann aber der Fehler "System.Net.Http.HttpRequestMessage.Content.get hat Nothing zurückgegeben."

    Hab mal in den MS Docs nachgeschaut aber nix gefunden was mir weiterhilft...

    Muss man das irgendwie initialisieren/instanziieren?

    @Bartosz
    Ja den String zu überprüfen ist dann der nächste Schritt, hab ich aber trotzdem schon mehrmals. Mal gucken wie die den TimeStamp wollen, dazu steht leider nix in den Docs. "Content-Type" sollte so stimmen laut Docs...


    Das mit dem Deadlock muss ich mir mal noch genauer anschauen. Hab bisher das Nuget Paket "DiscogsClient" benutzt und da das ziemlich viele Abhängigkeiten hat, hab ich beschlossen mal direkt auf die Web API zuzugreifen. Selbst damit gabs merkwürdige Probleme, das könnte ein Deadlock sein, da werden immer ein paar Bilder runtergeladen und dann gibts nen Fehler (den hab ich jetzt grad nicht mehr im Kopf). Hört sich das nach nem Deadlock an?
    Ja hab ich bei meiner Recherche schon von gelesen.

    Momentan verwende ich in meinem Projekt schon das Nuget-Paket DiscogsClient, da haste quasi Alles in Einem, Authentifizierung und Suchabfragen und Downloads.

    Hat aber noch drei oder vier weitere Abhängigkeiten, deshalb wollte ichs mal "raw" versuchen...

    Hab an anderer Stelle im Programm auch erfolgreich die RadioBrowser API implementiert, da dachte ich so schwer kanns ja nicht sein. Also wenn man ein Mal erfolgreich mit dem Discogs-Server connected hat sollte der Rest ja Kosmetik sein? Da gibt's ja die Eigenschaft DefaultRequestHeaders oder so...

    Also du weisst auch nicht was mit diesem Fehler "System.Net.Http.HttpRequestMessage.Content.get hat Nothing zurückgegeben“ ist?

    Wäre schade wenn ich an dieser Stelle schon aufgeben müsste...
    Warum nicht einfach mal den Sourcecode des Nuget-Packets unter die Lupe nehmen?
    github.com/damidhagor/DiscogsApiClient

    Wenn du dann OAuth selber einbauen willst, solltest du auch dazu OpenSource zum studieren finden. Lese erstmal bei Wikipedia, was OAuth überhaupt ist und wie es vom Prinzip her funktioniert. Dort findest du auch weiter führende Links.

    kafffee schrieb:

    Also du weisst auch nicht was mit diesem Fehler "System.Net.Http.HttpRequestMessage.Content.get hat Nothing zurückgegeben“ ist?


    Fehlen da noch Grundlagen? Content ist nothing, null, nada, nix!
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

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

    @DTF

    Ja cool den Link schau ich mir mal genauer an :) DiscogsAPIClient kannte ich jetzt noch nicht, weiss nicht ob das dasselbe ist wie DiscogsClient, aber ich glaube nicht.

    Also im Bereich Webanfragen fehlen mir definitiv noch Grundlagen, hab das wie gesagt auf diese Art noch nie versucht. Ich kenne zwar den Fehler, dass Content Nothing ist, aber weiss nicht wie man den in diesem Zusammenhang behebt. Gibt's vielleicht für solche Art Anfragen ein Tutorial, ich hab da schon ewig recherchiert und irgendwie macht das jeder ein bisschen anders...
    Oh, da hab ich das falsche gefunden, probier das Packet erst einmal aus, wenn das funktioniert hast du was zum anschauen. Oops, ist NET kein NET-FX, aber sollte dich trotzdem weiter bringen.
    nuget.org/packages/DiscogsApiClient
    Zitat von mir 2023:
    Was interessiert mich Rechtschreibung? Der Compiler wird meckern wenn nötig :D

    kafffee schrieb:

    Also du weisst auch nicht was mit diesem Fehler "System.Net.Http.HttpRequestMessage.Content.get hat Nothing zurückgegeben“ ist?

    Doch weiß ich :)
    Du musst den Content natürlich vorher setzen. Z.B. so:

    C#-Quellcode

    1. ​FormUrlEncodedContent content = new(new Dictionary<string, string>() { { "oauth_consumer_key", "" }, { "oauth_nonce", "" }, { "oauth_signature", "" } ... });
    2. // ...
    3. httpRequestMessage.Content = content;

    Hier ist es sogar so, dass der Header automatisch auf "application/x-www-form-urlencoded" gesetzt wird - du dich darum also gar nicht kümmern brauchst.
    Habs grad mal nach dem Beispiel von DiscogsAPIClient probiert und frag mich grad echt ob ich blöd bin:
    Hab das hier:

    VB.NET-Quellcode

    1. Using response As Task(Of HttpResponseMessage) = Klient.SendAsync(request, cancellationToken)
    2. Content = Await response.Content.ReadAsStringAsync(cancellationToken)
    3. End Using


    Das Original lautet:

    C#-Quellcode

    1. using var response = await httpClient.SendAsync(request, cancellationToken);
    2. var content = await response.Content.ReadAsStringAsync(cancellationToken);


    Da (VB.Net Code) sagt er mir Content sei kein Member von Task(Of HttpResponseMessage)

    Hä? Habs grad nochmal in den MS Docs nachgeschaut, und es ist(!) wohl ein Member... Irgendwas mach ich falsch...
    So hab eure Tipps mal beherzigt, auch das mit dem Deadlock, und hab das mal nach dem Vorbild des DiscogsAPIClient nachgebaut.

    Sieht jetzt so aus:

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Schritt1Ausfuehren()
    3. End Sub
    4. Private Async Sub Schritt1Ausfuehren()
    5. Dim result As String = Await Task.Run(Function() Schritt1())
    6. TextBox1.Text = result
    7. Debug.WriteLine(result)
    8. End Sub
    9. Private Function Schritt1() As String
    10. HoleTimeStampUndNonce()
    11. Dim authHeader As String = "OAuth "
    12. authHeader += "oauth_consumer_key=" & AnfZ & WebUtility.UrlEncode(ConsumerKey) & AnfZ & "," & Environment.NewLine
    13. authHeader += "oauth_nonce=" & AnfZ & WebUtility.UrlEncode(Nonce) & AnfZ & "," & Environment.NewLine
    14. authHeader += "oauth_signature=" & AnfZ & WebUtility.UrlEncode(ConsumerSecret) & "&" & AnfZ & "," & Environment.NewLine
    15. authHeader += "oauth_signature_method=" & AnfZ & "PLAINTEXT" & AnfZ & "," & Environment.NewLine
    16. authHeader += "oauth_timestamp=" & AnfZ & WebUtility.UrlEncode(TimeStamp) & AnfZ & "," & Environment.NewLine
    17. authHeader += "oauth_callback=" & AnfZ & WebUtility.UrlEncode("vsdcsd") & AnfZ
    18. Using request = New HttpRequestMessage(HttpMethod.Get, "https://api.discogs.com/oauth/request_token")
    19. request.Headers.Accept.Add(New MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"))
    20. 'request.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
    21. request.Headers.Add("Authorization", authHeader)
    22. request.Headers.Add("User-Agent", "VamosALaPlayer/3.0")
    23. Using response As HttpResponseMessage = Klient.SendAsync(request, cancellationToken).Result
    24. Dim Content = response.Content.ReadAsStringAsync().Result
    25. Return Content
    26. End Using
    27. End Using
    28. End Function
    29. Private Sub HoleTimeStampUndNonce()
    30. Dim elapsedTimeSince1970 = DateTime.UtcNow - New DateTime(1970, 1, 1)
    31. TimeStamp = CLng(elapsedTimeSince1970.TotalSeconds).ToString
    32. Nonce = CLng(elapsedTimeSince1970.TotalMilliseconds).ToString
    33. End Sub
    34. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    35. System.Net.ServicePointManager.SecurityProtocol = Net.SecurityProtocolType.Tls12
    36. End Sub


    Jetzt bekomm ich in Zeile 26 den Fehler System. FormatException: Das Format des Werts... ist ungültig, wobei anstatt der drei Pünktchen dann der String authHeader steht.

    Ich hab es jeweils mit und ohne die AnfZ und Environment.NewLine probiert.

    Gehe ich richtig, dass diese Fehlermeldung nicht vom Discogs-Server kommt sondern noch vorher auftritt?

    Was kann ich besser machen bzw. ist der Code sonst in Ordnung?

    Edit: Wenn ichs ohne das UrlEncode mache bekomme ich den Fehler 502 - Bad Gateway, kann das evtl. virenscannerbedingt sein?

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

    Du Mobst den armen Authorization Header ;)

    Spoiler anzeigen

    C#-Quellcode

    1. static void Main(string[] args)
    2. {
    3. HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://api.discogs.com/oauth/request_token");
    4. request.Headers.Authorization = GetAuthorizationHeader("your_consumer_key", "your_consumer_secret");
    5. HttpClient client = new HttpClient();
    6. HttpResponseMessage response = client.SendAsync(request).Result;
    7. Console.WriteLine("[{0}] {1}", response.StatusCode, response.Content.ReadAsStringAsync().Result);
    8. Console.ReadKey();
    9. }
    10. static AuthenticationHeaderValue GetAuthorizationHeader(string key, string secret)
    11. {
    12. List<string> authHeader = new List<string>()
    13. {
    14. $"oauth_consumer_key={key}",
    15. $"oauth_nonce={DateTime.Now.Ticks}",
    16. $"oauth_signature={secret}&",
    17. "oauth_signature_method=PLAINTEXT",
    18. $"oauth_timestamp={DateTime.UtcNow}",
    19. "oauth_callback="
    20. };
    21. return new AuthenticationHeaderValue("OAuth", string.Join(",", authHeader));
    22. }

    @Fakiz

    Jou dassis schon mal einiges übersichtlicher, danke hierfür :)

    Aber der werte Herr Compiler wirft immer noch den gleichen Fehler: FormatException

    Ist doch ein String oder? Versteh ich echt nicht....

    Edit: Ah ich habs. Letzte Zeile:
    Return New AuthenticationHeaderValue("Authorization", "OAuth " & String. Join(",", authHeader)

    Jetzt bekomm ich StatusCode 500 - das ist laut Docs aber denen ihr Fehler, da soll man den Support kontaktieren...

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