【问题标题】:Decompressing GZip Stream from HTTPClient Response从 HTTPClient 响应中解压 GZip 流
【发布时间】:2021-10-29 23:17:50
【问题描述】:

我正在尝试连接到从 WCF 服务(WCF 服务到 WCF 服务)返回 GZip 编码 JSON 的 api。我正在使用 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 APIMSDN 和 stackoverflow 上的几个示例,但我无法让其中任何一个为我工作。

实现这一目标的最佳方法是什么,我是否走在正确的轨道上?

谢谢大家。

【问题讨论】:

  • "最好的方法是返回 JSON 对象并将其存储在一个数组或字节中" 请注意,字符串是一个字节数组。

标签: c# json wcf gzip


【解决方案1】:

只需像这样实例化 HttpClient:

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

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

2020 年 6 月 19 日更新: 不建议在 'using' 块中使用 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);

【讨论】:

  • @FoxDeploy 使用此解决方案时,无需更改代码即可获取内容。请参阅此处以供参考:stackoverflow.com/questions/26597665/…
  • 虽然是老帖子,但这个答案刚刚解决了我在 .netcore 中的问题,从 1.1 移动到 2.0 似乎客户端自动进行了解压,所以我不得不在 2.0让它工作......谢谢!
  • 只是为了背靠@SebastianCastaldi,但.net core 1.1 正确设置了AutomaticDecompression,但在.net core 2.0 中它设置为NONE。这花了我太长时间才弄清楚......
  • 注意:HttpClient不应该在using中使用
  • np!当我们集群中的主机上的每个 VM 停止工作并抱怨端口时,我学到了很难的方法。花了数周时间才发现这是一个没有正确使用 HttpClient 的应用程序。
【解决方案2】:

我使用下面链接中的代码来解压缩 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();
        }
    }
}

【讨论】:

    【解决方案3】:

    好的,我最终解决了我的问题。如果有更好的方法请告诉我:-)

            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 值的 get 集。

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

    因此,上述方法可以将来自 webAPI 的 JSON 插入数据库。这是我开始工作的事情。但我绝不指望它是完美的。如果您有任何改进,请相应更新。

    【讨论】:

    • 您应该在using() 语句中分别创建您的HttpClientHttpResponse,以确保正确、及时地处理和关闭底层流。
    猜你喜欢
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多