https://edu.csdn.net/course/detail/36074
https://edu.csdn.net/course/detail/35475
在上一篇文章中,老周介绍了用自定义 ModelBinder 的方式实现一个 API(或MVC操作方法)可以同时支持 JSON 格式和 Form-data 格式的数据正文。今天该轮到 InputFormatter 了——接下来老周会演示如何实现自定义的 InputFormatter,使其可以读取 CSV 格式的正文。
CSV 的格式比较简单,一般是一行文本一条数据记录,每条记录的字段值用逗号隔开(英文逗号)。
CSV 的妙处就是格式简单,一次性提交多条记录时体积较小。比如,我要提交一批员工信息。这个在客户端必须先知道员工对象中各属性的顺序,因为 CSV 是逗号分隔的文本,顺序不要打乱。
有时候我们可以这样规范一下:CSV 的第一行写字段标题,从第二行开始才是数据记录。就像这样的员工信息:
emp\_id, emp\_name, emp\_age, emp\_part
050025, 小张, 31, 开发部
050130, 小谢, 29, 市场部
038012, 小李, 37, 财务部
045211, 小刘, 36, 讨债部
其实,就算第一行写上字段名,这种规范也是没什么用处的,客户端可以瞎传,比如,它可以传成这样:
emp\_id, emp\_name, emp\_age, emp\_part
土坑部, 523014, 34, 小明
酸菜部, 301027, 28, 小何
牛肉部, 621143, 32, 老高
你瞧,这不全乱套了吗?所以,提不提供字段名一行其实不关键,关键是客户端在提交 CSV 数据时,你得按规矩来,不然这戏就演不下去了。
OK,现在咱们开始今天的表演吧。
首先,我们定义两个模型类。
public sealed class Album
{
public string? Title { get; set; } = string.Empty;
public int Year { get; set; }
public string? Artist { get; set; }
}
public sealed class Book
{
public string? Name { get; set; } = string.Empty;
public string? Author { get; set; }
public **int? Year** { get; set; }
public string? Publisher { get; set; }
}
之所以定义了两个类,是为了稍验证一下自定义的 InputFormatter 是否能通用。Album 类表示一张音乐专辑,有标题、发行年份、艺术家三个属性;Book 表示一本书的信息,有书名、作者、出版年份、出版社四个属性。注意 Book 类的 Year 属性的类型,我故意弄成了 int?,即可以 null 的整数值。稍后用于验证自定义的 InputFormatter 是否能处理这样的值类型。
接着,写一个控制器类和两个操作方法。
public class TestController : ControllerBase
{
[HttpPost, ActionName("buyalbums")]
public IActionResult NewAlbums(**[FromBody]IEnumerable** **albums**)
{
return Ok(albums);
}
[HttpPost, ActionName("buybooks")]
public IActionResult NewBooks(**[FromBody] Book[] books**)
{
return Ok(books);
}
}
这个控制器类没有应用 [ApiController] 特性,所以,要让其能从 body 读取数据,参数上要应用 [FromBody] 特性。这时候,两个操作方法的参数并不是单个模型对象,而是