GZip Akışını HTTPClient Yanıtından Açma


97

Bir WCF hizmetinden (WCF hizmetinden WCF hizmetine) GZip kodlu JSON döndüren bir api'ye bağlanmaya çalışıyorum. API'ye bağlanmak için HTTPClient kullanıyorum ve JSON nesnesini bir dize olarak döndürebildim. Bununla birlikte, bu döndürülen verileri bir veritabanında saklayabilmem gerekiyor ve bu nedenle en iyi yolun JSON nesnesini bir dizi veya bayt veya bu satırlar boyunca bir şeye döndürmek ve depolamak olacağını düşündüm.

Özellikle sorun yaşadığım şey GZip kodlamasının sıkıştırmasının açılması ve birçok farklı örnek deniyor ama yine de alamıyorum.

Aşağıdaki kod, bağlantımı nasıl kuruyorum ve bir yanıt alıyorum, bu, API'den bir dize döndüren koddur.

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

Bu StackExchange API , MSDN ve stackoverflow'daki bir çift gibi birkaç farklı örneği takip ediyorum, ancak bunlardan hiçbirinin benim için çalışmasını sağlayamadım.

Bunu başarmanın en iyi yolu nedir, hatta doğru yolda mıyım?

Teşekkürler beyler.


"en iyi yol, JSON nesnesini bir dizi veya baytta döndürmek ve depolamaktır" Bir dizenin bir bayt dizisi olduğunu unutmayın.
user3285954

Yanıtlar:


239

Sadece HttpClient'i şu şekilde somutlaştırın:

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

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

Güncelleme 19 Haziran 2020: Bağlantı noktasının tükenmesine neden olabileceğinden httpclient'i 'kullanma' bloğunda kullanmanız önerilmez.

private static HttpClient client = null;

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

.Net Core 2.1+ kullanıyorsanız, IHttpClientFactory kullanmayı ve başlangıç ​​kodunuza bu şekilde enjekte etmeyi düşünün .

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

 services.AddHttpClient<XApiClient>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        {
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        }).AddPolicyHandler(request => timeout);

Bu yapıyı kullanırsam, yanıtımın içeriğini httpClient'ten nasıl alırım? C # konusunda çok yeniyim ve anladığımı sanmıyorum.
FoxDeploy

1
@FoxDeploy, bu çözümü kullandığınızda içeriği almak için kodda değişiklik yapılmasına gerek yoktur. Referans için buraya bakın: stackoverflow.com/questions/26597665/…
DIG

1
eski bir gönderi olmasına rağmen, bu cevap .netcore'daki problemimi çözdü, 1.1'den 2.0'a geçerken, müşteri otomatik olarak dekompresyon yapıyor gibi görünüyor, bu yüzden çalışmasını sağlamak için bu kodu 2.0'a eklemek zorunda kaldım ... Teşekkürler !
Sebastian Castaldi

3
Sadece @SebastianCastaldi'ye geri dönmek için, ancak .net çekirdeği 1.1'de Otomatik Sıkıştırma düzgün ayarlanmıştı, ancak .net çekirdek 2.0'da YOK olarak ayarlandı. Bunu
anlamam

5
Not: HttpClientİçeride KULLANILMAMALIDIRusing
imba-tjd

1

Tamam, sonunda sorunumu çözdüm. Daha iyi yollar varsa lütfen bana bildirin :-)

        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, JSON'un değerlerini alacak get setini içerir.

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

}

Yukarıdaki sınıfları oluşturmanın en kolay yolu, onu uygun şekilde biçimlendirecek ve aynı zamanda doğru veri türlerini sağlayacak json2charp'ı kullanmaktır .

Aşağıdakiler, Stackoverflow'daki başka bir cevaptan yine iç içe geçmiş JSON'u hesaba katmaz.

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

Son olarak yukarıdaki veri kümesini JSON ile eşlenmiş sütunların bulunduğu bir tabloya eklemek için SQL toplu kopyasını ve aşağıdaki sınıfı kullandım

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

Dolayısıyla yukarıdaki, bir webAPI'den JSON'u bir veritabanına eklemek için çalışır. Bu benim çalıştığım bir şey. Ama hiçbir şekilde mükemmel olmasını beklemiyorum. Herhangi bir iyileştirmeniz varsa, lütfen uygun şekilde güncelleyin.


2
Altta yatan akışların uygun şekilde, zamanında bertaraf edilmesini ve kapatılmasını sağlamak için kendi HttpClientve içinizin her biri HttpResponsebirer using()ifade oluşturmalısınız .
Ian Mercer

1

GZip akışını açmak için aşağıdaki linkteki kodu kullandım, ardından gerekli JSON nesnesini elde etmek için sıkıştırılmış bayt dizisini kullandım. Umarım birine yardımcı olabilir.

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();
        }
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.