using System.IO.Pipelines; using AyCode.Core.Serializers.Binaries; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.Logging; namespace AyCode.Services.Mvc; /// /// ASP.NET Core MVC InputFormatter for AcBinary wire format. Reads request body via PipeReader, /// drains into AsyncPipeReaderInput, deserializes to ModelType. Standard ProblemDetails error /// flow on failure (ModelState.AddModelError → 400 + application/problem+json). /// public class AcBinaryInputFormatter : InputFormatter { /// Vendor media type. application/vnd.acbinary by default. public const string DefaultMediaType = "application/vnd.acbinary"; private readonly AcBinarySerializerOptions _options; private readonly ILogger? _logger; public AcBinaryInputFormatter(AcBinarySerializerOptions? options = null, ILogger? logger = null) { _options = options ?? AcBinarySerializerOptions.Default; _logger = logger; SupportedMediaTypes.Add(DefaultMediaType); } public override async Task ReadRequestBodyAsync(InputFormatterContext context) { if (context is null) throw new ArgumentNullException(nameof(context)); var ct = context.HttpContext.RequestAborted; var reader = PipeReader.Create(context.HttpContext.Request.Body); try { var model = await AcBinaryDeserializer.DeserializeFromPipeReaderAsync(reader, context.ModelType, _options, ct).ConfigureAwait(false); return await InputFormatterResult.SuccessAsync(model).ConfigureAwait(false); } catch (OperationCanceledException) when (ct.IsCancellationRequested) { throw; } catch (Exception ex) { _logger?.LogWarning(ex, "AcBinary deserialization failed for type {ModelType}", context.ModelType); context.ModelState.TryAddModelError(context.ModelName ?? string.Empty, ex.Message); return await InputFormatterResult.FailureAsync().ConfigureAwait(false); } finally { await reader.CompleteAsync().ConfigureAwait(false); } } protected override bool CanReadType(Type type) => true; }