AyCode.Core/AyCode.Services/Mvc/AcBinaryInputFormatter.cs

57 lines
2.3 KiB
C#

using System.IO.Pipelines;
using AyCode.Core.Serializers.Binaries;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Logging;
namespace AyCode.Services.Mvc;
/// <summary>
/// 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).
/// </summary>
public class AcBinaryInputFormatter : InputFormatter
{
/// <summary>Vendor media type. <c>application/vnd.acbinary</c> by default.</summary>
public const string DefaultMediaType = "application/vnd.acbinary";
private readonly AcBinarySerializerOptions _options;
private readonly ILogger<AcBinaryInputFormatter>? _logger;
public AcBinaryInputFormatter(AcBinarySerializerOptions? options = null, ILogger<AcBinaryInputFormatter>? logger = null)
{
_options = options ?? AcBinarySerializerOptions.Default;
_logger = logger;
SupportedMediaTypes.Add(DefaultMediaType);
}
public override async Task<InputFormatterResult> 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;
}