ASP.NET Core: 二十一. 内容协商与自定义IActionResult和格式化类(一)

简介: 上一章的结尾留下了一个问题:同样是ObjectResult,在执行的时候又是如何被转换成string和JSON两种格式的呢?本章来解答这个问题,这里涉及到一个名词:“内容协商”。除了这个,本章将通过两个例子来介绍如何自定义IActionResult和格式化类。

一、内容协商

依然以返回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

4.png

图 1

它们都实现了IOutputFormatter接口,继承关系如下图 2:

5.png

图 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类型的。


目录
相关文章
|
1月前
|
开发框架 .NET C#
C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式
【10月更文挑战第9天】在 C#/.NET Core 中,有多种方法可以删除字符串的最后一个字符,包括使用 `Substring` 方法、`Remove` 方法、`ToCharArray` 与 `Array.Copy`、`StringBuilder`、正则表达式、循环遍历字符数组以及使用 LINQ 的 `SkipLast` 方法。
|
22天前
.NET 4.0下实现.NET4.5的Task类相似功能组件
【10月更文挑战第29天】在.NET 4.0 环境下,可以使用 `BackgroundWorker` 类来实现类似于 .NET 4.5 中 `Task` 类的功能。`BackgroundWorker` 允许在后台执行耗时操作,同时不会阻塞用户界面线程,并支持进度报告和取消操作。尽管它有一些局限性,如复杂的事件处理模型和不灵活的任务管理方式,但在某些情况下仍能有效替代 `Task` 类。
|
30天前
|
Windows
.NET 隐藏/自定义windows系统光标
【10月更文挑战第20天】在.NET中,可以使用`Cursor`类来控制光标。要隐藏光标,可将光标设置为`Cursors.None`。此外,还可以通过从文件或资源加载自定义光标来更改光标的样式。例如,在表单加载时设置`this.Cursor = Cursors.None`隐藏光标,或使用`Cursor.FromFile`方法加载自定义光标文件,也可以将光标文件添加到项目资源中并通过资源管理器加载。这些方法适用于整个表单或特定控件。
|
1月前
|
API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
23 0
|
3月前
|
开发框架 .NET Docker
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
|
3月前
|
缓存 程序员
封装一个给 .NET Framework 用的内存缓存帮助类
封装一个给 .NET Framework 用的内存缓存帮助类
|
5月前
|
存储 Go C#
【.NET Core】深入理解IO之File类
【.NET Core】深入理解IO之File类
77 6
|
5月前
|
安全 程序员 Shell
老程序员分享:NSIS自定义界面,下载并安装Net.Framework4.8
老程序员分享:NSIS自定义界面,下载并安装Net.Framework4.8
|
5月前
|
存储 分布式计算 大数据
MaxCompute操作报错合集之自定义udf的函数,引用了import net.sourceforge.pinyin4j.PinyinHelper;但是上传资源后,出现报错,是什么原因
MaxCompute是阿里云提供的大规模离线数据处理服务,用于大数据分析、挖掘和报表生成等场景。在使用MaxCompute进行数据处理时,可能会遇到各种操作报错。以下是一些常见的MaxCompute操作报错及其可能的原因与解决措施的合集。
106 0
|
5月前
|
存储 开发框架 缓存
【.NET Core】你真的了解HttpRuntime类吗
【.NET Core】你真的了解HttpRuntime类吗
48 0