一、内容协商
依然以返回Book类型的Action为例,看看它是怎么被转换为JSON类型的。
public Book GetModel() { return new Book() { Code = "1001", Name = "ASP" }; }
这个Action执行后被封装为ObjectResult,接下来就是这个ObjectResult的执行过程。
ObjectResult的代码如下:
public class ObjectResult : ActionResult, IStatusCodeActionResult { //部分代码略 public override Task ExecuteResultAsync(ActionContext context) { var executor = context.HttpContext.RequestServices.GetRequiredService<IActionResultExecutor<ObjectResult>>(); return executor.ExecuteAsync(context, this); } }
它是如何被执行的呢?首先会通过依赖注入获取ObjectResult对应的执行者,获取到的是ObjectResultExecutor,然后调用ObjectResultExecutor的ExecuteAsync方法。代码如下:
public class ObjectResultExecutor : IActionResultExecutor<ObjectResult> { //部分代码略 public virtual Task ExecuteAsync(ActionContext context, ObjectResult result) { //部分代码略 var formatterContext = new OutputFormatterWriteContext( context.HttpContext, WriterFactory, objectType, result.Value); var selectedFormatter = FormatterSelector.SelectFormatter( formatterContext, (IList<IOutputFormatter>)result.Formatters ?? Array.Empty<IOutputFormatter>(), result.ContentTypes); if (selectedFormatter == null) { // No formatter supports this. Logger.NoFormatter(formatterContext); context.HttpContext.Response.StatusCode = StatusCodes.Status406NotAcceptable; return Task.CompletedTask; } result.OnFormatting(context); return selectedFormatter.WriteAsync(formatterContext); } }
核心代码就是FormatterSelector.SelectFormatter()方法,它的作用是选择一个合适的Formatter。Formatter顾名思义就是一个用于格式化数据的类。系统默认提供了4种Formatter,如下图 1
图 1
它们都实现了IOutputFormatter接口,继承关系如下图 2:
图 2
IOutputFormatter代码如下:
public interface IOutputFormatter { bool CanWriteResult(OutputFormatterCanWriteContext context); Task WriteAsync(OutputFormatterWriteContext context); }
又是非常熟悉的方式,就像在众多XXXResultExecutor中筛选一个合适的Action的执行者一样,首先将它们按照一定的顺序排列,然后开始遍历,逐一执行它们的CanXXX方法,若其中一个的执行结果为true,则它就会被选出来。例如StringOutputFormatter的代码如下:
public class StringOutputFormatter : TextOutputFormatter { public StringOutputFormatter() { SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); SupportedMediaTypes.Add("text/plain"); } public override bool CanWriteResult(OutputFormatterCanWriteContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.ObjectType == typeof(string) || context.Object is string) { return base.CanWriteResult(context); } return false; } //省略部分代码 }
从StringOutputFormatter的CanWriteResult方法中可以知道它能处理的是string类型的数据。它的构造方法中标识它可以处理的字符集为UTF8和Unicode。对应的数据格式标记为“text/plain”。同样查看HttpNoContentOutputFormatter和HttpNoContentOutputFormatter对应的是返回值为void或者task的,StreamOutputFormatter对应的是Stream类型的。