RestSharp 简介

官方:RestSharp 可能是.NET 最流行的 HTTP 客户端库。它具有自动序列化和反序列化、请求和响应类型检测、多种身份验证和其他有用功能,正被数十万个项目使用。

RestSharp 的主要目的是通过 HTTP 对远程资源进行同步和异步调用。顾名思义,RestSharp 的主要受众是使用 REST API 的开发人员。只要您具有要发送的资源 URI 和请求参数符合 W3C HTTP 标准,RestSharp 就 可以通过 HTTP 调用任何 API。

由于 WMS 会出现作为 HTTP 客户端去调用 API 的场景,所以就用到了 RestSharp。之前本人有自己封装过一个简单的 HttpClient 库,也使用了一段时间,但是考虑到后续需求的多样性,所以还是使用了 RestSharp 进行开发,项目中我也对其进行了简单的二次封装,方便使用。

关于 RestSharp 的快速上手,这里我就不赘述了,官方提供了很详细的文档,建议大家自行学习使用,下面我会介绍 WMS 中对 RestSharp 的二次封装和使用,另外也说明下使用过程中遇到的各种坑。

RestSharp 二次封装

封装位置

在进行封装前,先考虑将其封装哪一层,这里我们既然将其封装为一个通用工具,让每个项目都可以使用,自然也就考虑放在Wpf.MvvmLight.SelfHost.Common这一层了。

安装包

打开Wpf.MvvmLight.SelfHost.Common项目后,可以通过 nuget 包管理工具直接搜索"RestSharp"进行包的安装,也可以使用 IDE 或命令行来进行包的安装:

dotnet add package RestSharp

开始封装

添加完 RestSharp 包后,接下来就可以进入正式的封装工作了。这里我对 RestSharp 进行了泛型和非泛型的两种封装,每种封装内分别提供了同步和异步方法,可供大家参考使用,如果有其他的需求,同样也可在此封装的基础上继续进行拓展。

非泛型封装

public class RangeRestApi
  {
    #region 同步

    public string Get(string url, object pars = null)
    {
      var type = Method.Get;
      RestResponse reval = GetApiInfo(url, pars, type);
      return reval.Content;
    }
    public string Post(string url, object pars = null)
    {
      var type = Method.Post;
      RestResponse reval = GetApiInfo(url, pars, type);
      return reval.Content;
    }
    public string Delete(string url, object pars = null)
    {
      var type = Method.Delete;
      RestResponse reval = GetApiInfo(url, pars, type);
      return reval.Content;
    }
    public string Put(string url, object pars = null)
    {
      var type = Method.Put;
      RestResponse reval = GetApiInfo(url, pars, type);
      return reval.Content;
    }

    private static RestResponse GetApiInfo(string url, object pars, Method type)
    {
      var request = new RestRequest();
      request.Method = type;
      if (pars != null)
      {
        request.AddObject(pars);
      }
      var client = new RestClient(url);
      RestResponse reval = client.Execute(request);
      if (reval.ErrorException != null)
      {
        throw new Exception($"{type}请求出错:{reval.ErrorException.Message}");
      }
      return reval;
    }

    #endregion

    #region 异步

    public async Task<string> GetAsync(string url, object pars = null)
    {
      var type = Method.Get;
      RestResponse reval = await GetApiInfoAsync(url, pars, type);
      return reval.Content;
    }
    public async Task<string> PostAsync(string url, object pars = null)
    {
      var type = Method.Post;
      RestResponse reval = await GetApiInfoAsync(url, pars, type);
      return reval.Content;
    }
    public async Task<string> DeleteAsync(string url, object pars = null)
    {
      var type = Method.Delete;
      RestResponse reval = await GetApiInfoAsync(url, pars, type);
      return reval.Content;
    }
    public async Task<string> PutAsync(string url, object pars = null)
    {
      var type = Method.Put;
      RestResponse reval = await GetApiInfoAsync(url, pars, type);
      return reval.Content;
    }

    private static async Task<RestResponse> GetApiInfoAsync(string url, object pars, Method type)
    {
      var request = new RestRequest();
      request.Method = type;
      if (pars != null)
      {
        request.AddObject(pars);
      }
      var client = new RestClient(url);
      RestResponse reval = await client.ExecuteAsync(request);
      if (reval.ErrorException != null)
      {
        throw new Exception($"{type}请求出错:{reval.ErrorException.Message}");
      }
      return reval;
    }

泛型封装

public class RangeRestApi<T> where T : class, new()
  {
    #region 同步
    public T Get(string url, object pars = null)
    {
      var type = Method.Get;
      RestResponse<T> reval = GetApiInfo(url, pars, type);
      return reval.Data;
    }
    public T Post(string url, object pars = null)
    {
      var type = Method.Post;
      RestResponse<T> reval = GetApiInfo(url, pars, type);
      return reval.Data;
    }
    public T Delete(string url, object pars = null)
    {
      var type = Method.Delete;
      RestResponse<T> reval = GetApiInfo(url, pars, type);
      return reval.Data;
    }
    public T Put(string url, object pars = null)
    {
      var type = Method.Put;
      RestResponse<T> reval = GetApiInfo(url, pars, type);
      return reval.Data;
    }

    private static RestResponse<T> GetApiInfo(string url, object pars, Method type)
    {
      var request = new RestRequest();
      request.Method = type;
      if (pars != null)
      {
        request.AddObject(pars);
      }
      var client = new RestClient(url);
      RestResponse<T> reval = client.Execute<T>(request);
      if (reval.ErrorException != null)
      {
        throw new Exception($"{type}请求出错:{reval.ErrorException.Message}");
      }
      return reval;
    }

    #endregion

    #region 异步

    public async Task<T> GetAsync(string url, object pars = null)
    {
      var type = Method.Get;
      RestResponse<T> reval = await GetApiInfoAsync(url, pars, type);
      return reval.Data;
    }
    public async Task<T> PostAsync(string url, object pars = null)
    {
      var type = Method.Post;
      RestResponse<T> reval = await GetApiInfoAsync(url, pars, type);
      return reval.Data;
    }
    public async Task<T> DeleteAsync(string url, object pars = null)
    {
      var type = Method.Delete;
      RestResponse<T> reval = await GetApiInfoAsync(url, pars, type);
      return reval.Data;
    }
    public async Task<T> PutAsync(string url, object pars = null)
    {
      var type = Method.Put;
      RestResponse<T> reval = await GetApiInfoAsync(url, pars, type);
      return reval.Data;
    }

    private static async Task<RestResponse<T>> GetApiInfoAsync(string url, object pars, Method type)
    {
      var request = new RestRequest();
      request.Method = type;
      if (pars != null)
      {
        request.AddObject(pars);
      }
      var client = new RestClient(url);
      RestResponse<T> reval = await client.ExecuteAsync<T>(request);
      if (reval.ErrorException != null)
      {
        throw new Exception($"{type}请求出错:{reval.ErrorException.Message}");
      }
      return reval;
    }

    #endregion
  }

开始使用

封装完成后,即可在项目中进行使用,下面提供了发送 Post 和 Get 异步请求的两种实例(同框架中实例):

private async Task SendPostRequest()
    {
      var response = await new RangeRestApi().PostAsync("http://127.0.0.1:9000/api/v1/home/echo", new { name = "Range post" });
      EventSignal.SendWriteDebugLogSignal($"获取POST请求响应:{response}");
    }

    private async Task SendGetRequest()
    {
      var response = await new RangeRestApi().GetAsync("http://127.0.0.1:9000/api/v1/home/echo", new { name = "Range get" });
      EventSignal.SendWriteDebugLogSignal($"获取GET请求响应:{response}");
    }

遇坑与填坑

总希望一切工作都可以顺利进行,但总是事与愿违,这次同样也不例外。

1.System.Net.Http 版本包问题

遇坑:一切封装和开发工作完成后,启动项目报错:

方法System.Net.Http.CloneableExtensions.Clone: 类型参数"System.Net.Http.Headers.MediaTypeHeaderValue"与类型参数"T"的约束冲突。

原因:项目在.NET 4.6.1 框架下集成了 SelfHost,而 SelfHost 中用到的 Web Api 的包与项目中的其他包产生了版本冲突。

填坑:提供两种解决方法供参考,第一,将项目中的 Nuget 包 System.Net.Http 更新到 4.3.1 版本以上即可;第二(不推荐),更改项目下 App.config 文件中的配置:

<dependentAssembly>
    <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.0.0.0" />
    </dependentAssembly>

2.System.ValueTuple 版本包问题

遇坑:一切封装和开发工作完成后,项目正常启动,但是通过 RestSharp 发送 Web 请求时,抛出异常:

未能加载文件或程序集"System.ValueTuple, Version=4.0.2.0,Culture=neutral......"

原因:RestSharp 中需要的 System.ValueTuple 包版本与当前项目.NET 4.6.1 框架下提供的该包版本不匹配。

填坑:提供两种解决方法供参考,第一,将 System.ValueTuple 包升级/降级到 4.3.0 版本即可;第二(不推荐),修改 App.config 文件中的配置,注释掉绑定重定向标签即可(或者直接修改版本):

<dependentAssembly>
    <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
    <!--<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />-->
</dependentAssembly>

3.可能遇到的其他版本包问题

上面两个坑是我项目开发中遇到的,如果有遇到类似上面的其他版本包问题,填坑方法也是类似的。