十、构建 Web 客户机——使用 Web 服务

到目前为止,在本书中,我们已经创建了 RESTful 服务,以便我们可以在项目内部或外部调用或使用这些服务。 在本章中,我们将讨论这些服务的一些用例,以及使用 RESTful web 服务的技术和方法。

在本章中,我们将涵盖以下主题:

  • 使用 RESTful web 服务
  • 构建一个 REST web 客户机

使用 RESTful web 服务

到目前为止,我们已经创建了 RESTful 服务,并在代码示例的帮助下讨论了服务器端代码。 我们使用了外部第三方工具,如 Postman 和 Advanced RESTClient,来使用这些服务。 我们还使用了假对象并在单元测试期间使用了这些服务。 虽然这些使用示例很有帮助,但它们并没有显示 RESTful 服务的真正实力,因为它们要么测试了其功能,要么验证了其输出。

在某些情况下,您可能需要在类似于控制器的另一个应用中使用或使用这些服务,甚至需要在您自己的应用中使用这些服务。 这些应用可以是以下任何一种:

  • 基于控制台的
  • 基于 Web 的
  • 移动或任何其他设备

让我们来看看我们已经讨论过的应用之一:假设在集成在线支付系统时,您需要某种机制来实现或使用外部 API(在本例中是 PayPal)。 在这种情况下,我们已经讨论过的外部工具,比如 Postman 和 Advanced RESTClient,就无能为力了; 为了满足您的需求,您需要一个 REST 客户机。

下图说明了如何在 HTTP 客户机的帮助下使用 REST 客户机使用服务。 在下面的图中,REST 客户机正在与用 ASP 开发的外部服务和网络服务交互(请求、响应).NET Core 或位于相同或不同的服务器上。

WebConsoleMobile等是在 REST 客户端帮助下使用这些服务的客户端。

现在,我们将讨论如何构建一个 REST 客户机,以便使用应用中的其他 RESTful web 服务(即 api)。

构建一个 REST web 客户机

基于 rest 的服务可能是 web 应用的一部分,也可能不是。 web 应用可以调用或使用来自同一应用的外部 api 或服务。 在服务和使用这些服务的应用之间实现交互或通信(请求、响应)的程序称为客户机

客户机帮助应用与 api 进行通信(请求、响应)。

在本节中,我们将创建一个 web 客户机。 web 客户端是用 ASP 编写的应用或程序。 净的核心。

在构建测试 web 客户端之前,我们需要讨论必须调用什么。

继续我们的 FlixOne 书店示例,下表列出了我们将调用和消费的产品和服务:

| API 资源 | 描述 | | GET /api/product | 获取产品列表。 | | GET /api/product{id} | 得到一个产品。 | | PUT /api/product{id} | 更新现有产品。 | | DELETE /api/product{id} | 删除现有产品。 | | POST /api/product | 添加新产品。 |

我们的 FlixOne 产品服务旨在完成以下任务:

  • 添加新产品
  • 更新现有产品
  • 删除现有产品
  • 检索一个产品

我们已经确保 Swagger 支持我们的产品 api(更多信息请参阅前面的章节),所以让我们开始吧。 要开始这个项目,请遵循以下步骤:

  1. 首先,运行 Visual Studio 2017
  2. 选择文件|打开
  3. 选择项目 flixon.com . bookstore . productservice
  4. 通过按F5或直接从菜单中单击来运行项目
  5. 输入以下 URL:http://localhost:10065/swagger/

现在,您应该看到产品 api 的 Swagger 文档,如下面的截图所示:

Product APIs documentation

烹饪 web 客户端

我们已经讨论了需要使用哪些 api 以及哪些资源返回什么,所以现在是时候烹饪我们的 web 客户端了,这样我们就可以使用和调用我们的产品 api。 要做到这一点,请遵循以下步骤:

  1. 要为我们的新项目创建一个全新的解决方案,转到文件|新|项目(或按Ctrl+Shift+N),如下截图所示。

  1. 从新项目中,选择 ASP.NET Core Web 应用。
  2. 将项目命名为FlixOne.BookStore.WebClient,然后单击 OK,如下图所示:

  1. 从 ASP。 选择“Web Application”,单击“OK”,如下图所示:

  1. 现在运行项目调试|开始调试或按F5键。
  2. 你现在应该看到一个默认的网站模板。
  3. 现在我们将使用RestSharp创建一个 web 客户端。 我们需要添加对 RestSharp 的支持,以获得通过 HTTP 协议调用 API 资源的功能。

RestSharp 是一个轻量级的 HTTP 客户端库。 您可以根据需要进行更改,因为它是一个开源库。 您可以在https://github.com/restsharp/RestSharp找到完整的存储库。

  1. 使用 Open package Manager(右键单击 Solution Explorer 中的 Solution)添加一个 NuGet 包,如下图所示:

  1. 搜索 RestSharp 并勾选包含预发布的复选框,然后单击安装,如下截图所示:

Selecting the RestSharp NuGet package

  1. 所需的软件包现在将安装,如下截图所示:

Installing the RestSharp package

在继续之前,让我们首先确保我们的产品 api 正常工作。 运行产品 API 项目,打开 Swagger,点击GET /api/product/productlist资源如下:

执行了前面的资源后,你应该看到一个完整的产品列表,如下面的截图所示:

Try all available resources to make sure that your product APIs are working correctly.

编写代码

到目前为止,我们已经准备好了为 REST web 客户端编写代码所需的东西; 在本节中,我们将编写实际的代码:

  1. 添加一个简单的代码来调用或使用您的产品 api。

如果你创建了一个新的项目在同一个解决方案(参见第一步一节烹饪的 web 客户端),请确保该项目产品 API 是运行在开始之前您的 web 客户端项目。

  1. Client文件夹中添加一个新类(Ctrl+Shift+C),命名为RestSharpWebClient

  1. 现在打开RestSharpWebClient类并添加以下代码:
private readonly RestClient _client = new RestClient("http://localhost:10065/api/");

前面的代码初始化 RestSharp 的 RestClient,并将基本 URL 作为字符串或 URI 接受。

URI 代表统一资源标识符,是用于标识资源的字符串的表示形式。

您可能遇到有多个环境的场景; 在这种情况下,您应该根据您的环境将 URI 存储在指向它的位置。 例如,您可以为您的开发环境使用 URIhttp://devserver:10065/api/,也可以为您的 QA 环境使用 URIhttp://testenv:10068/api/。 您应该将这些键存储在config文件或类似的地方,以便可以方便地访问这些值。 我们推荐使用new RestClient(somevariableforURI);

In our application, product APIs are running on localhost and the listening port 10065. This may be different in your case.

让我们讨论下面的代码片段来调用或使用GET /api/product /productlist资源并填充完整的产品列表,如下所示:

public List<ProductViewModel> GetProducts()
{
  var request = new RestRequest("product/productlist", Method.GET);
  var response = _client.Execute<List<ProductViewModel>>(request);
  return response.Data ?? new List<ProductViewModel> {new
  ProductViewModel()};
}

在这里,我们使用RestRequest创建了一个GET请求,其中传递了一个资源和方法。

要使用productid获得特定的产品,请输入以下代码:

public ProductViewModel GetProductDetails(string productId)
{
  var request = new RestRequest("product/{productid}", Method.GET);
  request.AddParameter("productid", productId);
  var response = _client.Execute<ProductViewModel>(request);
  return response.Data ?? new ProductViewModel();
}

在前面的代码块中,GetProductDetails方法做了与GetProducts方法相似的事情。 不同之处在于它接受参数productId

下面是我们 REST 客户端的完整代码:

public class RestSharpWebClient
{
  private readonly RestClient _client = new
  RestClient("http://localhost:10065/api/");
  public List<ProductViewModel> GetProducts()
  {
    var request = new RestRequest("product/productlist", 
    Method.GET);
    var response = _client.Execute<List<ProductViewModel>>
    (request);
    //To avoid any exception lets return an empty view model
    //On production environment return exact exception or your 
    custom code
    return response.Data ?? new List<ProductViewModel> {new
    ProductViewModel()};
  }
  public ProductViewModel GetProductDetails(string productId)
  {
    var request = new RestRequest("product/{productid}", 
    Method.GET);
    request.AddParameter("productid", productId);
    var response = _client.Execute<ProductViewModel>(request);
    //To avoid any exception lets return an empty view model
    //On production environment return exact exception or your 
    custom code
    return response.Data ?? new ProductViewModel();
  }
  public bool AddProduct(ProductViewModel product)
  {
    var request = new RestRequest("product/addproduct", 
    Method.POST);
    request.AddBody(product);
    var response = _client.Execute(request);
    return response.StatusCode == HttpStatusCode.OK;
  }
  public bool UpdateProduct(string id, ProductViewModel product)
  {
    var request = new RestRequest("updateproduct", Method.PUT);
    request.AddQueryParameter("productid", id);
    request.AddBody(product);
    var response = _client.Execute(request);
    return response.StatusCode == HttpStatusCode.NoContent;
  }
  public bool DeleteProduct(string id, ProductViewModel product)
  {
    var request = new RestRequest("deleteproduct", Method.DELETE);
    request.AddQueryParameter("productid", id);
    request.AddBody(product);
    var response = _client.Execute(request);
    return response.StatusCode == HttpStatusCode.NoContent;
  }
}

通过前面的代码片段,您现在已经添加了调用和使用产品 api 的功能。

实现 REST web 客户端

基于 rest 的服务可能是你的 web 应用的一部分,也可能不是,但我们仍然需要理解如何实现它们。

所以,现在是时候做些真正的工作了。 将ProductController添加到项目中,并执行以下操作:

public ActionResult Index()
{
  var webClient = new RestSharpWebClient();
  var products = webClient.GetProducts();
  return View("ProductList", products);
}

看一下前面的代码片段。 我们调用了RestSharpWebClientGetProducts方法,并在Index.cshtml视图中填充了完整的产品列表。

要添加另一个操作方法,请输入以下我们的ProductController的完整代码。 下面的代码片段包含了Index操作方法,并给了我们一个产品列表:

public class ProductController : Controller
{
  public ActionResult Index()
  {
    var webClient = new RestSharpWebClient();
    var products = webClient.GetProducts();
    return View("ProductList", products);
  }
  public ActionResult Details(string id)
  {
    var webClient = new RestSharpWebClient();
    var products = webClient.GetProductDetails(id);
    return View(products);
  }

现在让我们看看两种Create动作方法:HttpGetHttpPost。 第一个为我们提供一个输入屏幕,第二个使用HttpPost方法发布所有数据(输入值)。 在服务器端,您可以在IFormCollection参数中接收所有数据,还可以轻松地编写逻辑来获取ProductViewModel中的所有值。

  public IActionResult Create()
  {
    return View();
  }
  [HttpPost]
  [ValidateAntiForgeryToken]
  public IActionResult Create(IFormCollection collection)
  {
    try
    {
      var product = new ProductViewModel
      {
        ProductId = Guid.NewGuid(),
        ProductName = collection["ProductName"],
        ProductDescription = collection["ProductDescription"],
        ProductImage = collection["ProductImage"],
        ProductPrice = Convert.ToDecimal(collection["ProductPrice"]),
        CategoryId = new Guid("77DD5B53-8439-49D5-9CBC-DC5314D6F190"),
        CategoryName = collection["CategoryName"],
        CategoryDescription = collection["CategoryDescription"]
      };
      var webClient = new RestSharpWebClient();
      var producresponse = webClient.AddProduct(product);
      if (producresponse)
      return RedirectToAction(nameof(Index));
      throw new Exception();
    }
    catch
    {
      return View();
    }
  }

You can also write a HttpPost method that accepts a parameter of the type ProductViewModel.

下面的代码片段向我们展示了Edit动作方法的代码,它与Create动作方法相似,但它更新了现有数据,而不是插入新的数据:

  public ActionResult Edit(string id)
  {
    var webClient = new RestSharpWebClient();
    var product = webClient.GetProductDetails(id);
    return View(product);
  }
  [HttpPost]
  [ValidateAntiForgeryToken]
  public ActionResult Edit(string id, IFormCollection collection)
  {
    try
    {
      var product = new ProductViewModel
      {
        ProductId = new Guid(collection["ProductId"]),
        ProductName = collection["ProductName"],
        ProductDescription = collection["ProductDescription"],
        ProductImage = collection["ProductImage"],
        ProductPrice = Convert.ToDecimal(collection["ProductPrice"]),
        CategoryId = new Guid(collection["CategoryId"]),
        CategoryName = collection["CategoryName"],
        CategoryDescription = collection["CategoryDescription"]
      };
      var webClient = new RestSharpWebClient();
      var producresponse = webClient.UpdateProduct(id, product);
      if (producresponse)
      return RedirectToAction(nameof(Index));
      throw new Exception();
    }
    catch
    {
      return View();
    }
  }

动作方法用于从数据库或集合中删除特定的记录或数据。 HttpGet的操作方法Delete根据给定的 ID 获取一条记录,并显示准备修改的数据。 HttpPost的另一个Delete操作将修改后的数据发送到服务器进行进一步处理。 这意味着系统可以删除数据和记录。

  public ActionResult Delete(string id)
  {
    var webClient = new RestSharpWebClient();
    var product = webClient.GetProductDetails(id);
    return View(product);
  }
  [HttpPost]
  [ValidateAntiForgeryToken]
  public ActionResult Delete(string id, IFormCollection collection)
  {
    try
    {
      var product = new ProductViewModel
      {
        ProductId = new Guid(collection["ProductId"]),
        ProductName = collection["ProductName"],
        ProductDescription = collection["ProductDescription"],
        ProductImage = collection["ProductImage"],
        ProductPrice = Convert.ToDecimal(collection["ProductPrice"]),
        CategoryId = new Guid(collection["CategoryId"]),
        CategoryName = collection["CategoryName"],
        CategoryDescription = collection["CategoryDescription"]
      };
      var webClient = new RestSharpWebClient();
      var producresponse = webClient.DeleteProduct(id, product);
      if (producresponse)
      return RedirectToAction(nameof(Index));
      throw new Exception();
    }
    catch
    {
      return View();
    }
  }
}

现在让我们从Shared文件夹中打开_Layout.cshtml,添加以下一行,添加一个链接到我们新添加的ProductController:

<li><a asp-area="" asp-controller="Product" asp-action="Index">Web Client</a></li>

当你运行项目时,你应该看到一个名为 Web Client 的新菜单,如下面的截图所示:

我们现在准备看到一些结果。 单击 Web Client 菜单,您将看到以下屏幕:

在前面的屏幕中,您可以执行其他操作,以及调用和使用您的产品 api—即创建、编辑和删除。

总结

基于 rest 的服务的创建对于任何项目都是重要的,但是如果没有办法使用这些服务,那么它们就毫无用处。 在本章中,我们了解了如何将 RestSharp 支持添加到我们的 web 项目中,并使用我们预先开发的产品 api。 我们还创建了一个 web 客户端,它可以通过使用 ASP 在网页上呈现输出来使用 web 服务。 净的核心。

在下一章中,我们将讨论微服务的热门话题,下一个层次的服务分离。 我们将讨论微服务如何通信,它们的优势是什么,以及我们为什么需要它们。