使用 RESTful Web 服务

下载示例 下载示例

将 Web 服务集成到应用程序中是一种常见方案。 本文演示如何从 Xamarin.Forms 应用程序使用 RESTful Web 服务。

表述性状态转移 (REST) 是一种用于生成 Web 服务的体系结构样式。 Web 浏览器使用 HTTP 谓词来检索网页以及向服务器发送数据,REST 请求使用与此相同的 HTTP 谓词通过 HTTP 发出。 谓词如下:

  • GET - 此操作用于从 Web 服务检索数据。
  • POST - 此操作用于在 Web 服务上创建新的数据项。
  • PUT - 此操作用于更新 Web 服务上的数据项。
  • PATCH - 此操作用于更新 Web 服务上的数据项,方法是描述有关如何修改此项的一组说明。 示例应用程序中未使用此谓词。
  • DELETE - 此操作用于删除 Web 服务上的数据项。

遵循 REST 的 Web 服务 API 称为 RESTful API,并使用以下资源来定义:

  • 一个基 URI。
  • HTTP 方法,例如 GET、POST、PUT、PATCH 或 DELETE。
  • 数据的媒体类型,例如 JavaScript 对象表示法 (JSON) 。

RESTful Web 服务通常使用 JSON 消息将数据返回到客户端。 JSON 是一种基于文本的数据交换格式,可生成紧凑的有效负载,从而在发送数据时降低带宽要求。 示例应用程序使用 开放源代码 NewtonSoft JSON.NET 库来序列化和反序列化消息。

REST 的简单性使其成为在移动应用程序中访问 Web 服务的主要方法。

运行示例应用程序时,它将连接到本地托管的 REST 服务,如以下屏幕截图所示:

示例应用程序

注意

在 iOS 9 及更高版本中,应用传输安全 (ATS) 强制 Internet 资源 ((例如应用的后端服务器) 和应用)之间的安全连接,从而防止意外泄露敏感信息。 由于 ATS 默认在为 iOS 9 生成的应用中启用,因此所有连接都将受 ATS 安全要求的约束。 如果连接不符合这些要求,则会失败并出现异常。

如果无法对 Internet 资源使用 HTTPS 协议和安全通信,则可以选择退出 ATS 。 这可以通过更新应用的 Info.plist 文件来实现。 有关详细信息,请参阅 应用传输安全性

使用 Web 服务

REST 服务使用 ASP.NET Core 编写,并提供以下操作:

操作 HTTP 方法 相对 URI parameters
获取待办事项的列表 GET /api/todoitems/
创建新的任务项 POST /api/todoitems/ JSON 格式的 TodoItem
更新待办事项 PUT /api/todoitems/ JSON 格式的 TodoItem
删除待办事项 DELETE /api/todoitems/{id}

大多数 URI 都包含路径中的 TodoItem ID。 例如,若要删除 TodoItem 其 ID 为 6bb8a868-dba1-4f1a-93b7-24ebce87e243的 ,客户端会向 http://hostname/api/todoitems/6bb8a868-dba1-4f1a-93b7-24ebce87e243发送 DELETE 请求。 有关示例应用程序中使用的数据模型的详细信息,请参阅 对数据建模

当 Web API 框架收到请求时,它会将请求路由到操作。 这些操作只是 类中的 TodoItemsController 公共方法。 框架使用路由中间件来匹配传入请求的 URL,并将其映射到操作。 REST API 应使用属性路由模型,即应用功能作为一组资源,其操作由 HTTP 谓词表示。 属性路由使用一组属性将操作直接映射到路由模板。 有关属性路由的详细信息,请参阅 REST API 的属性路由。 有关使用 ASP.NET Core 生成 REST 服务的详细信息,请参阅为本机移动应用程序创建后端服务

HttpClient 用于通过 HTTP 发送和接收请求。 它提供从 URI 标识的资源发送 HTTP 请求和接收 HTTP 响应的功能。 每个请求都作为异步操作发送。 有关异步操作的详细信息,请参阅 异步支持概述

HttpResponseMessage 表示在发出 HTTP 请求后从 Web 服务收到的 HTTP 响应消息。 它包含有关响应的信息,包括状态代码、标头和任何正文。 类 HttpContent 表示 HTTP 正文和内容标头,例如 Content-TypeContent-Encoding。 可以使用任何 ReadAs 方法(如 ReadAsStringAsyncReadAsByteArrayAsync)读取内容,具体取决于数据的格式。

创建 HTTPClient 对象

实例 HttpClient 在类级别声明,以便只要应用程序需要发出 HTTP 请求,对象就可生存,如以下代码示例所示:

public class RestService : IRestService
{
  HttpClient client;
  ...

  public RestService ()
  {
    client = new HttpClient ();
    ...
  }
  ...
}

检索数据

方法 HttpClient.GetAsync 用于将 GET 请求发送到 URI 指定的 Web 服务,然后从 Web 服务接收响应,如以下代码示例所示:

public async Task<List<TodoItem>> RefreshDataAsync ()
{
  ...
  Uri uri = new Uri (string.Format (Constants.TodoItemsUrl, string.Empty));
  ...
  HttpResponseMessage response = await client.GetAsync (uri);
  if (response.IsSuccessStatusCode)
  {
      string content = await response.Content.ReadAsStringAsync ();
      Items = JsonSerializer.Deserialize<List<TodoItem>>(content, serializerOptions);
  }
  ...
}

REST 服务在 属性中 HttpResponseMessage.IsSuccessStatusCode 发送 HTTP 状态代码,以指示 HTTP 请求是成功还是失败。 对于此操作,REST 服务在响应中发送 HTTP 状态代码 200 (正常) ,指示请求成功且请求的信息在响应中。

如果 HTTP 操作成功,则会读取响应的内容以供显示。 属性 HttpResponseMessage.Content 表示 HTTP 响应的内容,方法 HttpContent.ReadAsStringAsync 以异步方式将 HTTP 内容写入字符串。 然后,此内容将从 JSON 反序列化为 List 实例的 TodoItem

警告

ReadAsStringAsync使用 方法检索大型响应可能会对性能产生负面影响。 在这种情况下,应直接反序列化响应,以避免对其进行完全缓冲。

创建数据

方法 HttpClient.PostAsync 用于将 POST 请求发送到 URI 指定的 Web 服务,然后从 Web 服务接收响应,如以下代码示例所示:

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
{
  Uri uri = new Uri (string.Format (Constants.TodoItemsUrl, string.Empty));

  ...
  string json = JsonSerializer.Serialize<TodoItem>(item, serializerOptions);
  StringContent content = new StringContent (json, Encoding.UTF8, "application/json");

  HttpResponseMessage response = null;
  if (isNewItem)
  {
    response = await client.PostAsync (uri, content);
  }
  ...

  if (response.IsSuccessStatusCode)
  {
    Debug.WriteLine (@"\tTodoItem successfully saved.");
  }
  ...
}

实例 TodoItem 序列化为 JSON 有效负载,以便发送到 Web 服务。 然后,此有效负载嵌入到 HTTP 内容的正文中,在使用 方法发出 PostAsync 请求之前,该内容将发送到 Web 服务。

REST 服务在 属性中 HttpResponseMessage.IsSuccessStatusCode 发送 HTTP 状态代码,以指示 HTTP 请求是成功还是失败。 此操作的常见响应包括:

  • 201 (CREATED) - 请求导致在发送响应之前创建新资源。
  • 400 (BAD REQUEST) – 服务器无法理解请求。
  • 409 (CONFLICT) - 由于服务器上的冲突,无法执行请求。

更新数据

方法 HttpClient.PutAsync 用于将 PUT 请求发送到 URI 指定的 Web 服务,然后从 Web 服务接收响应,如以下代码示例所示:

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
{
  ...
  response = await client.PutAsync (uri, content);
  ...
}

方法的操作 PutAsync 与用于在 Web 服务中创建数据的方法相同 PostAsync 。 但是,从 Web 服务发送的可能响应有所不同。

REST 服务在 属性中 HttpResponseMessage.IsSuccessStatusCode 发送 HTTP 状态代码,以指示 HTTP 请求是成功还是失败。 此操作的常见响应包括:

  • 204 (无内容) – 已成功处理请求,并且响应有意为空。
  • 400 (BAD REQUEST) – 服务器无法理解请求。
  • 404 (找不到) – 请求的资源不存在于服务器上。

删除数据

方法 HttpClient.DeleteAsync 用于将 DELETE 请求发送到 URI 指定的 Web 服务,然后从 Web 服务接收响应,如以下代码示例所示:

public async Task DeleteTodoItemAsync (string id)
{
  Uri uri = new Uri (string.Format (Constants.TodoItemsUrl, id));
  ...
  HttpResponseMessage response = await client.DeleteAsync (uri);
  if (response.IsSuccessStatusCode)
  {
    Debug.WriteLine (@"\tTodoItem successfully deleted.");
  }
  ...
}

REST 服务在 属性中 HttpResponseMessage.IsSuccessStatusCode 发送 HTTP 状态代码,以指示 HTTP 请求是成功还是失败。 此操作的常见响应包括:

  • 204 (无内容) - 请求已成功处理,响应有意为空。
  • 400 (BAD REQUEST) – 服务器无法理解该请求。
  • 404 (找不到) – 请求的资源不存在于服务器上。

本地开发

如果使用 ASP.NET Core Web API 等框架在本地开发 REST Web 服务,则可以同时调试 Web 服务和移动应用。 在此方案中,必须为 iOS 模拟器和 Android 模拟器启用明文 HTTP 流量。 有关配置项目以允许通信的信息,请参阅 连接到本地 Web 服务