一、概述
书接上回,上节我们留了几个问题,即:
1、接口Hello的名字究竟怎么来的?
2、我们如果想把接口改成其他的名称,应该怎么做?
3、我们继承ControllerBase,以及[Route(“[controller]”)]属性,又是分别起到什么作用?
需要解决以上几个问题,我们就要继续深入研究Controller相关的一些东西,主要包括ControllerBase基类、ApiController 属性,我们想要精通某个技术,必须要了解器原理,才能做到触类旁通、灵活运用。
ControllerBase 是Web API 控制器的基类,换句话说,Web API 控制器通常都应继承 ControllerBase。做过MVC项目的朋友可能都知道,在MVC里面控制器一般继承Controller。两者有什么关系和区别吗?
二、ControllerBase
1、ControllerBase基类和Controller基类的关系
查看ControllerBase 的定义,这是一个抽象类;
再看Controller的定义,其继承了ControllerBase
两者的关系是:Controller基类派生自ControllerBase
2、ControllerBase基类和Controller基类的区别
Controller 派生自 ControllerBase,主要区别是Controller 功能更强大,主要添加对视图的支持,因此它用于处理 Web 页面,而不是 Web API 请求。 如果同一控制器需要同时支持视图和 Web API,那应该派生自 Controller。如果仅仅需要支持 Web API,那就派生自 ControllerBase即可,这样更加轻量级。
3、ControllerBase作用
ControllerBase 类主要作用是提供了很多用于处理 HTTP 请求的属性和方法。比如我们可以通过这个基类提供的Request属性获取请求头信息:
// 获取请求头
var headers = this.Request.Headers;
foreach (var kvp in headers)
{
Console.WriteLine($"{kvp.Key} : {kvp.Value}");
}
运行结果可以看到我们把请求头的全部信息都获取到了
有关ControllerBase 可用方法和属性的列表,具体不展开,可参考官方文档。
三、[ApiController]属性
作用:[ApiController] 属性应用于控制器类,让控制器上或该控制器下的的API具有以下几个固定的功能
(1)、属性路由要求
(2)、自动 HTTP 400 响应
(3)、绑定源参数推理
(4)、Multipart/form-data 请求推理
(5)、错误状态代码的问题详细信息
下面分别阐述
1、属性路由要求
先介绍 ASP.*** Core 中的路由的两种方式 传统路由 和 属性路由
传统路由实现方式:
// 在Program.cs中或Startup.cs中使用
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
或者
// 在Program.cs中或Startup.cs中使用
app.MapDefaultControllerRoute();
属性路由的实现方式:
// 在Program.cs中或Startup.cs中使用
app.MapControllers();
然后在Controller中使用[Route(“”)]属性指定路由名称,如示例代码:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
介绍完了两种路由,再来看什是属性路由要求?
所谓属性路由要求,意思就是如果Controller加了[ApiController]属性,就必须走属性路由的方式。这也就能解释示例代码Controller上存在[ApiController][Route(“[controller]”)]这两个属性。
2、自动 HTTP 400 响应
什么是http 400?比如我们对用户注册的手机号码格式有校验要求,手机号码格式不正确时候,这个时候参数校验就会不通过,需要写如下代码。
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
[ApiController] 属性使模型验证错误自动触发 HTTP 400 响应。 因此,操作方法中不需要上述代码。
3、绑定源参数推理
我们获取参数的时候,有以下一些绑定源:
[FromBody] 请求正文
[FromForm] 请求正文中的表单数据
[FromHeader] 请求标头
[FromQuery] 请求查询字符串参数
[FromRoute] 当前请求中的路由数据
[FromServices] 作为操作参数插入的请求服务
[AsParameters] 方法参数
[ApiController] 属性借助推理规则,无需手动识别绑定源。先大概描述下,后续在参数传递和绑定章节再详细展开这些规则。
4、Multipart/form-data 请求推理
[ApiController] 属性针对 IFormFile 和 IFormFileCollection 类型的操作。 为这些类型自动推断内容类型为: multipart/form-data。不需要人工在请求Header头上指定类型。
5、错误状态代码的问题详细信息
错误结果(状态代码>= 400)以ProblemDetails展示。
ProblemDetails的定义如下:
看怎么使用:
比如有如下的代码,抛出一个404 NotFound的异常
if (pagePath == null)
{
return NotFound();
}
NotFound 方法会生成带 ProblemDetails 正文的 HTTP 404 状态代码。 例如:
{
type: "https://tools.ietf.org/html/rfc7231#section-6.5.4",
title: "Not Found",
status: 404,
traceId: "0HLHLH32KRN20:00000001"
}
小结
以上5点就是[ApiController]属性起到的作用,接下来我们来介绍[RouteAttribute]属性的作用。
四、[RouteAttribute]属性
其实上面属性路由环节基本已经介绍了这个属性,这个属性的作用就是:指定控制器上的属性路由
包含三个属性:
Name :路由名称。
Order :路由执行的顺序。
Template :路由模板。 可为 null。
其中,最关键的Template ,一般通过Template指定接口的访问路径,比如:
[Route(“/route/norest/{name?}/”)]
五、解决上节遗留问题
有了这些基础知识,我们来解决上节遗留的问题:
1、接口Hello的名字究竟怎么来的?
答:接口Hello的名字是根据下面代码有关
[Route("[controller]")]
public class HelloController : ControllerBase
因为Controller的名称为Hello,根据restful API规范,Controller里面又只有一个Get请求的接口,故Action的定义没有影响到接口的最终名称。
2、我们如果想把接口改成其他的名称,应该怎么做?
答:修改成如下代码即可,比如改成Greeting
[ApiController]
[Route("")]
public class HelloController : ControllerBase
{
[HttpGet("Greeting",Name = "GetHello")]
public string Hello()
{
return "Hello,World!";
}
}
有两处改动,去除Controller的Route名称,改为空字符串,然后给Hello方法指定Template(注:HttpGet可以直接指定Template )。
运行结果如下:
可以看到,名称已经修改成功。
3、我们继承ControllerBase,以及[Route(“[controller]”)]属性,又是分别起到什么作用?
答:这个上面都有详细描述了
六 、总结
本节的目标主要讲述controller相关的一些知识,再解决第1个问题的过程中,出现了一个restful API规范的概念,以及在路由环节的介绍基本也是带过,下节我们将深入路由环节,揭开它的神秘面纱。
敬请期待…