[c#] HTTPClient 응답에서 GZip 스트림 압축 해제

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 API 와 같은 몇 가지 다른 예를 따라 왔습니다 . MSDN 및 몇 가지 stackoverflow와 중 어느 것도 나를 위해 작동하지 못했습니다.

이를 수행하는 가장 좋은 방법은 무엇입니까? 내가 올바른 길을 가고 있습니까?

감사합니다.



답변

다음과 같이 HttpClient를 인스턴스화하십시오.

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

using (var client = new HttpClient(handler))
{
    // 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);


답변

그래서 결국 내 문제를 해결했습니다. 더 좋은 방법이 있으면 알려주세요 🙂

        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을 데이터베이스에 삽입하는 작업입니다. 이것은 내가 일하게되는 것입니다. 그러나 나는 그것이 완벽 할 것이라고 결코 기대하지 않습니다. 개선 사항이 있으면 그에 따라 업데이트하십시오.


답변

아래 링크의 코드를 사용하여 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();
        }
    }
}


답변