在 Linux, macOS, 以及 Windows平台 用 ASP.NET Core MVC 和 Visual Studio Code 创建你的第一个 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 项存入内存.
设置好你的开发环境
下载并安装:
- .NET Core
- Visual Studio Code
- Visual Studio Code C# 插件
创建项目
在命令行控制台, 运行一下命令:
mkdir TodoApi
cd TodoApi
dotnet new webapi
在 Visual Studio Code (VS Code) 中打开 TodoApi 目录并选择 Startup.cs 文件。
- 当提示 Warn 信息 "Required assets to build and debug are missing from 'TodoApi'. Add them?" 的时候选择 Yes。
- 当提示 Info 信息 "There are unresolved dependencies" 的时候选择 Restore。
按下 Debug (F5) 编译并运行程序。在浏览器中导航到 http://localhost:5000/api/values 。显示一下信息:
["value1","value2"]
参考 Visual Studio Code 帮助 了解更多关于 VS Code 的使用技巧。
添加 Entity Framework Core 支持
编辑 TodoApi.csproj 文件添加 Entity Framework Core InMemory database provider。这个 database provider 允许 Entity Framework Core 使用内存数据库。
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="1.1.1" />
</ItemGroup>
</Project>
运行 dotnet restore
下载并安装 EF Core InMemory DB provider。你可以在终端运行 dotnet restore
命令或者在 VS Code 里面输入 ⌘⇧P
(macOS) 或者 Ctrl+Shift+P
(Linux) 并输入.NET。 选择 .NET: Restore Packages。
添加模型类
模型表示应用程序中的数据的对象。在本示例中,唯一使用到的模型是一个 to-do 项
添加一个名为 Models 的目录. 你可以把模型类放到项目的任何地方, 但是 Models 是约定的默认目录 。
使用以下代码添加一个 TodoItem
类:
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 语句。
- 指定内存数据库注入到服务器容器。
添加控制器
在 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
。
运行程序
在 VS Code 中, 按下 F5 运行程序。导航到 http://localhost:5000/api/todo (我们刚刚创建的 Todo
控制)。
实现其他的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 请求:
- 设置 HTTP method 为
POST
- 选择 Body 单选按钮
- 选择 raw 单选按钮
- 设置数据类型为 JSON
- 在键值编辑器中,输入一下 Todo 数据项
{
"name":"walk dog",
"isComplete":true
}
- 点击 Send
- 选择下方窗格中的标题选项卡,并复制 Location 标题:
你可以使用 Location响应头(Location header URI) 来访问你刚才创建的资源。 重新调用 GetById
方法创建的 "GetTodo"
命名路由:
[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(long 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 。
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)。