• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
PowerCoder
博客园    首页    新随笔    联系   管理    订阅  订阅

讲一下Asp.net core MVC2.1 里面的 ApiControllerAttribute (转载)

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

 

本文参考自:Exploring the ApiControllerAttribute and its features for ASP.NET Core MVC 2.1

 

0. ApiControllerAttribute 继承自 ControllerAttribute



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


ApiControllerAttribute是ControllerAttribute的子类,所以,框架在处理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。因此,写了很多第三方的库来解决这个问题,比如:

  • WebApiContrib.Core.Formatter.Bson
  • WebApiContrib.Core.Formatter.Csv
  • 其他不写了,,就举个例子

现在,这些可以自动解决了。
除此之外,如果一个参数在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")]的方式。

 

顺便说一下,[Route("XXX")]特性标签是支持定义[controller]和[action]路由参数的,例如:

[ApiController]
[Route("api/[controller]/[action]")]
public class BookController : Controller
{
    [HttpPost]
    public Book AddBook(Book book)
    {
        //模拟做一些数据操作

        return book;
    }

    [HttpPost]
    public Book UpdateBook(Book book)
    {
        //模拟做一些数据操作

        return book;
    }

    [HttpGet]
    public Book GetBookById(string bookId)
    {
        //模拟做一些数据操作
        var book = new Book();

        return book;
    }

    [HttpGet]
    public Book GetBookByName(string bookName)
    {
        //模拟做一些数据操作
        var book = new Book();

        return book;
    }
}

 

 

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; }
}

 

 

原文链接

 

posted @ 2019-02-01 19:24  PowerCoder  阅读(465)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3