练习 - 添加控制器
控制器是一个公共类,具有一个或多个称为“操作”的公共方法。 按照惯例,控制器放在项目根目录的 Controllers 目录中。 这些操作在 Web API 控制器内公开为 HTTP 终结点。
添加控制器
在 Visual Studio Code 中选择 Controllers 文件夹,并添加名为 PizzaController.cs 的新文件。
系统会在 Controllers 目录中创建名为 PizzaController.cs 的空类文件。 目录名称“Controllers”是一种约定。 目录名称来自 Web API 使用的模型-视图-控制器体系结构。
注意
按照惯例,控制器类名称后缀为 Controller。
将以下代码添加到 Controllers/PizzaController.cs。 保存所做更改。
using ContosoPizza.Models; using ContosoPizza.Services; using Microsoft.AspNetCore.Mvc; namespace ContosoPizza.Controllers; [ApiController] [Route("[controller]")] public class PizzaController : ControllerBase { public PizzaController() { } // GET all action // GET by Id action // POST action // PUT action // DELETE action }
如前文所述,此类派生自
ControllerBase
,后者是 ASP.NET Core 中用于处理 HTTP 请求的基类。 它还包含你已经了解的两个标准属性:即[ApiController]
和[Route]
。 如前文所述,[Route]
属性定义了到[controller]
令牌的映射。 由于此控制器类名为PizzaController
,因此该控制器处理对https://localhost:{PORT}/pizza
的请求。
获取所有披萨
需要实现的第一个 REST 谓词是 GET
,使用该谓词,客户端可以从 API 获取所有披萨。 可使用内置 [HttpGet]
属性来定义从服务返回披萨的方法。
将 Controllers/PizzaController.cs 中的 // GET all action
注释替换为以下代码:
[HttpGet]
public ActionResult<List<Pizza>> GetAll() =>
PizzaService.GetAll();
上一个操作:
- 仅响应 HTTP
GET
谓词,如[HttpGet]
属性所示。 - 返回类型为
List<Pizza>
的ActionResult
实例。ActionResult
类型是 ASP.NET Core 中所有操作结果的基类。 - 查询服务以获取所有披萨,并通过
Content-Type
的值application/json
自动返回数据。
检索单种披萨
客户端可能还需要请求获取特定披萨而非整个列表的相关信息。 你可以实现另一个 GET
操作,此操作需要 id
参数。 可使用内置 [HttpGet("{id}")]
属性来定义从服务返回披萨的方法。 路由逻辑将 [HttpGet]
(没有 id
)和 [HttpGet("{id}")]
(具有 id
)注册为两个不同的路由。 然后,你可以编写一个单独的操作来检索单个项。
将 Controllers/PizzaController.cs 中的 // GET by Id action
注释替换为以下代码:
[HttpGet("{id}")]
public ActionResult<Pizza> Get(int id)
{
var pizza = PizzaService.Get(id);
if(pizza == null)
return NotFound();
return pizza;
}
上一个操作:
- 仅响应 HTTP
GET
谓词,如[HttpGet]
属性所示。 - 要求
pizza/
之后的 URL 段中包含id
参数的值。 请记住,控制器级别的[Route]
属性定义了/pizza
模式。 - 查询数据库以获取与所提供的
id
参数匹配的披萨。
上述操作中使用的每个 ActionResult
实例都映射到下表中对应的 HTTP 状态代码:
ASP.NET Core 操作结果 |
HTTP 状态代码 | 说明 |
---|---|---|
Ok 为隐式 |
200 | 内存中缓存中存在与所提供的 id 参数匹配的产品。该产品包含在由 accept HTTP 请求标头中所定义的媒体类型(默认情况下为 JSON)的响应正文中。 |
NotFound |
404 | 内存中缓存中不存在与所提供的 id 参数匹配的产品。 |
生成并运行新控制器
运行以下命令,生成并启动 Web API:
dotnet run
使用 Http 文件测试控制器
打开 ContosoPizza.http
添加新的 GET 以在 ### 分隔符下调用
Pizza
终结点:GET {{ContosoPizza_HostAddress}}/pizza/ Accept: application/json ###
选择此新 GET 调用上方的“发送请求”命令。
前面的命令将以 JSON 格式返回所有披萨列表:
HTTP/1.1 200 OK Connection: close Content-Type: application/json; charset=utf-8 Date: Wed, 17 Jan 2024 16:57:09 GMT Server: Kestrel Transfer-Encoding: chunked [ { "id": 1, "name": "Classic Italian", "isGlutenFree": false }, { "id": 2, "name": "Veggie", "isGlutenFree": true } ]
若要查询单个披萨,可以使用以下命令发出
GET
请求,但传入id
参数:GET {{ContosoPizza_HostAddress}}/pizza/1 Accept: application/json ###
前面的命令返回
Classic Italian
,输出结果如下:HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 02 Apr 2021 21:57:57 GMT Server: Kestrel Transfer-Encoding: chunked { "id": 1, "name": "Classic Italian", "isGlutenFree": false }
我们的 API 还可以处理项不存在的情况。 使用以下命令再次调用该 API,但传入一个无效的披萨
id
参数:GET {{ContosoPizza_HostAddress}}/pizza/5 Accept: application/json ###
前面的命令返回
404 Not Found
错误,输出结果如下:HTTP/1.1 404 Not Found Content-Type: application/problem+json; charset=utf-8 Date: Fri, 02 Apr 2021 22:03:06 GMT Server: Kestrel Transfer-Encoding: chunked { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "traceId": "00-ec263e401ec554b6a2f3e216a1d1fac5-4b40b8023d56762c-00" }
现在,你已完成实现 GET
谓词。 在下一个单元中,你可以向 PizzaController
添加更多操作以支持对披萨数据执行 CRUD 操作。
可选:使用命令行 HTTP 读取-求值-打印循环 (REPL) 测试控制器
打开现有
httprepl
终端,或者通过从主菜单中选择“终端”>“新终端”,从 Visual Studio Code 中打开新的集成终端。通过运行以下命令连接到 Web API:
httprepl https://localhost:{PORT}
或者,在
HttpRepl
运行时随时运行以下命令:connect https://localhost:{PORT}
若要查看新的可用
Pizza
终结点,请运行以下命令:ls
前面的命令将检测连接的终结点上所有可用的 API。 此命令应显示以下代码:
https://localhost:{PORT}/> ls . [] Pizza [GET] WeatherForecast [GET]
运行以下命令以转到
Pizza
终结点:cd Pizza
前面的命令将显示
Pizza
终结点的可用 API 的输出:https://localhost:{PORT}/> cd Pizza /Pizza [GET]
使用以下命令在
HttpRepl
中发出GET
请求:get
前面的命令将以 JSON 格式返回所有披萨列表:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 02 Apr 2021 21:55:53 GMT Server: Kestrel Transfer-Encoding: chunked [ { "id": 1, "name": "Classic Italian", "isGlutenFree": false }, { "id": 2, "name": "Veggie", "isGlutenFree": true } ]
若要查询单个披萨,可以使用以下命令发出
GET
请求,但传入id
参数:get 1
前面的命令返回
Classic Italian
,输出结果如下:HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Date: Fri, 02 Apr 2021 21:57:57 GMT Server: Kestrel Transfer-Encoding: chunked { "id": 1, "name": "Classic Italian", "isGlutenFree": false }
我们的 API 还可以处理项不存在的情况。 使用以下命令再次调用该 API,但传入一个无效的披萨
id
参数:get 5
前面的命令返回
404 Not Found
错误,输出结果如下:HTTP/1.1 404 Not Found Content-Type: application/problem+json; charset=utf-8 Date: Fri, 02 Apr 2021 22:03:06 GMT Server: Kestrel Transfer-Encoding: chunked { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.4", "title": "Not Found", "status": 404, "traceId": "00-ec263e401ec554b6a2f3e216a1d1fac5-4b40b8023d56762c-00" }
在 Visual Studio Code 中,返回到下拉列表中的
dotnet
终端,并通过选择键盘上的 CTRL+C 来关闭 Web API。
现在,你已完成实现 GET
谓词。 在下一个单元中,你可以向 PizzaController
添加更多操作以支持对披萨数据执行 CRUD 操作。