65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET Core 自定义格式化程序构建(YAML 格式化程序)

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2017 年 3 月 22 日

CPOL

4分钟阅读

viewsIcon

9239

了解如何为 ASP.NET Core 应用程序创建自定义请求/响应格式化程序

引言

不久前,我开始接触 .NET Core 的格式化程序。简而言之,格式化程序的作用是以您偏好的数据格式格式化您的响应和请求。例如,如果使用 Json 格式化程序,它会将您的响应(控制器操作返回的值)和请求(作为参数传递给控制器的值)格式化为 Json。XML 和其他格式化程序也是如此。.NET Core 开箱即用提供了一些格式化程序。 此官方文档 对它们进行了简要介绍。

但我们还是少谈格式化程序,多看看如何创建我们自己的自定义格式化程序吧。我想这正是我们来这里的目的,对吧?是的!让我们开始吧。

因此,框架提供了两个 abstract 类:InputFormmeterOutputFormatter。基本上,您会想使用这些类来创建自己的格式化程序。但是,还有另外两个 abstract 类扩展自这两个格式化程序。TextInputFormatterTextOuputFormatter 可以处理简单的 string 表示的数据格式的响应(数据也可以是二进制形式)。例如,JsonXML 格式化程序就扩展了这些类。我们将构建两个 Yaml 格式化程序,一个用于输入,另一个用于输出格式化。

那么,YAML 是什么呢?这是我直接从维基百科上抓取的定义:

YAML 是一种人类可读的数据序列化语言。它通常用于配置文件,但也可用于许多存储(例如调试输出)或传输(例如文档头)数据的应用程序。YAML 针对与 XML 相同的许多通信应用程序,但采取了更简洁的方法,并且故意打破与 SGML 的兼容性。YAML 是 JSON 的超集,JSON 是另一种简洁的数据序列化格式,它使用缩进而不是大括号和方括号。

这个想法非常简单。在使用 Yaml 输出格式化程序时,您将从当前的 HttpContext 中获取响应(控制器操作的返回值),将其 Serialize 为原始 Yaml 响应文本,然后将其发送回客户端。输入格式化程序也基本相同。在这种情况下,您将 Deserialize 来自客户端请求的 Yaml 内容,并以通用形式使用它们。另一件重要的事情是,您必须为这些格式化程序显式设置媒体类型头。这样做将在客户端定义具有该特定媒体类型格式(application/x-yaml)的 Accept 头(用于输出)和 Content-Type(用于输入)时激活这些格式化程序。

如果您不想在调用控制器操作时使用这些头,可以在获取或发布内容时显式定义应使用的格式化程序类型。例如,[Produces(application/x-yaml)] 将以 Yaml 格式返回响应,无论您是否定义了 Accept 头。同样,使用 [Consumes(application/x-yaml)] 属性将仅接受 Yaml 内容,无论您是否定义了 Content-Type

历史课就到这里。下面是 Yaml 的输入格式化程序。顺便说一下,我正在使用 Antoine Aubry(@antoineaubry)的 YamlDotNet 库进行 Yaml 的序列化和反序列化过程。

YamlInputFormatter.cs

public class YamlInputFormatter : TextInputFormatter
{
    private readonly Deserializer _deserializer;

    public YamlInputFormatter(Deserializer deserializer)
    {
        _deserializer = deserializer;

        SupportedEncodings.Add(Encoding.UTF8);
        SupportedEncodings.Add(Encoding.Unicode);
        SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationYaml);
        SupportedMediaTypes.Add(MediaTypeHeaderValues.TextYaml);
    }

    public override Task<InputFormatterResult> ReadRequestBodyAsync
                    (InputFormatterContext context, Encoding encoding)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (encoding == null)
        {
            throw new ArgumentNullException(nameof(encoding));
        }

        var request = context.HttpContext.Request;

        using (var streamReader = context.ReaderFactory(request.Body, encoding))
        {
            var type = context.ModelType;

            try
            {
                var model = _deserializer.Deserialize(streamReader, type);
                return InputFormatterResult.SuccessAsync(model);
            }
            catch (Exception)
            {
                return InputFormatterResult.FailureAsync();
            }
        }
    }
}

代码基本上是自 explanatory 的。从请求正文中获取 Yaml 内容,然后将其反序列化为通用类型,您就完成了。

这是 YamlOutputFormatter.cs

public class YamlOutputFormatter : TextOutputFormatter
    {
        private readonly Serializer _serializer;

        public YamlOutputFormatter(Serializer serializer)
        {
            _serializer = serializer;

            SupportedEncodings.Add(Encoding.UTF8);
            SupportedEncodings.Add(Encoding.Unicode);
            SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationYaml);
            SupportedMediaTypes.Add(MediaTypeHeaderValues.TextYaml);
        }

        public override async Task WriteResponseBodyAsync
              (OutputFormatterWriteContext context, Encoding selectedEncoding)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (selectedEncoding == null)
            {
                throw new ArgumentNullException(nameof(selectedEncoding));
            }

            var response = context.HttpContext.Response;
            using (var writer = context.WriterFactory(response.Body, selectedEncoding))
            {
                WriteObject(writer, context.Object);

                await writer.FlushAsync();
            }
        }

        private void WriteObject(TextWriter writer, object value)
        {
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            _serializer.Serialize(writer, value);
        }
    }

如果您想知道 MediaTypeHeaderValues 是从哪里来的?这是一个简单的类,我在其中为我的应用程序设置了所有媒体类型头。

internal class MediaTypeHeaderValues
{
    public static readonly MediaTypeHeaderValue ApplicationYaml
        = MediaTypeHeaderValue.Parse("application/x-yaml").CopyAsReadOnly();

    public static readonly MediaTypeHeaderValue TextYaml
        = MediaTypeHeaderValue.Parse("text/yaml").CopyAsReadOnly();
}

请注意,YamlInputFormatter 的构造函数接受一个 Deserializer,而 YamlOutputFormatter 的构造函数接受一个 Serializer。我们在 Startup.csConfigureServices 方法中配置格式化程序时,会构建带有某些选项的 SerializerDeserializer

public void ConfigureServices(IServiceCollection services)
{
    // Add framework services
    services.AddMvc(options=>
    {
        options.InputFormatters.Add(new YamlInputFormatter(new DeserializerBuilder().
              WithNamingConvention(namingConvention: new CamelCaseNamingConvention()).Build()));
        options.OutputFormatters.Add(new YamlOutputFormatter(new SerializerBuilder().
              WithNamingConvention(namingConvention: new CamelCaseNamingConvention()).Build()));
        options.FormatterMappings.SetMediaTypeMappingForFormat
                       ("yaml", MediaTypeHeaderValues.ApplicationYaml);
    });
}

一个简单的 GET 请求,Accept 头设置为 application/x-yaml

一个简单的 POST 请求,Content-Type 头设置为 application/x-yaml

格式化程序映射器是一个很棒的选项,在从浏览器客户端调用操作并指定格式时非常有用。例如,设置 [HttpGet("/api/[controller].{format}")] 属性将以浏览器 URL 中定义的格式返回操作结果。

[FormatFilter]
[HttpGet]
[HttpGet("/api/[controller].{format}")]
public IEnumerable<Geek> Get()
{
    return new List<Geek>()
    {
        new Geek() { Id = 1, Name = "Fiyaz", Expertise="Javascript", Rating = 3.0M },
        new Geek() { Id = 2, Name = "Rick", Expertise = ".Net", Rating = 5.0M }
    };
}

您可以像这样调用操作:http://appurl/geeks.yaml 以 Yaml 格式获取响应,或者像这样调用:http://appurl/geeks.json 以 Json 格式获取响应。

就是这样!这就是我关于构建 .NET Core 自定义格式化程序的所有知识。如果您愿意,可以在网络上找到其他社区成员提供的许多其他格式化程序,或者自己构建。我在 GitHub 仓库中提供的解决方案中添加了另外两个输出格式化程序。一个是 Pdf,另一个是 Xlsx 格式。 这是仓库的链接

历史

  • 2017 年 3 月 22 日:初版
© . All rights reserved.