讲一下Asp.net core MVC2.1 里面的 ApiControllerAttribute

先贴文章链接

正文

ASP.NET Core MVC 2.1 特意为构建 HTTP API 提供了一些小特性,今天主角就是 ApiControllerAttribute. (注:文章是18年2月份的,所以文章提到了core2.1还没发布)。

0. ApiControllerAttribute 继承自 ControllerAttribute

ASP.NET Core MVC 已经有了ControllerAttribute,这个用来标注一个类型是否是Controller。标注了之后框架就知道哪些是系统里面的Controller了。(框架也有其他方法来获取程序里面的Controller,所以,这个ControllerAttribute不是必须的)。

ApiControllerAttributeControllerAttribute的子类,所以,框架在处理Controller发现的时候和ControllerAttribute标注的对象是一样的。

但是,因为ApiControllerAttribute 实现了IApiBehaviorMetadata接口,所以提供了一些额外的特这些特性是以HTTP Api为出发点的。下面介绍一下这些特性。

1. 自动模型状态验证

这个是重点,框架会帮你自动验证model的state,也就是ModelState.(注:不过我就是因为用FluentValidation的时候模型验证不管用了出问题了才找到这篇文章的).

框架会为你自动注册ModelStateInvalidFilter,这个会运行在OnActionExecuting事件里面(具体来说:在action执行之前,model绑定之后)。他内部会检查ModelState是否为Valid,如果为InValid会直接返回400 BadRequest,这样就没有必要执行后面的代码,提高效率。

它会自动把model state 放到response里面,content type 是application/problem+json。当然你也可以自定义,因为毕竟你会有自己的验证,后文会讲。

下面,我们先来举个例子说一下。

  • 之前的写法
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook([FromBody]Book book)
    {
        if (ModelState.IsValid) //判断状态
        {
            return BadRequest(ModelState);
        }
        //其他代码。。。
    }
}
  • 现在可以这么写
[ApiController]
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook(Book book)
    {
        //直接写,不用验证modelstate
    }
}

顺道说一下,ModelStateInvalidFilter是个公共类,所以,不用ApiControllerAttribute也可以使用它。

2.参数绑定策略的自动推断

另一个非常有用的特性是action里面的参数的模型绑定可以自动推断。

ASP.NET Core MVC里面有一个比较令人恼怒的问题你需要手动给参数指定[FromBody]这个特性,以便让系统知道如何从Request body里面反序列化他们,比如反序列化json。因此,写了很多第三方的库来解决这个问题,比如:

现在,这些可以自动解决了。

除此之外,如果一个参数在route里面定义了,他会自动从先从path,也就是url上尝试绑定,不行的话会去从查询参数上绑定。IFormFlie默认从form表单上绑定获取。

下面看代码:

  • 之前
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook([FromBody]Book book)
    {
        // 写代码
    }
}
  • 现在
[ApiController]
[Route("[controller]")]
public class BookController : Controller
{
    [HttpPost("")]
    public IActionResult PostBook(Book book)//FromBody没必要写了
    {
        // 写代码
    }
}

3. 处理multipart/form-data请求

如果你的action里面的一个参数指定了[FromFile]特性(这通常是用于文件上传的),框架会自动假设请求是multipart/form-data。这个是用来解决社区里面提的这个问题

不过这个也是可选的,只要你自己定义在action上定义一下[Consumes(...)]

4.其他

有两个注意点:

  1. ApiExplorer 的可见性。 默认所有的controller对ApiExplorer都是可见的,所以,不影响swagger 等的生成。
  2. 只是一个基于特性的路由。集中的路由机制不会应用在API controller,框架要求只能使用基于特性的路由,即在action上指定[Route("XXX")]的方式。

5. 行为自定义

像MVC框架的大部分组件一样,ApiControllerAttribute的行为是高度可自定义的。首先,上面说的大部分内容都是可以简单的用 on/off 来切换。

具体的设置是在startup方法里面通过ApiBehaviorOptions来实现,先来看一下这个类。

    public class ApiBehaviorOptions
    {
        public Func<ActionContext, IActionResult> InvalidModelStateResponseFactory { get; set; }

        public bool SuppressModelStateInvalidFilter { get; set; }

        public bool SuppressInferBindingSourcesForParameters { get; set; }

        public bool SuppressConsumesConstraintForFormFileParameters { get; set; }
    }

所有bool类型的属性默认都是false。Suppres有阻止的意思。可以通过以下方法进行设置。

services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
    options.SuppressConsumesConstraintForFormFileParameters = true;
});

来看一下InvalidModelStateResponseFactory属性,他是一个返回IActionResult的Func,通过他,我们可以注入自己的委托来实现需要的返回类型,举个例子。

services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = actionContext => 
    {
        var errors = actionContext.ModelState
            .Where(e => e.Value.Errors.Count > 0)
            .Select(e => new Error
            {
            Name = e.Key,
            Message = e.Value.Errors.First().ErrorMessage
            }).ToArray();
 
        return new BadRequestObjectResult(errors);
    }
});
 
class Error
{
    public string Name { get; set; }
 
    public string Message { get; set; }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hotqin888的专栏

engineercms利用pdf.js制作连续看图功能

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/det...

1681
来自专栏JackieZheng

探秘Tomcat——连接器和容器的优雅启动

前言: 上篇《探秘Tomcat——启动篇》粗线条的介绍了在tomcat在启动过程中如何初始化Bootstrap类,加载并执行server,从而启动整个tomc...

2348
来自专栏JackieZheng

探秘Tomcat——启动篇

tomcat作为一款web服务器本身很复杂,代码量也很大,但是模块化很强,最核心的模块还是连接器Connector和容器Container。具体请看下图: ? ...

4677
来自专栏haifeiWu与他朋友们的专栏

golang重构博客统计服务

作为一个后端开发,在docker,etcd,k8s等新技术不断涌现的今天,其背后的功臣golang在语言排行榜上持续走高,因此楼主也就开了这次使用golang自...

1162
来自专栏DOTNET

ASP.NET MVC编程——单元测试

1自动化测试基本概念 自动化测试分为:单元测试,集成测试,验收测试。 单元测试 检验被测单元的功能,被测单元一般为低级别的组件,如一个类或类方法。 单元测试要满...

5175
来自专栏跟着阿笨一起玩NET

C# 地磅串口编程

然后最近有一个项目用到了地磅,这里也是通过串口通讯方式进行数据交互,说实话,地磅这东西,实在有点不方便。

1662
来自专栏逆向技术

64位内核第二讲,进程保护之对象钩子

         64位内核第二讲,进程保护. 一丶什么是保护. 什么是保护. 比如我们安装了xxx杀毒软件.那么此时你用任务管理器关闭.是关闭不了的.原因是内...

3326
来自专栏Java帮帮-微信公众号-技术文章全总结

Java多线程详解5【面试+工作】

Java多线程详解【面试+工作】 Java线程:新特征-信号量 Java的信号量实际上是一个功能完毕的计数器,对控制一定资源的消费与回收有着很重要的意义,信号量...

41610
来自专栏MasiMaro 的技术博文

hook键盘驱动中的分发函数实现键盘输入数据的拦截

我自己在看《寒江独钓》这本书的时候,书中除了给出了利用过滤的方式来拦截键盘数据之外,也提到了另外一种方法,就是hook键盘分发函数,将它替换成我们自己的,然后再...

942
来自专栏calvin

【nodejs】让nodejs像后端mvc框架(asp.net mvc)一orm篇【如EF般丝滑】typeorm介绍(8/8)

在使用nodejs开发过程中,刚好碰到需要做一个小工具,需要用到数据库存储功能。而我又比较懒,一个小功能不想搞一个nodejs项目,又搞一个后端项目。不如直接在...

3502

扫码关注云+社区

领取腾讯云代金券