using MessagePack;
using Microsoft.AspNetCore.Mvc.Formatters;

namespace Dpz.Core.WebApi.MessagePackFormatters;

/// <summary>
///     MVC output formatter.
/// </summary>
public class MessagePackOutputFormatter : OutputFormatter
{
    readonly MessagePackSerializerOptions _options;

    /// <summary>
    ///     ctor
    /// </summary>
    /// <param name="options">MessagePack 格式化选项</param>
    /// <param name="mediaTypes">支持类型</param>
    /// <exception cref="T:System.ArgumentNullException">
    ///     <paramref name="options" /> or <paramref name="mediaTypes" /> is
    ///     <c>null</c>.
    /// </exception>
    /// <exception cref="T:System.ArgumentException"><paramref name="mediaTypes" /> collection is empty.</exception>
    public MessagePackOutputFormatter(
        MessagePackSerializerOptions options,
        ICollection<string> mediaTypes
    )
    {
        _options = options ?? throw new ArgumentNullException(nameof(options));
        ArgumentNullException.ThrowIfNull(mediaTypes);

        if (mediaTypes.Count == 0)
        {
            throw new ArgumentException("Media type must be specified.", nameof(mediaTypes));
        }

        foreach (var mediaType in mediaTypes)
        {
            SupportedMediaTypes.Add(mediaType);
        }
    }

    /// <inheritdoc />
    public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context)
    {
        var writer = context.HttpContext.Response.BodyWriter;
        if (context.Object == null)
        {
            new MessagePackWriter(writer).WriteNil();
        }
        else
        {
            var objectType =
                context.ObjectType is null || context.ObjectType == typeof(object)
                    ? context.Object.GetType()
                    : context.ObjectType;

            MessagePackSerializer.Serialize(
                objectType,
                writer,
                context.Object,
                _options,
                context.HttpContext.RequestAborted
            );
        }

        await writer.FlushAsync();
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个自定义的 MessagePack 输出格式化器,用于在 ASP.NET Core Web API 中支持 MessagePack 序列化格式的响应输出。让我详细解释一下这个代码:

类的作用

MessagePackOutputFormatter 继承自 OutputFormatter,是 ASP.NET Core MVC 框架中用于处理 HTTP 响应输出格式化的组件。它的主要作用是将 C# 对象序列化为 MessagePack 二进制格式并写入 HTTP 响应体。

主要组成部分

1. 字段和构造函数

readonly MessagePackSerializerOptions _options;
  • 存储 MessagePack 序列化选项,控制序列化行为

构造函数参数:

  • options: MessagePack 序列化配置选项
  • mediaTypes: 支持的媒体类型集合(如 "application/x-msgpack")

构造函数中的验证逻辑:

  • 检查参数是否为 null
  • 确保媒体类型集合不为空
  • 将所有支持的媒体类型添加到 SupportedMediaTypes 集合中

2. 核心方法 WriteResponseBodyAsync

这是格式化器的核心方法,负责将对象序列化并写入响应体:

public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context)

处理逻辑:

  1. 获取响应写入器

    var writer = context.HttpContext.Response.BodyWriter;
    
  2. 处理 null 对象

    if (context.Object == null)
    {
        new MessagePackWriter(writer).WriteNil();
    }
    

    如果要序列化的对象为 null,则写入 MessagePack 的 nil 值

  3. 处理非 null 对象

    var objectType = context.ObjectType is null || context.ObjectType == typeof(object)
        ? context.Object.GetType()
        : context.ObjectType;
    
    • 确定要序列化的对象类型
    • 如果 context.ObjectType 为 null 或者是 object 类型,则使用实际对象的运行时类型
    • 否则使用指定的类型
  4. 执行序列化

    MessagePackSerializer.Serialize(
        objectType,
        writer,
        context.Object,
        _options,
        context.HttpContext.RequestAborted
    );
    

    使用 MessagePack 序列化器将对象序列化到响应体写入器中

  5. 刷新缓冲区

    await writer.FlushAsync();
    

    确保所有数据都被写入到响应流中

使用场景

这个格式化器通常用于:

  • 需要高性能二进制序列化的 Web API
  • 客户端和服务端都支持 MessagePack 格式的应用
  • 需要减少网络传输数据量的场景

集成方式

通常在 Startup.csProgram.cs 中注册:

services.AddMvc(options =>
{
    options.OutputFormatters.Add(new MessagePackOutputFormatter(
        MessagePackSerializerOptions.Standard,
        new[] { "application/x-msgpack" }
    ));
});

这样,当客户端请求 application/x-msgpack 格式的响应时,框架就会使用这个格式化器来处理输出。

评论加载中...