Descomprimir GZip Stream de HTTPClient Response

93

Estoy tratando de conectarme a una API, que devuelve JSON codificado en GZip, desde un servicio WCF (servicio WCF a servicio WCF). Estoy usando HTTPClient para conectarme a la API y he podido devolver el objeto JSON como una cadena. Sin embargo, necesito poder almacenar estos datos devueltos en una base de datos y, como tal, pensé que la mejor manera sería devolver y almacenar el objeto JSON en una matriz o byte o algo por el estilo.

Con lo que tengo problemas específicamente es con la descompresión de la codificación GZip y he estado probando muchos ejemplos diferentes pero aún no puedo conseguirlo.

El siguiente código es cómo establezco mi conexión y obtengo una respuesta, este es el código que devuelve una cadena de la API.

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 "";
    }
}

He estado siguiendo algunos ejemplos diferentes como estos StackExchange API , MSDN y un par de stackoverflow, pero no he podido hacer que ninguno de estos funcione para mí.

¿Cuál es la mejor manera de lograr esto? ¿Estoy siquiera en el camino correcto?

Gracias chicos.

Corey
fuente
"la mejor manera sería devolver y almacenar el objeto JSON en una matriz o byte" Tenga en cuenta que una cadena es una matriz de bytes.
user3285954

Respuestas:

232

Simplemente cree una instancia de HttpClient así:

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

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

Actualización del 19 de junio de 2020: no se recomienda usar httpclient en un bloque de 'uso', ya que podría causar el agotamiento del puerto.

private static HttpClient client = null;

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

Si usa .Net Core 2.1+, considere usar IHttpClientFactory e inyectar así en su código de inicio.

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

 services.AddHttpClient<XApiClient>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        }).AddPolicyHandler(request => timeout);
CAVAR
fuente
Si utilizo esta estructura, ¿cómo recupero el contenido de mi respuesta del httpClient? Soy muy nuevo en C # y no creo que lo esté entendiendo.
FoxDeploy
1
@FoxDeploy no se necesitan cambios para que el código obtenga el contenido cuando usa esta solución. Consulte aquí para referencia: stackoverflow.com/questions/26597665/…
DIG
1
aunque es una publicación antigua, esta respuesta acaba de resolver mi problema en .netcore, pasando de 1.1 a 2.0 parece que el cliente estaba haciendo la descompresión automáticamente, así que tuve que agregar este código en 2.0 para que funcione ... Gracias !
Sebastian Castaldi
3
Solo para aprovechar @SebastianCastaldi, pero .net core 1.1 tenía AutomaticDecompression configurado correctamente, pero en .net core 2.0 está configurado en NONE. Esto me tomó demasiado tiempo para
darme
5
Nota: HttpClientNO debe usarse en interioresusing
imba-tjd
1

Ok, finalmente resolví mi problema. Si hay mejores formas, hágamelo saber :-)

        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;
    }

El RootObject contiene el conjunto get que obtendrá los valores del JSON.

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; }

}

La forma más fácil de crear la (s) clase (s) anterior (es) es usar json2charp que lo formateará en consecuencia y también proporcionará los tipos de datos correctos.

Lo siguiente es de otra respuesta en Stackoverflow nuevamente, no tiene en cuenta JSON anidado.

    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;
        }
    }
};

Luego, finalmente, para insertar el conjunto de datos anterior en una tabla con columnas que se asignaron al JSON, utilicé una copia masiva de SQL y la siguiente clase

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();
        }         
    }
};

Entonces, lo anterior funciona para insertar JSON desde una webAPI en una base de datos. Esto es algo en lo que me pongo a trabajar. Pero de ninguna manera espero que sea perfecto. Si tiene alguna mejora, actualícela en consecuencia.

Corey
fuente
2
Debe crear su HttpClienty su HttpResponseinterior una using()declaración cada uno para garantizar la eliminación adecuada y oportuna y el cierre de las corrientes subyacentes.
Ian Mercer
1

Usé el código del enlace de abajo para descomprimir el flujo de GZip. Luego usé la matriz de bytes descomprimida para obtener el objeto JSON requerido. Espero que pueda ayudar a alguien.

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
fuente