为原生移动应用程序创建后端服务
作者 Steve Smith
翻译 谢炀(Kiler)
移动应用程序可以轻松的与 ASP.NET Core 后端服务通信。
原生移动应用程序示例
本教程演示如何使用 ASP.NET Core MVC 创建后端服务来支持原生移动应用程序。 它使用 Xamarin Forms ToDoRest 应用程序作为其原生客户端APP,其中包括用于Android,iOS, Windows通用和Window Phone设备的各种原生客户端APP。 您可以按照链接的教程创建原生客户端APP(需要安装免费 Xamarin 工具),以及下载 Xamarin 示例解决方案。 Xamarin 示例包括一个 ASP.NET Web API 2 服务项目,会被本文的ASP.NET Core 应用程序替换(客户端不需要进行修改)。
功能
ToDoRest APP 应用程序支持列表、添加、删除、以及 To-Do 数据项。每条数据包含 ID, Name, Notes, 以及一个属性定于该条数据是否完成的字段。
如上所示,项目的主视图列出了每个项目的名称,并指示是否使用复选标记。
点击 +
图标打开一个添加项目对话框:
在主列表界面上点击项目将打开一个编辑对话框,可以修改项目的“名称”,“注释”和“完成”设置,或者可以删除该项目:
本示例默认配置为使用托管在 developer.xamarin.com 上的后端服务,该服务允许只读操作。为了测试一下接下来你自己在计算机上中创建的 ASP.NET Core 应用程序,您需要更新应用程序的 RestUrl
常量。 导航到 ToDoREST
项目并打开 Constants.cs 文件。 将 RestUrl
w替换为包含机器IP地址的URL(不是本地主机或127.0.0.1,因为该地址是从设备模拟器使用的,而不是来自您的机器)。 还包括端口号(5000)。 为了测试您的服务与设备的一致性,请确保您没有主动防火墙阻止对该端口的访问。
// URL of REST service (Xamarin ReadOnly Service)
//public static string RestUrl = "http://developer.xamarin.com:8081/api/todoitems{0}";
// use your machine's IP address
public static string RestUrl = "http://192.168.1.207:5000/api/todoitems/{0}";
创建的 ASP.NET Core 应用程序
在 Visual Studio 中创建一个新的 ASP.NET Core Web应用程序。 选择 Web API 模板,然后选择无验证。 命名项目 ToDoApi。
应用程序会响应所有对端口5000的请求。通过更新 Program.cs 包含 .UseUrls("http://*:5000")
来实现:
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls("http://*:5000")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
[!NOTE]
> Make sure you run the application directly, rather than behind IIS Express, which ignores non-local requests by default. Run `dotnet run` from a command prompt, or choose the application name profile from the Debug Target dropdown in the Visual Studio toolbar.-->
备注
确保您是直接运行应用程序的,而不是在 IIS Express 之后,默认情况下会忽略非本地请求。 在命令提示符运行 dotnet run
,或从 Visual Studio 工具栏的 Debug Target 下拉列表中选择应用程序名称配置文件。
添加一个模型类来表示待办事项。 使用 [Required]
属性来标记必填的字段:
using System.ComponentModel.DataAnnotations;
namespace ToDoApi.Models
{
public class ToDoItem
{
[Required]
public string ID { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Notes { get; set; }
public bool Done { get; set; }
}
}
API 方法需要一些处理数据的方法。 使用原始 Xamarin 示例使用的相同的 IToDoRepository
接口:
using System.Collections.Generic;
using ToDoApi.Models;
namespace ToDoApi.Interfaces
{
public interface IToDoRepository
{
bool DoesItemExist(string id);
IEnumerable<ToDoItem> All { get; }
ToDoItem Find(string id);
void Insert(ToDoItem item);
void Update(ToDoItem item);
void Delete(string id);
}
}
对于本示例,实现只使用了私有的项目集合:
using System.Collections.Generic;
using System.Linq;
using ToDoApi.Interfaces;
using ToDoApi.Models;
namespace ToDoApi.Services
{
public class ToDoRepository : IToDoRepository
{
private List<ToDoItem> _toDoList;
public ToDoRepository()
{
InitializeData();
}
public IEnumerable<ToDoItem> All
{
get { return _toDoList; }
}
public bool DoesItemExist(string id)
{
return _toDoList.Any(item => item.ID == id);
}
public ToDoItem Find(string id)
{
return _toDoList.FirstOrDefault(item => item.ID == id);
}
public void Insert(ToDoItem item)
{
_toDoList.Add(item);
}
public void Update(ToDoItem item)
{
var todoItem = this.Find(item.ID);
var index = _toDoList.IndexOf(todoItem);
_toDoList.RemoveAt(index);
_toDoList.Insert(index, item);
}
public void Delete(string id)
{
_toDoList.Remove(this.Find(id));
}
private void InitializeData()
{
_toDoList = new List<ToDoItem>();
var todoItem1 = new ToDoItem
{
ID = "6bb8a868-dba1-4f1a-93b7-24ebce87e243",
Name = "Learn app development",
Notes = "Attend Xamarin University",
Done = true
};
var todoItem2 = new ToDoItem
{
ID = "b94afb54-a1cb-4313-8af3-b7511551b33b",
Name = "Develop apps",
Notes = "Use Xamarin Studio/Visual Studio",
Done = false
};
var todoItem3 = new ToDoItem
{
ID = "ecfa6f80-3671-4911-aabe-63cc442c1ecf",
Name = "Publish apps",
Notes = "All app stores",
Done = false,
};
_toDoList.Add(todoItem1);
_toDoList.Add(todoItem2);
_toDoList.Add(todoItem3);
}
}
}
在 Startup.cs 中的配置实现:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton<IToDoRepository,ToDoRepository>();
}
此时,您已准备好创建 ToDoItemsController。
[!TIP] > Learn more about creating web APIs in [Building Your First Web API with ASP.NET Core MVC and Visual Studio](../tutorials/first-web-api.md).-->提示
了解更多如何创建 web APIs的信息请参考 使用 ASP.NET Core MVC 和 Visual Studio 创建你的第一个 Web API 。
创建控制器
向项目添加一个新的控制器 ToDoItemsController。 它应该继承自 Microsoft.AspNetCore.Mvc.Controller 。 添加一个 Route
属性来指示控制器将处理以 api/todoitems
开头的路径的请求。 路由中的 [controller]
令牌被控制器的名称所取代(省略了 Controller
后缀),对全局路由特别有用。 参考 routing 了解更多信息。
控制器需要一个 IToDoRepository
才能正常运行; 通过控制器的构造函数请求此类型的实例。 在运行时,将使用框架内置的依赖注入 支持来提供此实例。
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ToDoApi.Interfaces;
using ToDoApi.Models;
namespace ToDoApi.Controllers
{
[Route("api/[controller]")]
public class ToDoItemsController : Controller
{
private readonly IToDoRepository _toDoRepository;
public ToDoItemsController(IToDoRepository toDoRepository)
{
_toDoRepository = toDoRepository;
}
该 API 支持四种不同的 HTTP 谓词来对数据源执行 CRUD(创建,读取,更新和删除)操作。 其中最简单的是读操作,它对应于 HTTP GET 请求。
读取数据
通过对 List
方法的 GET请求来获取项目列表。 List
方法中的 [HttpGet]
属性表示此操作只能处理 GET 请求。 此操作的路由是在控制器上指定。 您不一定需要使用 action 名称作为路径的一部分。 你只需要确保每个action 都有一个独特和明确的路径。 路由属性可以在控制器和方法级别应用,来建立特定的路由。
[HttpGet]
public IActionResult List()
{
return Ok(_toDoRepository.All);
}
List
方法返回 200 OK 响应代码和所有 ToDo 项并序列化为JSON。
您可以使用各种工具测试您的新的 API 方法,例如Postman,如下所示:
创建项目
按照惯例,创建新的数据项被映射到 HTTP POST 谓词 Create
方法应用了一个n [HttpPost]
属性,并接受一个ID参数和一个 ToDoItem
对象实例。 HTTP谓词属性,如 [HttpPost]
,可选地接受一个路由模板字符串(在这个例子中为{id}
)。 这与将 [Route]
属性添加到操作中具有同样的效果。 由于 item
参数将在POST的主体中传递,所以这个参数用 [FromBody]
属性装饰。
在方法内,检查项目的有效性和数据先前是否存在,如果没有发生问题,则使用存储库添加该项。 检查 ModelState.IsValid
执行模型验证,并且应该在接受用户输入的每个 API 方法中完成。
[HttpPost("{id}")]
public IActionResult Create(string id, [FromBody]ToDoItem item)
{
try
{
if (item == null || !ModelState.IsValid)
{
return BadRequest(ErrorCode.TodoItemNameAndNotesRequired.ToString());
}
bool itemExists = _toDoRepository.DoesItemExist(item.ID);
if (itemExists)
{
return StatusCode(StatusCodes.Status409Conflict, ErrorCode.TodoItemIDInUse.ToString());
}
_toDoRepository.Insert(item);
}
catch (Exception)
{
return BadRequest(ErrorCode.CouldNotCreateItem.ToString());
}
return Ok(item);
}
该示例使用枚举包含错误代码传递给移动客户端:
public enum ErrorCode
{
TodoItemNameAndNotesRequired,
TodoItemIDInUse,
RecordNotFound,
CouldNotCreateItem,
CouldNotUpdateItem,
CouldNotDeleteItem
}
测试使用 Postman 添加新项目,通过选择在请求正文中以 JSON 格式提供新对象的 POST 谓词。 您还应该添加一个请求头,指定 Content-Type
为 application/json
。
该方法在响应中返回新创建的项目。
更新项目
使用 HTTP PUT 请求修改记录。 除了这种变化, Edit
方法与 Create
几乎相同。 请注意,如果没有找到记录,则 Edit
操作将返回NotFound
(404) 响应。
[HttpPut("{id}")]
public IActionResult Edit(string id, [FromBody] ToDoItem item)
{
try
{
if (item == null || !ModelState.IsValid)
{
return BadRequest(ErrorCode.TodoItemNameAndNotesRequired.ToString());
}
var existingItem = _toDoRepository.Find(id);
if (existingItem == null)
{
return NotFound(ErrorCode.RecordNotFound.ToString());
}
_toDoRepository.Update(item);
}
catch (Exception)
{
return BadRequest(ErrorCode.CouldNotUpdateItem.ToString());
}
return NoContent();
}
要用 Postman 测试,将verb更改为 PUT ,并将要更新的记录的 ID 添加到 URL。 在请求的正文中指定更新的对象数据。
该方法在成功时返回 NoContent
(204) 响应,以便与预先存在的 API 保持一致。
删除项目
删除记录是通过对服务执行DELETE请求并传递要删除的项目的ID来实现的。 与更新一样,对不存在的项目的请求将收到 NotFound
响应。 否则,成功的请求将得到一个NoContent
(204) 响应。
[HttpDelete("{id}")]
public IActionResult Delete(string id)
{
try
{
var item = _toDoRepository.Find(id);
if (item == null)
{
return NotFound(ErrorCode.RecordNotFound.ToString());
}
_toDoRepository.Delete(id);
}
catch (Exception)
{
return BadRequest(ErrorCode.CouldNotDeleteItem.ToString());
}
return NoContent();
}
请注意,在测试删除功能时,请求正文中不需要任何内容。
常见的 Web API 约定
当您为你的应用程序开发后端服务时,您将需要制定一套统一的约定或策略来处理横切关注点。 例如,在上面显示的服务中,未找到的特定记录的请求收到 NotFound
响应,而不是 BadRequest
响应。 类似地,通过模型绑定类型传递给该服务的命令总是检查 ModelState.IsValid
,并为无效的模型类型返回一个 BadRequest
。
一旦确定好了 API 的通用策略,您可以将其封装在filter中。 了解更多详细信息请参考如何在 ASP.NET Core MVC 应用程序中封装通用 API 策略。