ASP.NET Core MVC — IActionResult执行(1) ObjectResultExecutor与OutputFormatter的工作原理

在命名空间Microsoft.AspNetCore.Mvc.Infrastructure下,有一个ObjectResultExecutor类,继承自IActionResultExecutor<ObjectResult>,负责执行ObjectResult。其构造函数会注入被两个参数:

  • OutputFormatterSelector: 用于选择当前输出格式
  • IHttpResponseStreamWriterFactory: 用于向 HttpResponse 写入Stream的Writer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ObjectResultExecutor : IActionResultExecutor<ObjectResult>
{
public ObjectResultExecutor( OutputFormatterSelector formatterSelector,IHttpResponseStreamWriterFactory writerFactory, ILoggerFactory loggerFactory)
{
if (formatterSelector == null) { /* throw ;*/ }
if (writerFactory == null) { /* throw ;*/ }
if (loggerFactory == null) { /* throw ;*/ }

FormatterSelector = formatterSelector;
WriterFactory = writerFactory.CreateWriter;
Logger = loggerFactory.CreateLogger<ObjectResultExecutor>();
}

...
}

这里的ObjectResultExecutor所依赖的服务是在AddMvcCore()时自动注册的

1
2
3
services.TryAddSingleton<IHttpResponseStreamWriterFactory, MemoryPoolHttpResponseStreamWriterFactory>();
...
services.TryAddSingleton<OutputFormatterSelector, DefaultOutputFormatterSelector>();

注意这里的MemoryPoolHttpResponseStreamWriterFactory会生成一个HttpResponseStreamWriter向 HttpResponse中写入文本数据:

1
2
3
4
5
6
7
8
9
10
11
12
internal class MemoryPoolHttpResponseStreamWriterFactory : IHttpResponseStreamWriterFactory
{
...

public TextWriter CreateWriter(Stream stream, Encoding encoding)
{
if (stream == null) { /* throw ;*/ }
if (encoding == null){ /* throw ;*/ }

return new HttpResponseStreamWriter(stream, encoding, DefaultBufferSize, _bytePool, _charPool);
}
}

总之,借助于此工厂函数生成的TextWriter,我们可以把Text型数据(不可用于Binary数据)输出到响应流。

ObjectResultExecutor的关键方法实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public virtual Task ExecuteAsync(ActionContext context, ObjectResult result)
{
if (context == null) { /* throw ;*/ }
if (result == null) { /* throw ;*/ }

InferContentTypes(context, result);

var objectType = result.DeclaredType;
if (objectType == null || objectType == typeof(object))
{
objectType = result.Value?.GetType();
}

// 构建格式器上下文
var formatterContext = new OutputFormatterWriteContext( context.HttpContext, WriterFactory, objectType, result.Value);
// 生成对应的Formatter
var selectedFormatter = FormatterSelector.SelectFormatter( formatterContext, (IList<IOutputFormatter>)result.Formatters ?? Array.Empty<IOutputFormatter>(), result.ContentTypes);

if (selectedFormatter == null) { /* sttauscode=406 */ }

Logger.ObjectResultExecuting(result.Value);
// change result.ContentTypes
result.OnFormatting(context);

// 使用指定的格式化器写数据
return selectedFormatter.WriteAsync(formatterContext);
}

总体逻辑是:

  1. 构建格式化上下文(OutputFormatterWriteContext)
  2. 选择对应的输出格式化器(IOutputFormatter)
  3. 执行IOutputFormatter::WriteAsync(formatterContext)

这里的FormatterSelector服务类似于一个Provider,会根据指定的FormatterContext等信息生成一个IOutputFormatter。从而根据运行时的外部请求和特定条件把ObjectResult转换成特定格式的输出。