65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ASP.NET MVC4 和 RestSharp 消费 ASP.NET WEB API

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (24投票s)

2014 年 10 月 6 日

CPOL

8分钟阅读

viewsIcon

167415

downloadIcon

5723

在本文中,我们将学习如何使用 ASP.NET MVC4(作为客户端)和 RESTSHARP 来消费托管在另一台服务器上的 ASP.NET WEB API(仅作为 REST 服务)。

Snapshot of demo project

目录

引言

在大多数场景下,我们使用 ASP.NET WEB API 来开发 ASP.NET MVC4 应用程序。但是,像 ASP.NET WEB API 这样的 RESTFul 服务 meant to be used by different clients。在本文中,我们将使用 ASP.NET MVC4(作为客户端)和 RestSharp(.NET 的简单 REST 和 HTTP 客户端)来消费简单的 ASP.NET WEB API。

背景

在阅读本文之前,我建议您阅读我之前的 文章,其中我定义了如何使用 FluentNHibernate 和 Repository Pattern 创建 ASP.NET WEB API(CRUD 操作)。

先决条件

要实现并运行源代码,您应该具备

  • Visual Studio 2013 及更高版本
  • ASP.NET MVC4 及更高版本
  • ASP.NET WEB API2 及更高版本
  • RestFul 服务基础知识
  • ASP.NET WEB API 基础知识
  • ASP.NET MVC4 及更高版本基础知识

为什么要使用 RestSharp?

由于我们将使用 RestSharp 调用我们的 ASP.NET WEB API URI,因此,首先,让我们讨论 "RestSharp"。它只是 .NET 的一个简单 REST 和 HTTP API 客户端。它提供了一种简单的方式,我们可以初始化 RestClient,传递 Request,定义 Method (GET, POST, PUT, DELETE) 并获得 response

以下是常用的代码片段

//Create a restclient
RestClient restClient = new RestClient("url");

//Create request with GET
var request = new RestRequest("api/serverdata", Method.GET);
  • 不要忘记提供 RequestFormatRestSharp 会自动序列化/反序列化复杂对象,例如我们演示项目中的 ServerDataModel
    //Do not forget while using complex types
    var request = new RestRequest("api/serverdata", Method.GET)
     {RequestFormat = DataFormat.Json};
  • 获取复杂类型的格式化结果
    var response = restClient.Execute<List<ServerDataModel>>(request);

本文未涵盖许多其他功能。请参考 RestSharp Wiki

我们要消费什么?

如前所述,在本文中,我们将消费现有的 ASP.NET WEB API URI(托管在服务器上),下表列出了所有 URI。

操作 HTTP 方法 相对 URI
获取服务器数据列表 GET /api/serverdata
按 ID 获取服务器数据 GET /api/serverdata/id
按数据类型获取服务器数据 GET /api/serverdata/type/datatype
按机器 IP 获取服务器数据 GET /api/serverdata/ip/ip
创建一个新的服务器数据 POST /api/serverdata
更新现有服务器数据 PUT /api/serverdata/id
删除现有服务器数据 删除 /api/serverdata/id

请参考演示应用程序:演示 ASP.NET WEB API

创建 Web 项目

让我们按照以下步骤开始创建我们的新 ASP.NET 项目(客户端项目)。

启动 Visual Studio,选择 文件->新建->项目(或按 Ctrl + Shift + N)。

Create new asp.net mvc4 web project

模板 对话框中,选择 已安装模板,然后展开 Visual C# 节点。在 Visual C# 下,选择 Web。在项目模板列表中,选择 ASP.NET MVC4 Web 应用程序,为项目命名,然后单击“确定”。

我将我的项目命名为 consumewebapi,您可以选择任何您喜欢的名称。 :)

项目模板 中,选择 Internet 应用程序,并从 视图引擎 下拉列表中选择 Razor,然后单击“确定”。

Select new project template of asp.net mvc4

如果您正在进行 测试驱动开发,也可以选中 创建单元测试项目 复选框,我强烈推荐这样做。本文不涵盖这部分内容。

现在,在完成上述步骤后,我们已经拥有了默认的 ASP.NET MVC4 项目模板,稍后我们将添加我们的内容。

Default folder structure of asp.net mvc4 project template

向项目添加 RestSharp 支持

我们将使用 RestSharp 来消费我们的 ASP.NET WEB API,请按照以下步骤操作:

转到 工具->NuGet 包管理器->包管理器控制台

在包管理器控制台中,运行以下命令:

Install-Package RestSharp

Install restsharp from nuget command line

它将为您的 consumewebapi 项目安装 RestSharp 的当前版本。

添加 Rest 客户端

  1. 在解决方案资源管理器中,创建一个新文件夹,并将其命名为 Helper - 此文件夹将包含我们所需的所有 RestSharp 客户端的帮助程序类(这些类将在我们的客户端和服务之间发挥接口作用)
  2. 添加一个接口,并将其命名为 IServerDataRestClient

    IServerDataRestClient 的完整代码如下:

    public interface IServerDataRestClient
    {
        void Add(ServerDataModel serverDataModel);
        void Delete(int id);
        IEnumerable<serverdatamodel> GetAll();
        ServerDataModel GetById(int id);
        ServerDataModel GetByIP(int ip);
        ServerDataModel GetByType(int type);
        void Update(ServerDataModel serverDataModel);
    }
  3. Helper 文件夹下添加新类,并将其命名为 ServerDataRestClient,然后实现接口 IServerDataRestClient
    public class ServerDataRestClient : IServerDataRestClient
    {
     //implementation
    }

我们需要创建一个 RestClient,在类中创建一个变量 private readonly RestClient _client; 并在构造函数中对其进行初始化。

//initializing RestClient
public ServerDataRestClient()
{
 _client = new RestClient(_url);
}

在上面,当我们初始化 RestClient 对象时,我们提供了 BaseUrl 什么都没有,只是一个完整的 URL 名称,例如 http://myexample.com/

我们也可以这样做:

 _client = new RestClient {BaseUrl = _url};

我们从 config 文件中获取基础 URL,为什么它在 config 文件中?这对于大型项目非常有用,因为我们有不同的环境,例如 Dev, Staging,QA, Prod 等。

//getting base url from config
private readonly string _url = ConfigurationManager.AppSettings["webapibaseurl"];

现在,我们需要一个请求它只是包含一个资源和一个方法

Resource and Method

通过提供 {RequestFormat = DataFormat.Json};,我们告诉 RestClient 以 Json 的形式提供输出。这在我们处理复杂类时是必需的,例如我们在这里使用 ServerDataModel 类。

在 DEBUG 模式下,在快速监视(Ctrl + D,Q)中检查 _client,您将找到我们在创建 RestClient 对象时设置的所有内容。

Internal of RestClient

我们的客户端已准备好传输请求并获取响应。

var response = _client.Execute<List<ServerDataModel>>(request);

上面的代码将以 ServerDataModel 类的列表形式提供输出。

打开 response 的快速监视,您将看到两个节点 [RestSharp.RestResponse<System.Collections.Generic.List<ConsumeWebAPI.Models.ServerDataModel>>] {RestSharp.RestResponse<System.Collections.Generic.List<ConsumeWebAPI.Models.ServerDataModel>>} RestSharp.RestResponse<System.Collections.Generic.List<ConsumeWebAPI.Models.ServerDataModel>> Data.

Showing internal of Response using RestClient

第一个节点包含我们的实际内容(JSON 格式)以及其他信息,例如 ContentLengthContentTypeCookiesErrorMessageHeaderRequestResponseStatusStatusCode 等。

Data 节点包含我们请求和需要的格式化数据。在本例中,它是一个 ServerDataModel 类型的列表。我们将稍后在视图中使用此模型。

我们也可以请求一个特定的资源,例如如果我们按 ID 获取记录,那么我们可以这样做:

var request = new RestRequest("api/serverdata/{id}", Method.GET) {RequestFormat = DataFormat.Json};

request.AddParameter("id", id, ParameterType.UrlSegment);

下面的代码将提供 ServerDataModel 类型的输出:

var response = _client.Execute<serverdatamodel>(request);

这是我们完整的 ServerDataRestClient 帮助程序类:

public class ServerDataRestClient : IServerDataRestClient
    {
        private readonly RestClient _client;
        private readonly string _url = ConfigurationManager.AppSettings["webapibaseurl"];

        public ServerDataRestClient()
        {
            _client = new RestClient {BaseUrl = _url};
        }

        public IEnumerable<serverdatamodel> GetAll()
        {
            var request = new RestRequest
            ("api/serverdata", Method.GET) {RequestFormat = DataFormat.Json};

            var response = _client.Execute<list<serverdatamodel>>(request);

            if (response.Data == null)
                throw new Exception(response.ErrorMessage);

            return response.Data;
        }

        public ServerDataModel GetById(int id)
        {
            var request = new RestRequest
            ("api/serverdata/{id}", Method.GET) {RequestFormat = DataFormat.Json};

            request.AddParameter("id", id, ParameterType.UrlSegment);

            var response = _client.Execute<serverdatamodel>(request);

            if (response.Data == null)
                throw new Exception(response.ErrorMessage);

            return response.Data;
        }

        public ServerDataModel GetByType(int type)
        {
            var request = new RestRequest("api/serverdata/type/{datatype}", Method.GET)
            {
                RequestFormat = DataFormat.Json
            };

            request.AddParameter("datatype", type, ParameterType.UrlSegment);

            var response = _client.Execute<serverdatamodel>(request);

            return response.Data;
        }

        public ServerDataModel GetByIP(int ip)
        {
            var request = new RestRequest
            ("api/serverdata/ip/{ip}", Method.GET) {RequestFormat = DataFormat.Json};
            request.AddParameter("ip", ip, ParameterType.UrlSegment);

            var response = _client.Execute<serverdatamodel>(request);

            return response.Data;
        }

        public void Add(ServerDataModel serverData)
        {
            var request = new RestRequest
            ("api/serverdata", Method.POST) {RequestFormat = DataFormat.Json};
            request.AddBody(serverData);

            var response = _client.Execute<serverdatamodel>(request);

            if (response.StatusCode != HttpStatusCode.Created)
                throw new Exception(response.ErrorMessage);
        }

        public void Update(ServerDataModel serverData)
        {
            var request = new RestRequest
            ("api/serverdata/{id}", Method.PUT) {RequestFormat = DataFormat.Json};
            request.AddParameter("id", serverData.Id, ParameterType.UrlSegment);
            request.AddBody(serverData);

            var response = _client.Execute<serverdatamodel>(request);

            if (response.StatusCode == HttpStatusCode.NotFound)
                throw new Exception(response.ErrorMessage);
        }

        public void Delete(int id)
        {
            var request = new RestRequest("api/serverdata/{id}", Method.DELETE);
            request.AddParameter("id", id, ParameterType.UrlSegment);

            var response = _client.Execute<serverdatamodel>(request);

            if (response.StatusCode == HttpStatusCode.NotFound)
                throw new Exception(response.ErrorMessage);
        }
    }

创建模型类

解决方案资源管理器 中,右键单击 Models 文件夹,然后选择 添加->新建项(或按 Ctrl + Shift + A)。选择类,并将其命名为 ServerDataModel,然后单击“添加”。这将添加一个空的 Model 类。

这是我们完整的 model 类:

 public class ServerDataModel
    {
        public int Id { get; set; }
        [Required]
        [Display(Name = "Initial date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        public DateTime InitialDate { get; set; }
        [Required]
        [Display(Name = "End date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        public DateTime EndDate { get; set; }

        [Required]
        [Display(Name = "Order number")]
        public int OrderNumber { get; set; }
        [Required]
        [Display(Name = "Is dirty")]
        public bool IsDirty { get; set; }
        [Required, StringLength(15)]
        [Display(Name = "Data Server IP")]
        [RegularExpression(@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)
        {3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
        ErrorMessage = "Data Server IP should be in the form of 255.255.255")]
        public string IP { get; set; }

        [Required]
        [Display(Name = "Record data type")]
        [RegularExpression(@"^([1-2])$", 
        ErrorMessage = "Record Data Type should be 1 or 2")]
        public int Type { get; set; }

        [Display(Name = "Record identifier")]
        [RegularExpression(@"^([0-9])$", 
        ErrorMessage = "Record identifier should be between 0 to 9")]
        public int RecordIdentifier { get; set; }
    }

描述模型类

以下是我们应该在 model 类中注意的一些点:

  1. Initial DateEnd Date 应为 Date 类型,格式为 MM/dd/yyyy
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
        public DateTime InitialDate { get; set; }
  2. 在这里,我们使用了数据注解。
  3. 我们设置了 ApplyFormatInEditMode = true,这样在编辑模式下,用户就不会忘记提供所需的格式。
  4. 我们的日期字段在浏览器上应该包含 calendar 控件,该控件遵循 HTML5 规则。

    Rendring Date type fields

  5. IP 应以实际 IP 格式显示(以避免输入不必要的 string)。
    [Display(Name = "Data Server IP")]
    [RegularExpression(@"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.)
    {3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$",
    ErrorMessage = "Data Server IP should be in the form of 255.255.255")]
    public string IP { get; set; }
  6. 这里,我们使用了 RegularExpression,如果格式不匹配,应用程序将抛出错误。

    Rendring Date type fields

  7. Record data type 应为 1 或 2。
    [Required]
    [Display(Name = "Record data type")]
    [RegularExpression(@"^([1-2])$", ErrorMessage = "Record Data Type should be 1 or 2")]
    public int Type { get; set; }
  8. 如果表达式不匹配,它将抛出错误消息。

    Rendring Date type fields

  9. Record identifier 应介于 0-9 之间。
  10. 如果表达式不匹配,它将抛出错误消息。

    Rendring Date type fields

为什么所有检查都在客户端进行?

在本文中,我们在客户端项目(ASP.NET MVC4)中实现了 ASP.NET WEB API,在这里我们希望确保每个请求都经过验证且无错误。关于这种方法(是在客户端还是在服务侧实现)存在很多争论。在我看来,服务是中心化的过程,它将从多个客户端调用,因此,每个客户端都有责任确保传入的请求经过验证且无错误(这里传入的请求不包含任何安全逻辑,这里我指的是正常的验证数据检查)。

创建控制器

  1. 解决方案资源管理器 中,右键单击 Controllers 文件夹,然后单击 控制器

    Add new controller

  2. 在“添加控制器”对话框中,输入控制器名称 ServerDataController,并从 模板 下拉列表中选择“带有空的读/写操作的 MVC 控制器”,然后单击“添加”。

    Add new controller

描述控制器

我们的 ServerDataController 将使用 RestSharp 来消费 ASP.NET WEB API(我们将使用上面创建的帮助程序类)。

static readonly IServerDataRestClient RestClient = new ServerDataRestClient();

上面,我们简单地初始化了我们的 ServerDataRestClient

private IServerDataRestClient _restClient;

public ServerDataController(IServerDataRestClient  restClient)
{
   _restClient = restClient;
}

上面是另一种方式,其中我们需要使用控制反转(IOC)。

public ActionResult Index()
{
   return View(RestClient.GetAll());
}

在上面的代码中,我们的 Index ActionResult 方法将从服务获取所有 ServerData 记录并渲染我们的 view。此时我们需要添加视图。

创建视图

此时,我们需要一个 UI,我们可以在其中显示输出或为用户提供与应用程序交互的界面。因此,我们需要添加一个 view

添加视图的直接/粗略方法是,在解决方案资源管理器中,添加一个新文件夹 ServerData,右键单击它,然后单击 添加视图

Add new controller

添加视图 对话框中,将视图命名为 Index,选择 Razor 作为 ViewEngine,创建强类型视图,并选择 ServerDataModel 作为 Model 类。我使用了 List 的 Scaffold Template(如果您不想使用,可以忽略),使用您的主布局,然后单击“添加”。

Add new view

这将在项目中添加一个新的视图。

@model IEnumerable<consumewebapi.models.serverdatamodel>

@{
    ViewBag.Title = "Manipulate Server Data";
    Layout = "~/Views/Shared/_Layout.cshtml";
}</consumewebapi.models.serverdatamodel>

我们做了一些小的修改来获得输出。

@string.Format(item.IsDirty ? "Yes" : "No")

这将为我们显示 YesNo,表明我们的记录是否脏。

按照相同的步骤添加其他视图,这是结构:

Views of project

执行项目

最后,我们完成了所有自定义和更改,现在是时候运行我们的项目了。只需单击运行或按 F5。我们的 Index 页面将呈现为:

Get all records

关注点

  1. 啊!ASP.NET MVC4 没有提供直接实现 RestSharp 的方法。有一个很好的方法可以向我们的项目添加 RestSharp 支持,然后我们就可以使用这个强大的框架了。
  2. 我在这里创建了一个演示应用程序:here。您可以使用它,并将其代码作为部分或全部采用。如果您发现任何问题或需要处理的其他事项,我很乐意收到您的反馈。

历史

  • 2014 年 10 月 6 日:初始版本
  • 2015 年 8 月 13 日:源代码已更新
© . All rights reserved.