using MessagePack;
using MessagePack.Resolvers;
using Microsoft.Extensions.Options;

namespace Dpz.Core.WebApi.MessagePackFormatters;

/// <summary>
///     格式化选项设置
/// </summary>
public class MessagePackFormatterMvcOptionsSetup(
    IOptions<MessagePackFormatterOptions> messagePackFormatterOptions
) : IConfigureOptions<MvcOptions>
{
    private readonly IOptions<MessagePackFormatterOptions> _messagePackFormatterOptions =
        messagePackFormatterOptions
        ?? throw new ArgumentNullException(nameof(messagePackFormatterOptions));

    /// <inheritdoc />
    /// <exception cref="T:System.InvalidOperationException">不支持的类型</exception>
    /// <exception cref="T:System.ArgumentNullException"><paramref name="options" /> is <see langword="null" /></exception>
    public void Configure(MvcOptions options)
    {
        ArgumentNullException.ThrowIfNull(options);

        var formatterOptions = _messagePackFormatterOptions.Value;
        var supportedMediaTypes = formatterOptions
            .MediaTypes.Where(t => !string.IsNullOrWhiteSpace(t))
            .ToArray();
        if (supportedMediaTypes.Length == 0)
        {
            throw new InvalidOperationException("No supported media types were specified.");
        }

        var msgpackOptions = MessagePackSerializerOptions.Standard;
        msgpackOptions = msgpackOptions.WithResolver(
            formatterOptions.FormatterResolver ?? ContractlessStandardResolver.Instance
        );
        msgpackOptions = msgpackOptions
            .WithAllowAssemblyVersionMismatch(formatterOptions.AllowAssemblyVersionMismatch)
            .WithCompression(formatterOptions.Compression)
            .WithOldSpec(formatterOptions.UseOldSpecification)
            .WithOmitAssemblyVersion(formatterOptions.OmitAssemblyVersion);

        options.InputFormatters.Add(
            new MessagePackInputFormatter(msgpackOptions, supportedMediaTypes)
        );
        options.OutputFormatters.Add(
            new MessagePackOutputFormatter(msgpackOptions, supportedMediaTypes)
        );

        foreach (var fileExtension in formatterOptions.FileExtensions)
        {
            options.FormatterMappings.SetMediaTypeMappingForFormat(
                fileExtension,
                supportedMediaTypes[0]
            );
        }
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这段代码实现了一个用于在 ASP.NET Core MVC 中注册 MessagePack 编/解码格式化器(input/output formatter)的 IConfigureOptions,负责把 MessagePack 支持集成到 MVC 的请求/响应管道中。要点说明如下:

总体职责

  • 通过依赖注入接收 IOptions,读取用户配置(支持的媒体类型、resolver、压缩、兼容性等)。
  • 构建 MessagePackSerializerOptions(MessagePack-CSharp 的选项),并基于这些选项创建并注册输入/输出格式化器到 MvcOptions 中。
  • 为指定的文件扩展名设置格式到媒体类型的映射(FormatterMappings),以便通过扩展名或 format 参数识别 MessagePack。

主要流程(按代码块)

  1. 构造器和字段检查
  • 构造函数注入 IOptions 并在为 null 时抛出 ArgumentNullException。
  1. Configure 方法入口参数检查
  • 对传入的 MvcOptions 做 ArgumentNullException.ThrowIfNull 检查。
  1. 获取并验证支持的媒体类型
  • 从 messagePackFormatterOptions.Value.MediaTypes 过滤空白字符串,得到 supportedMediaTypes。
  • 如果 supportedMediaTypes 为空,抛出 InvalidOperationException("No supported media types were specified."),说明配置中必须至少指定一个媒体类型(例如 "application/x-msgpack")。
  1. 构建 MessagePackSerializerOptions
  • 以 MessagePackSerializerOptions.Standard 为起点。
  • 使用 formatterOptions.FormatterResolver(若为 null 则默认 ContractlessStandardResolver.Instance)设置解析器。
  • 依次调用 WithAllowAssemblyVersionMismatch、WithCompression、WithOldSpec、WithOmitAssemblyVersion 来从配置应用对应的行为(这些选项影响序列化时的程序集版本兼容、是否启用压缩、是否使用旧版规范、是否省略程序集版本号等)。
  1. 注册输入/输出格式化器
  • 将新的 MessagePackInputFormatter 和 MessagePackOutputFormatter(使用构建好的 msgpackOptions 和 supportedMediaTypes)分别添加到 options.InputFormatters 和 options.OutputFormatters。这样 MVC 能够处理 Content-Type 为指定媒体类型的请求体并以该格式返回响应。
  1. 文件扩展名到媒体类型的映射
  • 遍历 formatterOptions.FileExtensions,将每个扩展名映射到 supportedMediaTypes[0](首选媒体类型),通过 options.FormatterMappings.SetMediaTypeMappingForFormat。这样例如访问 /api/xxx.format(或基于扩展名的路由策略)时可以推断使用 MessagePack。

异常与边界

  • 构造时若注入的 IOptions 为 null 抛 ArgumentNullException。
  • Configure 时若 options 为 null 抛 ArgumentNullException。
  • 若未配置任何有效媒体类型抛 InvalidOperationException。

使用情境(简述)

  • 在 Startup/Program 中注册 MessagePackFormatterOptions 并把该 IConfigureOptions 实例加入 DI(或通过扩展方法调用将其注册),从而在整个应用中启用 MessagePack 作为请求/响应格式之一。常见的 media type 如 "application/x-msgpack" 或 "application/msgpack"。

补充说明

  • ContractlessStandardResolver 是 MessagePack-CSharp 提供的一种解析器,常用于无需显式 [MessagePackObject]/键名映射的“合同无关”序列化。
  • MessagePackInputFormatter/MessagePackOutputFormatter 在代码中是格式化器实现(此处未显示),它们负责把 HTTP 请求体反序列化为控制器参数,或把返回值序列化为 MessagePack 二进制响应。

如果你想,我可以再帮你:

  • 展示 MessagePackFormatterOptions 的可能定义与默认值示例;
  • 示范如何在 Program.cs/Startup.cs 中注册此配置类并启用 MessagePack。
评论加载中...