Mendekompresi Aliran GZip dari Respons HTTPClient

97

Saya mencoba menyambung ke api, yang mengembalikan JSON yang dikodekan GZip, dari layanan WCF (layanan WCF ke layanan WCF). Saya menggunakan HTTPClient untuk menyambung ke API dan telah dapat mengembalikan objek JSON sebagai string. Namun saya harus dapat menyimpan data yang dikembalikan ini dalam database dan karena itu saya pikir cara terbaik adalah mengembalikan dan menyimpan objek JSON dalam array atau byte atau sesuatu di sepanjang garis itu.

Apa yang saya alami kesulitan secara khusus adalah dekompresi pengkodean GZip dan telah mencoba banyak contoh yang berbeda tetapi masih tidak bisa mendapatkannya.

Kode di bawah ini adalah bagaimana saya membangun koneksi saya dan mendapatkan respon, ini adalah kode yang mengembalikan string dari 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 "";
    }
}

Saya telah mengikuti beberapa contoh berbeda seperti ini StackExchange API , MSDN , dan beberapa di stackoverflow, tetapi saya belum bisa mendapatkan semua ini bekerja untuk saya.

Apa cara terbaik untuk mencapai ini, apakah saya bahkan berada di jalur yang benar?

Terima kasih teman-teman.

Corey
sumber
"cara terbaik adalah mengembalikan dan menyimpan objek JSON dalam larik atau byte" Perhatikan bahwa string adalah larik byte.
pengguna3285954

Jawaban:

239

Buat instance HttpClient seperti ini:

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

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

Pembaruan 19 Juni 2020: Tidak disarankan untuk menggunakan httpclient dalam blok 'menggunakan' karena dapat menyebabkan kehabisan port.

private static HttpClient client = null;

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

Jika menggunakan .Net Core 2.1+, pertimbangkan untuk menggunakan IHttpClientFactory dan menyuntikkan seperti ini ke kode startup Anda.

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

 services.AddHttpClient<XApiClient>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        }).AddPolicyHandler(request => timeout);
MENGGALI
sumber
Jika saya menggunakan struktur ini, bagaimana cara mengambil konten tanggapan saya dari httpClient? Saya sangat baru di c # dan saya rasa saya tidak mengerti.
FoxDeploy
1
@FoxDeploy tidak perlu mengubah kode untuk mendapatkan konten saat Anda menggunakan solusi ini. Lihat di sini untuk referensi: stackoverflow.com/questions/26597665/…
DIG
1
meskipun ini adalah posting lama, jawaban ini baru saja menyelesaikan masalah saya di .netcore, berpindah dari 1.1 ke 2.0 sepertinya klien secara otomatis melakukan dekompresi, jadi saya harus menambahkan kode ini di 2.0 untuk membuatnya berfungsi ... Terima kasih !
Sebastian Castaldi
3
Hanya untuk mendukung @SebastianCastaldi, tetapi .net core 1.1 memiliki AutomaticDecompression yang disetel dengan benar, tetapi di .net core 2.0 disetel ke NONE. Ini butuh waktu lama bagiku untuk mengetahuinya ...
KallDrexx
5
Catatan: HttpClientJANGAN digunakan di dalamusing
imba-tjd
1

Oke jadi saya akhirnya menyelesaikan masalah saya. Jika ada cara yang lebih baik, beri tahu saya :-)

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

RootObject berisi set get yang akan mendapatkan nilai 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; }

}

Cara termudah untuk membuat kelas di atas adalah dengan menggunakan json2charp yang akan memformatnya sesuai dan juga menyediakan tipe data yang benar.

Berikut ini adalah dari jawaban lain di Stackoverflow lagi itu tidak memperhitungkan JSON bersarang.

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

Kemudian akhirnya untuk memasukkan dataset di atas ke dalam tabel dengan kolom yang dipetakan ke JSON saya menggunakan salinan massal SQL dan kelas berikut

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

Jadi cara di atas berfungsi untuk memasukkan JSON dari webAPI ke dalam database. Ini adalah sesuatu yang saya mulai bekerja. Tapi saya tidak mengharapkannya sempurna. Jika Anda memiliki peningkatan, harap perbarui sesuai.

Corey
sumber
2
Anda harus membuat pernyataan Anda HttpClientdan Anda HttpResponsedi dalam using()masing-masing untuk memastikan pembuangan yang tepat dan tepat waktu serta penutupan aliran yang mendasarinya.
Ian Mercer
1

Saya menggunakan kode dari tautan di bawah ini untuk mendekompresi aliran GZip. Kemudian menggunakan array byte yang didekompresi untuk mendapatkan objek JSON yang diperlukan. Semoga ini bisa membantu seseorang.

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
sumber