【问题标题】:Universal Rest Client in C# for different REST API'sC# 中用于不同 REST API 的 Universal Rest Client
【发布时间】:2017-06-05 02:57:58
【问题描述】:

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

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

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

任何帮助将不胜感激

【问题讨论】:

  • 听起来您可能正在寻找像 Swagger.io 这样的工具?
  • 如果我在 XML 文件中提供 API 方法,编写一个 XML 解析器来获取并在 API Server 上执行这些方法。将响应存储在数据结构中。解析模板 JSON 文件并查找 API 响应输出以处理最终输出,然后生成 csv。这听起来像是一种可扩展的方法吗?

标签: c# json rest client


【解决方案1】:

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

如果你有一个从多个 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>>();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 2023-04-04
    • 2014-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多