C#中的Universal Rest Client用于不同的REST API

时间:2017-06-05 02:57:58

标签: c# json rest client

我想在C#中编写一个REST客户端,它可以从配置文件中读取API调用,处理JSON响应并相应地生成csv文件。 将有另一个配置文件包含每个API调用返回的属性。此客户端应阅读此模板配置文件并处理响应。

客户端应该可以与生成JSON响应的所有第三方休息api一起使用。唯一的区别是每个数据源会有不同的配置文件和模板文件。

有没有其他方法可以实现上述功能? 是否有任何第三方库可以帮助实现这一目标? 什么应该是一个很好的设计方法来实现这一目标?

非常感谢任何帮助

1 个答案:

答案 0 :(得分:0)

是的。有一种方法可以实现此功能,但听起来好像您在这里询问多个问题。您可能应该将问题分成多个部分。

如果您有一个配置文件可以从多个Rest源中提取数据,则可以使用基于HttpClient的简单Rest客户端,如下所示:

此处的图书馆:https://github.com/MelbourneDeveloper/RestClient.Net

  public class RestClient
    {
        #region Fields
        private readonly HttpClient _HttpClient = new HttpClient();
        #endregion

        #region Public Properties
        public Uri BaseUri => _HttpClient.BaseAddress;
        public Dictionary<string, string> Headers { get; private set; } = new Dictionary<string, string>();
        public AuthenticationHeaderValue Authorization { get; set; }
        public Dictionary<HttpStatusCode, Func<byte[], object>> HttpStatusCodeFuncs = new Dictionary<HttpStatusCode, Func<byte[], object>>();
        public IZip Zip;
        public ISerializationAdapter SerializationAdapter { get; }
        #endregion

        #region Constructor
        public RestClient(ISerializationAdapter serializationAdapter, Uri baseUri)
        {
            _HttpClient.BaseAddress = baseUri;
            _HttpClient.Timeout = new TimeSpan(0, 3, 0);
            SerializationAdapter = serializationAdapter;
        }
        #endregion

        #region Private Methods
        private async Task<T> Call<T>(string queryString, HttpVerb httpVerb, string contentType, object body = null)
        {
            _HttpClient.DefaultRequestHeaders.Clear();

            if (Authorization != null)
            {
                _HttpClient.DefaultRequestHeaders.Authorization = Authorization;
            }

            HttpResponseMessage result = null;
            var isPost = httpVerb == HttpVerb.Post;
            if (!isPost)
            {
                _HttpClient.DefaultRequestHeaders.Clear();
                foreach (var key in Headers.Keys)
                {
                    _HttpClient.DefaultRequestHeaders.Add(key, Headers[key]);
                }
            }

            string bodyString = null;
            StringContent stringContent = null;
            byte[] data = null;

            switch (httpVerb)
            {
                case HttpVerb.Post:

                    if (body is string bodyAsString)
                    {
                        bodyString = bodyAsString;
                    }
                    else
                    {
                        if (body != null)
                        {
                            data = await SerializationAdapter.SerializeAsync(body);
                            bodyString = SerializationAdapter.Encoding.GetString(data);
                        }
                        else
                        {
                            bodyString = string.Empty;
                        }
                    }

                    stringContent = new StringContent(bodyString, Encoding.UTF8, contentType);

                    //Don't know why but this has to be set again, otherwise more text is added on to the Content-Type header...
                    stringContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);

                    stringContent.Headers.ContentLength = bodyString.Length;

                    foreach (var key in Headers.Keys)
                    {
                        stringContent.Headers.Add(key, Headers[key]);
                    }

                    result = await _HttpClient.PostAsync(queryString, stringContent);
                    break;

                case HttpVerb.Get:
                    result = await _HttpClient.GetAsync(queryString);
                    break;
                case HttpVerb.Delete:
                    result = await _HttpClient.DeleteAsync(queryString);
                    break;

                case HttpVerb.Put:
                case HttpVerb.Patch:

                    data = await SerializationAdapter.SerializeAsync(body);
                    bodyString = SerializationAdapter.Encoding.GetString(data);
                    var length = bodyString.Length;
                    stringContent = new StringContent(bodyString, SerializationAdapter.Encoding, contentType);

                    if (httpVerb == HttpVerb.Put)
                    {
                        result = await _HttpClient.PutAsync(queryString, stringContent);
                    }
                    else
                    {
                        var method = new HttpMethod("PATCH");
                        var request = new HttpRequestMessage(method, queryString)
                        {
                            Content = stringContent
                        };
                        result = await _HttpClient.SendAsync(request);
                    }
                    break;
            }

            if (result.IsSuccessStatusCode)
            {
                var gzipHeader = result.Content.Headers.ContentEncoding.FirstOrDefault(h => !string.IsNullOrEmpty(h) && h.Equals("gzip", StringComparison.InvariantCultureIgnoreCase));
                if (gzipHeader != null && Zip != null)
                {
                    var bytes = await result.Content.ReadAsByteArrayAsync();
                    data = Zip.Unzip(bytes);
                }
                else
                {
                    data = await result.Content.ReadAsByteArrayAsync();
                }

                return await SerializationAdapter.DeserializeAsync<T>(data);
            }

            var errorData = await result.Content.ReadAsByteArrayAsync();

            if (HttpStatusCodeFuncs.ContainsKey(result.StatusCode))
            {
                return (T)HttpStatusCodeFuncs[result.StatusCode].Invoke(errorData);
            }

            throw new HttpStatusException($"{result.StatusCode}.\r\nBase Uri: {_HttpClient.BaseAddress}. Full Uri: {_HttpClient.BaseAddress + queryString}", result.StatusCode, errorData);
        }
        #endregion

        #region Public Methods
        public async Task<T> GetAsync<T>()
        {
            return await GetAsync<T>(null);
        }

        public async Task<T> GetAsync<T>(string queryString, string contentType = "application/json")
        {
            return await Call<T>(queryString, HttpVerb.Get, contentType);
        }

        public async Task<TReturn> PostAsync<TReturn, TBody>(TBody body, string queryString, string contentType = "application/json")
        {
            return await Call<TReturn>(queryString, HttpVerb.Post, contentType, body);
        }

        public async Task<TReturn> PutAsync<TReturn, TBody>(TBody body, string queryString, string contentType = "application/json")
        {
            return await Call<TReturn>(queryString, HttpVerb.Put, contentType, body);
        }

        public async Task DeleteAsync(string queryString, string contentType = "application/json")
        {
            await Call<object>(queryString, HttpVerb.Delete, contentType, null);
        }

        public async Task<TReturn> PatchAsync<TReturn, TBody>(TBody body, string queryString, string contentType = "application/json")
        {
            return await Call<TReturn>(queryString, HttpVerb.Patch, contentType, body);
        }
        #endregion
}

用法:

var countryCodeClient = new RestClientDotNet.RestClient(new NewtonsoftSerializationAdapter(), new Uri("http://services.groupkt.com/country/get/all"));
var countryData = await countryCodeClient.GetAsync<groupktResult<CountriesResult>>();