การบีบอัดสตรีม GZip จากการตอบสนองของ HTTPClient


97

ฉันกำลังพยายามเชื่อมต่อกับ api ซึ่งส่งคืน GZip ที่เข้ารหัส JSON จากบริการ WCF (บริการ WCF ไปยังบริการ WCF) ฉันใช้HTTPClientเพื่อเชื่อมต่อกับ API และสามารถส่งคืนออบเจ็กต์ JSON เป็นสตริงได้ อย่างไรก็ตามฉันต้องสามารถจัดเก็บข้อมูลที่ส่งคืนนี้ในฐานข้อมูลและด้วยเหตุนี้ฉันจึงคิดว่าวิธีที่ดีที่สุดคือการส่งคืนและจัดเก็บออบเจ็กต์ JSON ในอาร์เรย์หรือไบต์หรือบางอย่างตามบรรทัดเหล่านั้น

สิ่งที่ฉันมีปัญหาโดยเฉพาะคือการคลายการบีบอัดการเข้ารหัส GZip และได้ลองใช้ตัวอย่างต่างๆมากมาย แต่ก็ยังไม่สามารถรับได้

โค้ดด้านล่างคือวิธีที่ฉันสร้างการเชื่อมต่อและรับการตอบกลับนี่คือรหัสที่ส่งคืนสตริงจาก 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 "";
    }
}

ฉันได้ติดตามตัวอย่างที่แตกต่างกันเล็กน้อยเช่นStackExchange API , MSDNและอีกสองสามรายการใน stackoverflow แต่ฉันไม่สามารถหาสิ่งเหล่านี้มาใช้งานได้

อะไรคือวิธีที่ดีที่สุดในการทำสิ่งนี้ให้สำเร็จฉันมาถูกทางแล้วหรือยัง?

ขอบคุณเพื่อน.


"วิธีที่ดีที่สุดคือส่งคืนและจัดเก็บออบเจ็กต์ JSON ในอาร์เรย์หรือไบต์" โปรดทราบว่าสตริงเป็นอาร์เรย์ของไบต์
user3285954

คำตอบ:


239

เพียงแค่สร้างอินสแตนซ์ HttpClient ดังนี้:

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

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

อัปเดต 19 มิถุนายน 2020: ไม่แนะนำให้ใช้ httpclient ในบล็อก 'โดยใช้' เนื่องจากอาจทำให้พอร์ตหมดได้

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+ ให้ลองใช้IHttpClientFactoryและฉีดสิ่งนี้ในรหัสเริ่มต้นของคุณ

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

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

หากฉันใช้โครงสร้างนี้ฉันจะดึงเนื้อหาของการตอบกลับจาก httpClient ได้อย่างไร ฉันใหม่มากสำหรับ c # และฉันไม่คิดว่าจะได้รับมัน
FoxDeploy

1
@FoxDeploy ไม่มีการเปลี่ยนแปลงที่จำเป็นสำหรับรหัสเพื่อรับเนื้อหาเมื่อคุณใช้โซลูชันนี้ ดูข้อมูลอ้างอิงได้ที่นี่: stackoverflow.com/questions/26597665/…
DIG

1
แม้ว่าจะเป็นโพสต์เก่า แต่คำตอบนี้ช่วยแก้ปัญหาของฉันใน. netcore ได้โดยย้ายจาก 1.1 เป็น 2.0 ดูเหมือนว่าไคลเอนต์กำลังทำการคลายการบีบอัดโดยอัตโนมัติดังนั้นฉันจึงต้องเพิ่มรหัสนี้ใน 2.0 เพื่อให้ใช้งานได้ ... ขอบคุณ !
Sebastian Castaldi

3
เพียงแค่กลับไปที่ @SebastianCastaldi แต่. net core 1.1 มี AutomaticDecompression ตั้งค่าอย่างถูกต้อง แต่ใน. net core 2.0 จะถูกตั้งค่าเป็น NONE ฉันใช้เวลานานเกินกว่าจะคิดออก ...
KallDrexx

5
หมายเหตุ: HttpClientไม่ควรใช้ภายในusing
imba-tjd

1

โอเคในที่สุดฉันก็แก้ปัญหาได้ หากมีวิธีที่ดีกว่าโปรดแจ้งให้เราทราบ :-)

        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 มี get set ที่จะรับค่าของ 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; }

}

วิธีที่ง่ายที่สุดในการสร้างคลาสด้านบนคือการใช้json2charpซึ่งจะจัดรูปแบบตามนั้นและยังระบุประเภทข้อมูลที่ถูกต้อง

ต่อไปนี้มาจากคำตอบอื่นในStackoverflow อีกครั้งโดยไม่คำนึงถึง JSON ที่ซ้อนกัน

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

จากนั้นในที่สุดเพื่อแทรกชุดข้อมูลด้านบนลงในตารางที่มีคอลัมน์ที่แมปกับ JSON ฉันใช้ SQL จำนวนมากคัดลอกและคลาสต่อไปนี้

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

ดังนั้นข้างต้นจึงทำงานเพื่อแทรก JSON จาก webAPI ลงในฐานข้อมูล นี่คือสิ่งที่ฉันได้รับในการทำงาน แต่ฉันไม่คาดหวังว่ามันจะสมบูรณ์แบบ หากคุณมีการปรับปรุงใด ๆ โปรดอัปเดตตามนั้น


2
คุณควรสร้างคำชี้แจงภายในHttpClientของคุณและของคุณเพื่อให้แน่ใจว่ามีการกำจัดอย่างเหมาะสมทันเวลาและการปิดสตรีมพื้นฐาน HttpResponseusing()
Ian Mercer

1

ฉันใช้โค้ดจากลิงค์ด้านล่างเพื่อคลายการบีบอัดสตรีม GZip จากนั้นใช้อาร์เรย์ไบต์ที่คลายการบีบอัดเพื่อรับวัตถุ JSON ที่ต้องการ หวังว่ามันอาจช่วยได้บ้าง

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();
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.