【ASP.NET Core】深入理解Controller的工作机制

系列文章目录

链接: 【ASP.NET Core】REST与RESTful详解,从理论到实现



前言

Web开发中,我们时刻都在和HTTP请求打交道,构建路由,解析请求,响应结果。在ASP.NET Core MVC/Web API中,有一个核心的组件,名为Controller。作为处理请求、协调模型和视图/响应的“指挥中心”。本文将深入探讨Controller的角色、功能。


一、Controller的泛用职责

Controller本质上是一个包含了Action方法的类,负责处理客户端HTTP请求,然后调用指定业务逻辑,最后再返回响应给客户端。可以把Controller简单的理解成请求的入口点(实际上Controller是连接着请求管道的末尾),通过路由映射,再匹配到到具体的Action调用逻辑。

我们可以把Controller的职责总结为三部分:

  • 处理客户端HTTP请求,映射路由,解析参数
  • 匹配具体Action,调用业务逻辑方法
  • 生成响应,返回客户端。

请求管道与控制器

实际上,作为一个完整的ASP.NET Core请求管道,严格来说Controller不属于请求管道本身,而Controller是这条管道最后抵达的“终点”。
Controller的目的是执行业务逻辑,它需要通过路由中间件和端点中间件与请求管道集成。在这个请求管道里,还有那些认证授权,记录日志等通用的中间件,抑或是限流等自定义中间件。事实上请求管道也就是由多个中间件组成的一条处理链

ASP.NET Core WebAPI里的Controller继承自ControllerBase;ASP.NET Core MVC里的Controller继承自Controller,其Controller内部也是继承自ControllerBase;:

  • ControllerBase是所有控制器的抽象基类,仅包含处理 HTTP 请求的核心功能,如状态码返回、模型验证、路由绑定等。
  • Controller继承自ControllerBase,并额外集成了视图引擎相关功能,用于处理需要返回视图。

二、Controller的特性

2.1 ApiController

ApiController是一个用于控制器的特性,主要是当传入的参数不符合数据模型验证,会自动返回400 Bad Request响应。

2.2 属性路由Route

控制器里的方法上通过标注Route自定义特性,来表示这个Controller包含的Action方法如何匹配。路由Route特性里可路由模板自定义路径。

[Route("api/[controller]")]

GET方法 https://localhost:7027/api/[controller名称]

像类似"api/[controller]",模板里的[controller]是占位符。通过控制器的名称结合HTTP属性匹配Action方法。

https://localhost:7027/api/[controller名称]/[action名称]

也可也在模板里留下自定义action路径,通过控制器的名称和方法名称来匹配路由。

2.3 Action方法HTTP特性

  • [HttpGet]
  • [HttpGet(“{id}”)]
  • [HttpPost]
  • [HttpPut(“{id}”)]
  • [HttpPatch(“{id}”)]
  • [HttpDelete(“{id}”)]
    诸如以上几种方式,通过在Action前指定HTTP特性,便可通过不同的请求方式获取不同的Action。

2.4 Action方法的异步实现

使用async修饰Action,有返回值的通过Task包裹。Action的名称可不用Async尾缀修饰。

[HttpGet("{id}")]
public async Task<ActionResult<Student>> GetStudent(int id)

三、Action方法参数

3.1 URL占位符

在[HttpGet]、[HttpPost]等HTTP特性修饰的Action方法中使用占位符,来解析URL路径中的内容。
比如

[HttpGet("{id}")]
public ActionResult<Student> GetStudent(int id)
{
    var student = _students.FirstOrDefault(e => e.Id == id);

    if (student == null)
        return NotFound("");

    return Ok(student);
}

这个[HttpGet(“{id}”)]修饰的Action方法,就会解析URL里的匹配{id}占位符的内容,并且赋与GetStudent方法里的(int id)参数。

但是如果占位符的名字和方法里的参数名称不一致,可以用[FromRoute(Name=“名字”)]进行手动映射

[HttpGet("{idStr}")]
public ActionResult<Student> GetStudent([FromRoute(Name = "idStr")]string id)

3.2 QueryString

QueryString在请求路径是以?xxx=yyy&…的形式存在。如果Querystring中的名字和Action方法参数对应,那么框架会自动匹配。

[HttpGet]
public ActionResult<Student> GetStudent(int id)
{
    var student = _students.FirstOrDefault(e => e.Id == id);

    if (student == null)
        return NotFound("");

    return Ok(student);
}

但是如果占位符的名字和方法里的参数名称不一致,可以用[FromRoute(Name=“名字”)]进行手动映射

[HttpGet]
public ActionResult<Student> GetStudent([FromQuery(Name ="idStr")]string id)

3.3 请求报文体

一般HTTP的POST/PUT/PATCH请求中会用到报文体,一般通过[FromBody]绑定JSON数据(JSON格式的请求体是主流,客户端设定请求头中的Content-Type为application/json)。也有少部分通过表单提交数据[FromForm]。

存在URL占位符,QueryString和报文体嵌套的情况下,厘清Action方法里的数据参数对应的来源,用相应的诸如[FromRoute] [FromQuery] [FromBody]等修饰参数

四、Controller里的结果响应

在 ASP.NET Core WebAPI中,默认响应格式是JSON。当return Ok(data),框架会自动将对象序列化为JSON并设置响应头 Content-Type: application/json。
在返回格式上我们一般常用IActionResult 和 ActionResult。这样的好处是可以方便的调用框架里的各种封装好的方法,如各类状态码的返回。

4.1 IActionResult

IActionResult的使用在ASP.NET Core MVC中较为常见。这里的控制器的主要职责是返回视图,而非直接返回数据。
常见的诸如:
返回模型

 return View(model);

重定向

return RedirectToAction("Login", "Auth"); 

下载excel

return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");

这些都不是直接返回数据。MVC控制器的核心是视图渲染,以及混合这多种类型(视图、重定向、文件等)的返回。

4.2 ActionResult

Web API 通常返回单一类型的数据,并且考虑到格式,我们更加倾向于使用ActionResult的泛型ActionResult< T>返回数据对象,自动处理序列化和状态码。

[HttpGet("{id}")]
public ActionResult<Student> GetStudent(int id)
{
    var student = _students.FirstOrDefault(e => e.Id == id);

    if (student == null)
        return NotFound("");
    return student;
}

如上代码直接返回student,会被框架自动处理序列化和状态码。

这点我们观察泛型ActionResult< T>内部实现,它包含两个隐式的操作符转换。

  • 方法签名返回ActionResult< TValue>,但实际返回 TValue
  • 方法签名返回ActionResult< TValue>,但实际返回ActionResult result。

这两者都会触发new ActionResult< TValue>的执行。这样的好处是可以直接返回一个数据对象,或者一个ActionResult的方法。

如果没有隐式操作符

// 必须手动调用Ok()包裹示例
return Ok(classInstance);
// 必须手动包装
return new ActionResult<ClassName>(new NotFoundResult());

隐式操作符转换

/// <summary>
/// Implicitly converts the specified <paramref name="value"/> to an <see cref="ActionResult{TValue}"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
public static implicit operator ActionResult<TValue>(TValue value)
{
    return new ActionResult<TValue>(value);
}

/// <summary>
/// Implicitly converts the specified <paramref name="result"/> to an <see cref="ActionResult{TValue}"/>.
/// </summary>
/// <param name="result">The <see cref="ActionResult"/>.</param>
public static implicit operator ActionResult<TValue>(ActionResult result)
{
    return new ActionResult<TValue>(result);
}

总结

本文详解ASP.NET Core中Controller的职责、基类差异、特性、Action 参数绑定及响应方式,展现其处理HTTP 请求的核心作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值