# MVC — AcBinary formatters > ⚠️ **TEMPORARILY DISABLED** — formatter sources block-commented (`/* ... */`) and `Microsoft.AspNetCore.App` FrameworkReference removed from `AyCode.Services.csproj` (downstream net10.0 Hybrid client conflict on `Microsoft.AspNetCore.Mvc` namespace). Re-enable when split into a separate NuGet package / solution. Description below documents the intended state. ASP.NET Core MVC `InputFormatter` / `OutputFormatter` pair for the AcBinary wire format. Works in controller-based MVC and Minimal API on .NET 9+. The wire payload is the raw `byte[]` produced by `AcBinarySerializer.Serialize(value, opts)` — bit-compatible with the single-shot byte[] API; no MVC-specific envelope. > **Code:** `AyCode.Services/Mvc/` (`AcBinaryInputFormatter`, `AcBinaryOutputFormatter`, `AcBinaryMvcBuilderExtensions`) > **Binary serializer:** `../../../AyCode.Core/AyCode.Core/docs/BINARY/README.md` ## Registration ```csharp // Program.cs builder.Services.AddControllers() .AddAcBinaryFormatters(opts => { opts.UseGeneratedCode = true; }); ``` `AddAcBinaryFormatters` inserts both formatters at **index 0** of `MvcOptions.InputFormatters` / `OutputFormatters` — AcBinary wins content-negotiation when the client's `Accept` header allows. ## Media Type `application/vnd.acbinary` (vendor tree, registered with the IANA pattern but not yet IANA-listed). The same media type is sent on both request (`Content-Type`) and response. Override via `SupportedMediaTypes.Add(...)` on a custom formatter instance if a project-specific type is needed. ## Request flow (InputFormatter) ``` HttpContext.Request.Body (Stream) → PipeReader.Create(Body) (PipeReader) → drain-loop on calling thread: while (true) { result = await reader.ReadAsync(ct); foreach (segment in result.Buffer) input.Feed(segment.Span); reader.AdvanceTo(result.Buffer.End); if (result.IsCompleted) break; } input.Complete(); ↑ background Task.Run feeds AcBinaryDeserializer.Deserialize(input, ModelType, opts) → ModelType instance → InputFormatterResult.Success ``` The drain-loop is **inline** in the formatter — the serializer surface ends at `AsyncPipeReaderInput`. Any I/O-specific draining (PipeReader, NamedPipe, FileStream, custom transport) is the consumer's responsibility. ## Response flow (OutputFormatter) ``` HttpContext.Response.Body (Stream) → PipeWriter.Create(Body) (PipeWriter) → AcBinarySerializer.SerializeChunked(value, ObjectType, writer, opts) (raw mode — pure AcBinary bytes, no [201][UINT16] framing) → await pipeWriter.CompleteAsync() ``` `SerializeChunked` (not `SerializeChunkedFramed`) — the wire is a single self-contained AcBinary blob, identical to `Serialize(value, opts) → byte[]`. No multiplexed framing on the HTTP body. ## Error model Deserialization failure → `ModelState.TryAddModelError(ModelName, ex.Message)` → `InputFormatterResult.Failure()`. ASP.NET pipeline emits `400 Bad Request` with `application/problem+json` (RFC 7807) — **not** an AcBinary-encoded error. The client reads the error body as JSON. `OperationCanceledException` (when `RequestAborted` is signalled) is rethrown so the pipeline aborts the response cleanly. ## Cancellation `HttpContext.RequestAborted` flows into both formatters. The InputFormatter passes it to `PipeReader.ReadAsync` and `Task.Run`; the OutputFormatter calls `cancellationToken.ThrowIfCancellationRequested()` after `CompleteAsync`. Mid-request abort releases all pooled resources via the `using` and `finally` blocks. ## What the formatter does NOT include - **No Stream-async API in the binary core** — `AcBinarySerializer` has no `SerializeAsync(Stream, T)` method. The formatter is the wrapper. (See `BINARY_TODO.md#accore-bin-t-t8k3` — parked.) - **No options thread-safety guard** — `AcBinarySerializerOptions` is currently mutable; if registered as a DI singleton with `Configure<>` it is fine because `Configure` is read-only at runtime, but raw-shared mutable instances across concurrent requests are unsafe. (See `BINARY_ISSUES.md#accore-bin-i-l8n5` and `BINARY_TODO.md#accore-bin-t-b7h4`.) - **No OpenAPI metadata helpers** — `Microsoft.AspNetCore.OpenApi` / `Swashbuckle.AspNetCore` pick up `SupportedMediaTypes` automatically; no extra integration needed. ## Future work The formatter currently lives in `AyCode.Services` (alongside the SignalR transport). The intent is to extract it into its own NuGet package — `AyCode.AspNetCore.Mvc.Formatters.AcBinary` — when the binary serializer is moved to a dedicated solution. No code change required at extraction time; only project-file split.