在 Mac 系统中使用 Visual Studio 以及 ASP.NET Core 开发 Web API 项目
作者 Rick Anderson 、 Mike Wasson
翻译 谢炀(Kiler)
HTTP协议 不仅仅提供网页服务. 它也是一个构建公开服务和数据 API 的强大平台。HTTP 协议是简单、灵活、无处不在的。 几乎你能想到的任何平台上都有HTTP支持, 所以HTTP服务能够发送到多种客户端, 包括浏览器,移动设备和传统的桌面应用程序。
在本教程中, 你将创建一个简单的 Web API 来管理一个 "to-do" 列表。在本教程中你无需编写任何 UI 代码.
ASP.NET Core 已经内置了用 MVC 架构 构建 Web API 的支持。
本教程有以下3个版本:
- macOS: 使用 Visual Studio Mac 版本 开发Web API
- Windows: 使用 Visual Studio Windows 版本 开发Web API
- macOS, Linux, Windows: 使用 Visual Studio Code 开发Web API
总览
这是你需要创建的 API :
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/todo | 获取所有的to-do items | 无 | Array of to-do items |
GET /api/todo/{id} | 通过ID获取item | 无 | To-do item |
POST /api/todo | 添加一个新的item | To-do item | To-do item |
PUT /api/todo/{id} | 更新已经存在的item | To-do item | 无 |
DELETE /api/todo/{id} | 删除指定的item。 | 无 | 无 |
下面的图表展示了应用程序的基本设计.
- model 是一个代表你应用程序数据的类. 在本案例中, 只有一个模型 to-do 项. 模型表现为简单 C# 类型 (POCOs).
- controller 是一个处理 HTTP 请求并返回 HTTP 响应的对象. 这个示例程序将只会有一个 controller.
- 为了保证教程简单我们不使用持久化数据库. 作为替代, 我们会把 to-do 项存入内存.
- 参考 Mac、Linux 平台 ASP.NET Core MVC 介绍 使用的持久化数据库。
环境准备
按照以下组件和程序:
创建项目
在 Visual Studio 中, 选择 File > New Solution。
选择 .NET Core App > ASP.NET Core Web API > Next。
在 Project Name 中输入 TodoApi, 并点击 Create。
启动应用程序
在 Visual Studio 中, 选择 Run > Start With Debugging 运行应用成. Visual Studio 启动版浏览器并导航到 http://localhost:port
, port 是一个随机端口好。 你会收到 HTTP 404 (Not Found) 错误信息。修改链接地址 http://localhost:port/api/values
并访问。 ValuesController
数据显示如下:
["value1","value2"]
添加 Entity Framework Core 支持
安装 Entity Framework Core InMemory database provider。这个 database provider 允许 Entity Framework Core 使用内存数据库。
- 在 Project 菜单,选择 Add NuGet Packages。
- 另外,你也可以右击 Dependencies, 并选择 Add Packages。
- Enter
EntityFrameworkCore.InMemory
in the search box. - Select
Microsoft.EntityFrameworkCore.InMemory
, and then select Add Package.
添加模型类
模型表示应用程序中的数据的对象。在本示例中,唯一使用到的模型是一个 to-do 项
添加一个名为 "Models" 的目录. 在解决方案浏览器中, 右击项目. 选择 Add > New Folder. 把目录名命名为 Models 。
Note: 你可以把模型类放到项目的任何地方, 但是 Models 是约定的默认目录 。
添加一个 TodoItem
类. 右击 Models 目录并选择 Add > New File > General > Empty Class。命名类为 TodoItem
并点击 New。
将生成代码替换为:
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
创建数据库上下文
数据库上下文 是 Entity Framework 用来协调数据模型的主要类。您可以从 Microsoft.EntityFrameworkCore.DbContext
类派生来创建此类。
在 Models 目录中添加 TodoContext
类。
using Microsoft.EntityFrameworkCore;
namespace TodoApi.Models
{
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; }
}
}
注册数据库上下文
为了将数据库上下文注入到控制器,我们必须在 依赖注入 容器中注册它。 把数据库上下文注册服务容器使用内置 依赖注入 支持。 使用一下代码替换掉 Startup.cs 文件。
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using TodoApi.Models;
namespace TodoApi
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase());
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
}
}
}
代码改动部分:
- 移除了不需要的 using 语句。
- 指定内存数据库注入到服务器容器。
Add a controller
在 Solution Explorer(解决方案浏览器)中, 在 Controllers 目录里面, 添加 TodoController
类。
将生成的代码替换为以下内容(并添加关闭大括号):
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using TodoApi.Models;
using System.Linq;
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
public class TodoController : Controller
{
private readonly TodoContext _context;
public TodoController(TodoContext context)
{
_context = context;
if (_context.TodoItems.Count() == 0)
{
_context.TodoItems.Add(new TodoItem { Name = "Item1" });
_context.SaveChanges();
}
}
}
}
在前面的代码中:
- 定义一个空控制器类。 在接下来的教程中,我们将添加 API 方法的实现。
- 控制器使用 依赖注入 来把数据库上下文 (
TodoContext
) 注入到控制器。数据库上下文在每个控制器的 CRUD 方法中都被用到。 - 构造函数将一个数据项添加到内存数据库(如果不存在)。
获取 to-do 列表
为了获取 to-do 项,添加下列方法到 TodoController
类 。
[HttpGet]
public IEnumerable<TodoItem> GetAll()
{
return _context.TodoItems.ToList();
}
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(long id)
{
var item = _context.TodoItems.FirstOrDefault(t => t.Id == id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
代码实现了两个 GET 方法:
GET /api/todo
GET /api/todo/{id}
以下是 GetAll
方法 HTTP 响应:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/10.0
Date: Thu, 18 Jun 2015 20:51:10 GMT
Content-Length: 82
[{"Key":"1", "Name":"Item1","IsComplete":false}]
在后面的教程中,我将会告诉你如何使用 Postman 或者 curl 工具查看 HTTP 响应。
路由和 URL 路径
[HttpGet]
标签指定这些方法均为 HTTP GET 方法。 每个方法构建的 Url 如下:
- 替换 controller 模版里面的路由标签:
namespace TodoApi.Controllers { [Route("api/[controller]")] public class TodoController : Controller { private readonly TodoContext _context;
- 把 "[Controller]" 替换为控制器名, 必须是带 "Controller" 后缀的小写名称. 在本示例里面控制器的名字为 "todo" (不区分大小写). 对于这个例子, controller 的类名是 TodoController 并且根名是 "todo". ASP.NET MVC Core 路由 是需要区分大小写的 。
- 如果
[HttpGet]
标签有模版字符串, 附加到路径 。 本示例没有模版字符串 。参考 使用 Http [Verb] 属性进行属性路由 获取更多信息。
在 GetById
方法中:
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(long id)
在实际的 HTTP 请求中"{id}"
是一个占位符, 客户端在运行时会使用 todo
项的 ID 属性, 当 GetById
被调用时候 , 会把 "{id}" 占位符分配到 Url 方法的 id
参数上去 。
Name = "GetTodo"
creates a named route and allows you to link to this route in an HTTP Response. I'll explain it with an example later. See Routing to Controller Actions for detailed information.
返回值
GetAll
方法返回一个 IEnumerable
对象 。MVC 自动把对象序列化为 JSON 并把 JSON 对象写入响应消息正文. 响应状态码为 200, 假设没有未处理异常的情况下。(未处理异常一般会被转化为 5xx 错误。)
相反, GetById
将会返回一个 IActionResult
类型, 代表一个更加通用的结果对象. 因为 GetById
有两个不同的返回值:
- 如果没有数据项可以匹配 ID, 方法会返回 404 错误,并最终以返回
NotFound
。
- 否则, 方法会返回 200 以及 JSON 响应正文。并最终以返回
ObjectResult
。
启动程序
在 Visual Studio 中, 选择 Run > Start With Debugging 运行应用成. Visual Studio 启动版浏览器并导航到 http://localhost:port
, port 是一个随机端口好。 你会收到 HTTP 404 (Not Found) 错误信息。修改链接地址 http://localhost:port/api/values
并访问。 ValuesController
数据显示如下:
["value1","value2"]
导航到 http://localhost:port/api/todo
访问 Todo
控制器:
[{"key":1,"name":"Item1","isComplete":false}]
实现其他的CRUD操作
我们将要把 Create
, Update
, 以及 Delete
方法到 controller 。这些方法都是围绕着一个主题,所以我将只列出代码以及标注出主要的区别。生成项目以后添加或者修改代码。
Create
[HttpPost]
public IActionResult Create([FromBody] TodoItem item)
{
if (item == null)
{
return BadRequest();
}
_context.TodoItems.Add(item);
_context.SaveChanges();
return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
}
这是一个 HTTP POST 方法, 用 [HttpPost]
标签声明 。[FromBody]
标签告诉 MVC 从 HTTP 请求的正文中获取 to-do 项的值 。
当通过 CreatedAtRoute
方法向服务器发出 HTTP POST 方法以创建新资源时,将返回标准的 201 响应。
CreateAtRoute
还把 Location 头信息加入到了响应。 Location 头信息指定新创建的 todo 项的 URI。 查看 10.2.2 201 Created.
使用 Postman 发送 Create 请求
- 启动程序 (Run > Start With Debugging)。
- 启动 Postman。
- 设置 HTTP method 为
POST
- 选择 Body 单选按钮
- 选择 raw 单选按钮
- 设置类型为 JSON
- 在键值对编辑器中,输入以下 Todo 数据项
{
"name":"walk dog",
"isComplete":true
}
- 选择 Send
- 在下面的面板中选择 Headers 选项卡拷贝 Location 头:
你可以使用 Location响应头(Location header URI) 来访问你刚才创建的资源。 重新调用 GetById
方法创建的 "GetTodo"
命名路由:
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)
Update
[HttpPut("{id}")]
public IActionResult Update(long id, [FromBody] TodoItem item)
{
if (item == null || item.Id != id)
{
return BadRequest();
}
var todo = _context.TodoItems.FirstOrDefault(t => t.Id == id);
if (todo == null)
{
return NotFound();
}
todo.IsComplete = item.IsComplete;
todo.Name = item.Name;
_context.TodoItems.Update(todo);
_context.SaveChanges();
return new NoContentResult();
}
Update
类似于 Create
,但是使用 HTTP PUT 。响应是 204 (No Content) 。
根据 HTTP 规范, PUT 请求要求客户端发送整个实体更新,而不仅仅是增量。为了支持局部更新,请使用 HTTP PATCH 。
{
"key": 1,
"name": "walk dog",
"isComplete": true
}
Delete
[HttpDelete("{id}")]
public IActionResult Delete(long id)
{
var todo = _context.TodoItems.First(t => t.Id == id);
if (todo == null)
{
return NotFound();
}
_context.TodoItems.Remove(todo);
_context.SaveChanges();
return new NoContentResult();
}
方法返回 204 (No Content)。
后续教程
- 路由到控制器和Action
- 更多如何部署你的API的信息, 请参考 发布与部署.
- 查看或者下载示例代码
- Postman
- Fiddler