Dekomprimieren des GZip-Streams aus der HTTPClient-Antwort

91

Ich versuche, eine Verbindung zu einer API herzustellen, die GZip-codiertes JSON von einem WCF-Dienst (WCF-Dienst zu WCF-Dienst) zurückgibt. Ich verwende den HTTPClient , um eine Verbindung zur API herzustellen , und konnte das JSON-Objekt als Zeichenfolge zurückgeben. Ich muss jedoch in der Lage sein, diese zurückgegebenen Daten in einer Datenbank zu speichern, und als solche dachte ich, der beste Weg wäre, das JSON-Objekt in einem Array oder Byte oder etwas in dieser Richtung zurückzugeben und zu speichern.

Ich habe speziell Probleme mit der Dekomprimierung der GZip-Codierung und habe viele verschiedene Beispiele ausprobiert, kann sie aber immer noch nicht verstehen.

Mit dem folgenden Code stelle ich meine Verbindung her und erhalte eine Antwort. Dies ist der Code, der eine Zeichenfolge von der API zurückgibt.

public string getData(string foo)
{
    string url = "";
    HttpClient client = new HttpClient();
    HttpResponseMessage response;
    string responseJsonContent;
    try
    {
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        response = client.GetAsync(url + foo).Result;
        responseJsonContent = response.Content.ReadAsStringAsync().Result;
        return responseJsonContent;
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show(ex.Message);
        return "";
    }
}

Ich habe einige verschiedene Beispiele wie diese StackExchange-API , MSDN und einige Beispiele zum Stackoverflow verfolgt, aber ich konnte keines davon für mich zum Laufen bringen.

Was ist der beste Weg, um dies zu erreichen? Bin ich überhaupt auf dem richtigen Weg?

Danke Leute.

Corey
quelle
"Der beste Weg wäre, das JSON-Objekt zurückzugeben und in einem Array oder Byte zu speichern." Beachten Sie, dass eine Zeichenfolge ein Array von Bytes ist.
user3285954

Antworten:

230

Instanziieren Sie HttpClient einfach so:

HttpClientHandler handler = new HttpClientHandler()
{
    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
};

using (var client = new HttpClient(handler))
{
    // your code
}

Update 19. Juni 2020: Es wird nicht empfohlen, httpclient in einem "using" -Block zu verwenden, da dies zu einer Erschöpfung des Ports führen kann.

private static HttpClient client = null;

ContructorMethod()
{
   if(client == null)
   {
        HttpClientHandler handler = new HttpClientHandler()
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        };        
        client = new HttpClient(handler);
   }
// your code            
 }

Wenn Sie .Net Core 2.1+ verwenden, sollten Sie IHttpClientFactory verwenden und wie folgt in Ihren Startcode einfügen.

 var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
            TimeSpan.FromSeconds(60));

 services.AddHttpClient<XApiClient>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        }).AddPolicyHandler(request => timeout);
GRABEN
quelle
Wie kann ich den Inhalt meiner Antwort vom httpClient abrufen, wenn ich diese Struktur verwende? Ich bin super neu in c # und ich glaube nicht, dass ich es bekomme.
FoxDeploy
1
@FoxDeploy Es ist keine Änderung erforderlich, damit der Code den Inhalt erhält, wenn Sie diese Lösung verwenden. Siehe hier als Referenz: stackoverflow.com/questions/26597665/…
DIG
1
Obwohl es sich um einen alten Beitrag handelt, hat diese Antwort gerade mein Problem in .netcore gelöst und von 1.1 auf 2.0 verschoben. Es scheint, dass der Client die Dekomprimierung automatisch durchgeführt hat. Daher musste ich diesen Code in 2.0 hinzufügen, damit er funktioniert ... Danke !
Sebastian Castaldi
3
Nur um auf @SebastianCastaldi zurückzugreifen, aber .net Core 1.1 hatte AutomaticDecompression richtig eingestellt, aber in .net Core 2.0 ist es auf NONE gesetzt. Ich habe viel zu lange
gebraucht, um
5
Hinweis: HttpClientSollte NICHT im Inneren verwendet werdenusing
imba-tjd
1

Ich habe Code von unten verwendet, um den GZip-Stream zu dekomprimieren. Dann habe ich das dekomprimierte Byte-Array verwendet, um das erforderliche JSON-Objekt zu erhalten. Hoffe, es kann jemandem helfen.

var readTask = result.Content.ReadAsByteArrayAsync().Result;
var decompressedData = Decompress(readTask);
string jsonString = System.Text.Encoding.UTF8.GetString(decompressedData, 0, decompressedData.Length);
ResponseObjectClass responseObject = Newtonsoft.Json.JsonConvert.DeserializeObject<ResponseObjectClass>(jsonString);

https://www.dotnetperls.com/decompress

static byte[] Decompress(byte[] gzip)
{
    using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
    {
        const int size = 4096;
        byte[] buffer = new byte[size];
        using (MemoryStream memory = new MemoryStream())
        {
            int count = 0;
            do
            {
                count = stream.Read(buffer, 0, size);
                if (count > 0)
                {
                    memory.Write(buffer, 0, count);
                }
            }
            while (count > 0);
            return memory.ToArray();
        }
    }
}
NidhinSPradeep
quelle
0

Ok, also habe ich mein Problem gelöst. Wenn es bessere Möglichkeiten gibt, lass es mich wissen :-)

        public DataSet getData(string strFoo)
    {
        string url = "foo";

        HttpClient client = new HttpClient();
        HttpResponseMessage response;   
        DataSet dsTable = new DataSet();
        try
        {
               //Gets the headers that should be sent with each request
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
              //Returned JSON
            response = client.GetAsync(url).Result;
              //converts JSON to string
            string responseJSONContent = response.Content.ReadAsStringAsync().Result;
              //deserializes string to list
            var jsonList = DeSerializeJsonString(responseJSONContent);
              //converts list to dataset. Bad name I know.
            dsTable = Foo_ConnectAPI.ExtentsionHelpers.ToDataSet<RootObject>(jsonList);
              //Returns the dataset                
            return dsTable;
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message);
            return null;
        }
    }

       //deserializes the string to a list. Utilizes JSON.net. RootObject is a class that contains the get and set for the JSON elements

    public List<RootObject> DeSerializeJsonString(string jsonString)
    {
          //Initialized the List
        List<RootObject> list = new List<RootObject>();
          //json.net deserializes string
        list = (List<RootObject>)JsonConvert.DeserializeObject<List<RootObject>>(jsonString);

        return list;
    }

Das RootObject enthält die get-Menge, die die Werte des JSON abruft.

public class RootObject
{  
      //These string will be set to the elements within the JSON. Each one is directly mapped to the JSON elements.
      //This only takes into account a JSON that doesn't contain nested arrays
    public string EntityID { get; set; }

    public string Address1 { get; set; }

    public string Address2 { get; set; }

    public string Address3 { get; set; }

}

Der einfachste Weg, die oben genannten Klassen zu erstellen, ist die Verwendung von json2charp, das sie entsprechend formatiert und auch die richtigen Datentypen bereitstellt.

Das Folgende ist aus einer anderen Antwort auf Stackoverflow , die verschachtelten JSON nicht berücksichtigt.

    internal static class ExtentsionHelpers
{
    public static DataSet ToDataSet<T>(this List<RootObject> list)
    {
        try
        {
            Type elementType = typeof(RootObject);
            DataSet ds = new DataSet();
            DataTable t = new DataTable();
            ds.Tables.Add(t);

            try
            {
                //add a column to table for each public property on T
                foreach (var propInfo in elementType.GetProperties())
                {
                    try
                    {
                        Type ColType = Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType;

                            t.Columns.Add(propInfo.Name, ColType);

                    }
                    catch (Exception ex)
                    {
                        System.Windows.Forms.MessageBox.Show(ex.Message);
                    }

                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }

            try
            {
                //go through each property on T and add each value to the table
                foreach (RootObject item in list)
                {
                    DataRow row = t.NewRow();

                    foreach (var propInfo in elementType.GetProperties())
                    {
                        row[propInfo.Name] = propInfo.GetValue(item, null) ?? DBNull.Value;
                    }

                    t.Rows.Add(row);
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }

            insert.insertCategories(t);
            return ds.
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message);

            return null;
        }
    }
};

Um schließlich das obige Dataset in eine Tabelle mit Spalten einzufügen, die dem JSON zugeordnet wurden, habe ich eine SQL-Massenkopie und die folgende Klasse verwendet

public class insert
{ 
    public static string insertCategories(DataTable table)
    {     
        SqlConnection objConnection = new SqlConnection();
          //As specified in the App.config/web.config file
        objConnection.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["foo"].ToString();

        try
        {                                 
            objConnection.Open();
            var bulkCopy = new SqlBulkCopy(objConnection.ConnectionString);

            bulkCopy.DestinationTableName = "dbo.foo";
            bulkCopy.BulkCopyTimeout = 600;
            bulkCopy.WriteToServer(table);

            return "";
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message);
            return "";
        }
        finally
        {
            objConnection.Close();
        }         
    }
};

Das obige funktioniert also, um JSON von einer WebAPI in eine Datenbank einzufügen. Das ist etwas, was ich zur Arbeit bekomme. Aber ich erwarte keineswegs, dass es perfekt ist. Wenn Sie Verbesserungen haben, aktualisieren Sie diese bitte entsprechend.

Corey
quelle
2
Sie sollten Ihre erstellen HttpClientund Ihr HttpResponseinnerhalb einer using()Anweisung jeweils eine ordnungsgemäße, fristgerechte Entsorgung und Schließen der zugrunde liegenden Ströme.
Ian Mercer