Laden...

Warum liefern Postman und WebClient()-Klasse unterschiedlich detaillierte Fehlermeldungen?

Erstellt von joob vor 4 Jahren Letzter Beitrag vor 4 Jahren 2.242 Views
J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren
Warum liefern Postman und WebClient()-Klasse unterschiedlich detaillierte Fehlermeldungen?

Ich habe einen Dienst programmiert der von einem Versandanbieter (ShipCloud) Labels zurückschickt.

Wenn ich meinen Request (Json) absende bekomme ich bei einem Fehler immer nur > Fehlermeldung:

Der Remoteserver hat einen Fehler zurückgegeben: (400) Ungültige Anforderung. Sonst bekomme ich ein JSON als Antwort in dem die Labeldaten stehen.

Wenn ich den Json über PostMan sende, bekomme ich eine detaillierte Fehlermeldung.
Diese Fehlermeldung würde ich gerne auch bei einem Fehler, wie oben beschrieben, bekommen um besser auf die Ursache zu schließen.

In der detailierten Fehlermeldung wird eine Aussage die einzelnen fehlerhaften Werte des JSON angezeigt.

Beide Anfragen sind POST.

Warum ist das so.
Wie kann ich neben der allgemein gehaltenen Fehlermeldung auch die differenzierte Meldung abrufen.

Hier mein Request:

 try
            {
                using (var client = new WebClient())
                {
                    client.Encoding = Encoding.UTF8;
                    client.Headers[HttpRequestHeader.ContentType] = "application/json";
                    client.Credentials = new System.Net.NetworkCredential(strapikey, strpass);

                    requestResult = client.UploadString(strurl, "POST", aktJson);
                    
                }

            } catch (Exception ex)
            {
                requestResult = ".." +  ex.Message;        
            }

P
441 Beiträge seit 2014
vor 4 Jahren

Meine erste Vermutung wäre, dass die Details in der InnerException Property der Exception stehen.
Das würdest du mit dem Debugger herausbekommen. ( [Artikel] Debugger: Wie verwende ich den von Visual Studio? )

Ansonsten ist die generelle Empfehlung HttpClient anstatt von WebClient zu nutzen - das würde an der Fehlerbehandlung und am Debugging aber nichts ändern 😃

Edit: Nach PN Diskussion, um es hier zu teilen:

Die Antwort steht nicht in der InnerException, sondern in der Response Property der WebException.

J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren
Rückgabewerte bei Request, Verständnisproblem

Hab jetzt alle Elemente der Exception angesehen und nichts gefunden.
Aber irgendwo muss das je herkommen.

Hat jemand noch einen Anstoß, ich weiß im Moment nicht wo ich suchen soll.

P
441 Beiträge seit 2014
vor 4 Jahren

??? nach meiner Antwort hast du mich per PN angeschrieben und ich habe dir eine ausführliche Antwort geschickt (die ich hier in Kurzform ergänzt habe)... was funktioniert daran nicht?

From - Sun, 22 Dec 2019 15:47:18
Date: Sun, 22 Dec 2019 16:47:18 +0100
From: "Papst" <noreply@mycsharp.de>
To: "joob" <noreply@mycsharp.de>
Subject: RE: request - Verständnisproblem

Hi,

du kannst das gerne auch einfach in deinem Thread schreiben 😃

Wenn man synchron und async/await mischt, ist das etwas tricky.

Für WebClient habe ich gerade einfach mal debugged... was du suchst steht in der Response, du musst sie lesen:

  
using (StreamReader reader = new StreamReader(ex.Response.GetResponseStream()))  
{  
  reader.ReadToEnd().Dump();  
}  
  

Synchron mit HttpClient arbeiten kannst du so:

  
HttpClient cl = new HttpClient();  
  
var result = cl.PostAsync("", new StringContent("...")).GetAwaiter().GetResult();  
  

Ich poste es gleich noch mit in den Thread.

Gruß
Marco

-----Ursprüngliche Nachricht-----
Nachricht von: joob
Gesendet: 22.12.2019 16:37
An: Papst
Betreff: request - Verständnisproblem

Beim Debugging konnte ich nichts herausfinden.
innerexception war aber leer.
Die Fehlermeldung kommt auch aus einer Exception, ich dachte aber vielleicht ist dort was zu finden.

J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren
Rückgabewerte bei Request, Verständnisproblem

Hab noch mal mit PostMan abgerufen, der gibt die differenzierte Fehlermeldung an der gleichen Stelle aus wie die Rückgabedaten, aber sobald die Exception in C# am Start ist, kann ich diese nicht mehr finden.

J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Super deine Lösung funktioniert.
Erst einmal schönen Dank 👍,

ich wußte überhaupt nicht wie ich da ran kommen sollte.

Ich habs nur noch nicht ganz kapiert.

Mit GetResponsStream bekomme ich mehr Informationen und die muss ich dann abrufen.

Warum wird dieser Teil nur mit GetResponsStream verfügbar.

Du findest HttpRequest besser, warum ?

J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren

Ich poste jetzt noch mal meine funktionierende Lösung.

Nochmals vielen Dank.

 try
            {
                using (var client = new WebClient())
                {
                    client.Encoding = Encoding.UTF8;
                    client.Headers[HttpRequestHeader.ContentType] = "application/json";
                    client.Credentials = new System.Net.NetworkCredential(strapikey, strpass);
                    
                    requestResult = client.UploadString(strurl, "POST", aktJson);
                    
                }

            } 
            
            catch (WebException ex)
            {
                requestResult = ".." +  ex.Message;
                using (System.IO.StreamReader reader = new System.IO.StreamReader(ex.Response.GetResponseStream()))
                {
                    requestResult =  requestResult + " - " + reader.ReadToEnd();
                }
            } 
            
            catch (Exception ex)
            {
                requestResult = ".." + ex.Message;
            }

            return requestResult;

16.806 Beiträge seit 2008
vor 4 Jahren

Nimm den Rat zu Herzen, und verwende den HttpClient.
Der WebClient wird schon seit zig Jahren nicht mehr empfohlen.

Warum -> Verwenden von HttpClientFactory zur Implementierung robuster HTTP-Anforderungen

Und:
Exceptions über das identische Objekt zurück zu geben wie ein positiver Inhalt, und dann auch nich als einfacher String: das kann nicht richtig sein.
So behandelt man in .NET keine Exceptions 😃
Siehe dazu Design Guidelines for Exceptions

PS: der Sinn eines Forums ist es nicht von einem öffentlichen Thema auf eine PN zu schwenken 😉

J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren
Rückgabewerte bei Request, Verständnisproblem

Hallo,

ich probier den Http aus.

Das mit der Exception ist so korrekt, dazu muss man den Context sehen.
Die Anwendung ist ein Dienst der verschiedene Klassen verwendet, aber am Ende muss in der Ereignisanzeige ein zusammenhängender Text stehen in der auf den Erfolg / Misserfolg und die Ursachen zu schießen sein muss.

16.806 Beiträge seit 2008
vor 4 Jahren

Joa - würde man so oder so trotzdem nicht so machen 😃
Kann man problemlos mit ordentlichem Exception-Handling abdecken.

J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren
Rückgabewerte bei Request, Verständnisproblem

Wie würdest du das machen ?

Ist mein erstes Projekt in C#.

16.806 Beiträge seit 2008
vor 4 Jahren

Vorweg:
Übrigens kannst Du Dir solch einen Code automatisch generieren lassen, wenn Du das sehr weit verbreitete https://github.com/reactiveui/refit verwendest.
Gerade für HTTP-basierte APIs - egal ob Json oder XML - absolut zu empfehlen.

Les Dir mal die Links durch, das zeigt im Prinzip den grundlegenden Umgang von Exceptions.

  • Wenn der Request sauber durchläuft, dann bekommst Du die Antwort als Rückgabeparameter
  • Wenn der Request schief läuft, dann wird eine Exception geworfen, die Du fangen kannst. Diese enthält den Error
    public class MyRequestClient
    {
        private readonly IHttpClientFactory _httpClientFactory;

        public MyRequestClient(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        public async Task<string> PostAsync<T>(T body, string url) where T : class
        {
            // setup
            var httpClient = _httpClientFactory.CreateClient();

            // params
            var jsonBody = JsonConvert.SerializeObject(body);
            var postContent = new StringContent(jsonBody, Encoding.UTF8, "application/json");

            // request
            var result = await httpClient.PostAsync(url, postContent);

            // response from request
            var response = await result.Content.ReadAsStringAsync();

            if (!result.IsSuccessStatusCode)
            {
                throw new MyRequestException(result.StatusCode, response);
            }

            return response;
        }
    }

    public class MyRequestException : Exception
    {
        public HttpStatusCode StatusCode { get; }
        public string Content { get; }

        public MyRequestException(HttpStatusCode statusCode, string content) 
            : base($"Request failed with status {statusCode}. Response: " + content)
        {
            StatusCode = statusCode;
            Content = content;
        }
    }

Man könnte das zumindest in diesel Fall auch mit dem im Link genannten Try-Pattern lösen.
Aber dieses Grundkonstrukt zeigt die korrekte Fehlerbehandlung in .NET.

Wenn Du alles über einen String zurück gibst, kann man der Methode niemals entnehmen, ob sie korrekt durchgelaufen ist, oder nicht.
Das ist quasi auch ein untestbarer Zustand im Sinne von [Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio

Nimm Dir ein paar Minuten Zeit und les Dir die Exception Guidelines durch, gerade wenn Du neu bist.
Dann verstehst Du auch sehr viele .NET Framework Methoden, die von Haus aus existieren, und deren Konzepte beser.

J
joob Themenstarter:in
8 Beiträge seit 2019
vor 4 Jahren
Rückgabewerte bei Request, Verständnisproblem

Erst mal schönen Dank für deine Mühe.

Ich muss mir das heute abend mal anschauen und versuchen es nachzuvollziehen.

Was ich aber sofort fragen muss ist:

public async Task<string> PostAsync<T>(T body, string url) where T : class

dieser Teil würde doch asyncron laufen, auch wenn mit await der Request syncron wäre.
Da aber der Request Teil eines notwendigerweise syncronen Ablaufes sein muss würde mir
das Probleme bereiten.

Oder ist das nicht so.

Was man wissen muss ist das, dies alles läuft schon in einem asyncronen übergeordnetem Task.
Und was ich in diesem Zusammenhang in Erwägung gezogen habe, ist das ein Fehler nicht aus
der Programmlogik kommt sonder durch die Benutzereingaben. (z.B. falsche Strasse oder so was.)

16.806 Beiträge seit 2008
vor 4 Jahren

Technisch gesehen kannst Du problemlos asychrone Methoden auch synchron ausführen lassen.
PostAsync().GetAwaiter().GetResult();

Was man wissen muss ist das, dies alles läuft schon in einem asyncronen übergeordnetem Task.

Das spielt prinzipiell keine Rolle. Darum kümmert sich nachher die State Machine hinter async/await.

Wenn Dein Ablauf dies nicht verträgt, dann stimmt was an Deinem Ablauf nicht.
Davon abgesehen, dass asynchroner Code kein paralleler Code bzw. parallele Execution darstellt, ändert asynchroner Code prinzipiell kein Ablauf.

Das Ziel von asynchronem Code ist Ressourcen zu schonen, zB. damit eine UI nicht einfriert, dass ein Thread nicht blockiert etc etc etc.

Es ist eher so, dass Du gewisse Dinge asynchron ablaufen musst oder sollst - weil alles andere Nachteile hätte.

Dahingehend haben moderne bzw. neuere Bibliotheken und Klassen oft auch nur noch asynchrone Methoden im Angebot, was aus Software Architektur auch der korrekte Weg darstellt.
In .NET bleibt Dir bei modernem Code Gott sei Dank keine andere Wahl als sich mit async/await zu beschäftigen - und das ist auch gut so 😃