dotnet / dotnet/aspnetcore

Built-in OpenAPI generator emits 3.0-era `format: binary` for binary/file types in 3.1/3.2 documents

Open
#67,826 4 comments 2 reactions 1 assignee Claimed by @Youssef1313 View on GitHub
area-minimal feature-openapi
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Summary

`Microsoft.AspNetCore.OpenApi` now defaults `OpenApiOptions.OpenApiVersion` to `OpenApi3_2`, but the generator describes binary/file types using the OpenAPI **3.0-era** idiom `type: string, format: binary` — including when it produces 3.1 and 3.2 documents, where that representation is no longer part of the specification.

The same 3.0 shape is emitted verbatim regardless of the target document version (verified with the repro below on `11.0.100-preview.6`).

### Repro

`BinaryFormatRepro.csproj`:

```xml


net11.0
enable
enable



```

`Program.cs`:

```csharp
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.OpenApi;

var builder = WebApplication.CreateBuilder(args);

// Three documents that differ ONLY by target spec version.
builder.Services.AddOpenApi("v3_0", o => o.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0);
builder.Services.AddOpenApi("v3_1", o => o.OpenApiVersion = OpenApiSpecVersion.OpenApi3_1);
builder.Services.AddOpenApi("v3_2", o => o.OpenApiVersion = OpenApiSpecVersion.OpenApi3_2); // current default

var app = builder.Build();
app.MapOpenApi("/openapi/{documentName}.json");

// Binary response (file download)
app.MapGet("/download", () => Results.File(new byte[] { 1, 2, 3 }, "application/octet-stream"))
.Produces(StatusCodes.Status200OK, "application/octet-stream");

// Binary request (file upload)
app.MapPost("/upload", (IFormFile file) => Results.Ok());

app.Run();
```

Run it, then fetch the three documents:

```
curl http://localhost:5199/openapi/v3_0.json
curl http://localhost:5199/openapi/v3_1.json
curl http://localhost:5199/openapi/v3_2.json
```

### Actual behavior

The documents correctly declare different versions (`"openapi": "3.0.4"`, `"3.1.2"`, `"3.2.0"`), but the binary schema is **identical** in all three:

```jsonc
// components.schemas in v3_0.json, v3_1.json AND v3_2.json — byte-for-byte identical:
"FileContentHttpResult": { "type": "string", "format": "binary" },
"IFormFile": { "type": "string", "format": "binary" }
```

### Expected behavior

When generating a 3.1 or 3.2 document, binary data should use the JSON-Schema-2020-12–aligned representation rather than `format: binary`:

- Raw bytes: describe via the media type / `contentMediaType` (the schema can be omitted when the media type already conveys binary).
- Base64-in-JSON: `contentEncoding: base64` (this is the correct target for `byte[]`, and is distinct from raw-binary).

### Specification references

- OpenAPI 3.0 — `format: binary` describes file/binary content, `format: byte` is base64. See "String Formats" in the OpenAPI 3.0 Data Types guide: https://swagger.io/docs/specification/v3_0/data-models/data-types/
- OpenAPI 3.1.1 — §4.4.2 **Working with Binary Data**: https://spec.openapis.org/oas/v3.1.1.html#working-with-binary-data
- OpenAPI 3.1.1 — §4.4.2.1 **Migrating binary descriptions from OAS 3.0** (explicitly describes replacing `format: binary` / `format: byte` with `contentMediaType` / `contentEncoding`): https://spec.openapis.org/oas/v3.1.1.html#migrating-binary-descriptions-from-oas-3-0

Note: `format: binary` is not *invalid* under 3.1/3.2 (`format` is an open annotation), but it is the deprecated 3.0 idiom and not the representation the spec prescribes, so tooling that follows 3.1+ may not interpret it as binary.

### Scope

Affects all special-cased binary types in `src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs`:

- `IFormFile`, `IFormFileCollection`
- `Stream`, `PipeReader`
- `FileContentResult`, `FileStreamResult`
- `FileContentHttpResult`, `FileStreamHttpResult`

### Notes

- The earlier bug where `FileContentResult`/`FileContentHttpResult` mapped to an *object* `$ref` (#63172, #64561) is fixed — these types now produce a binary schema; this issue is specifically about that schema using the legacy 3.0 shape under 3.1/3.2.
- Related: #67145 (proposed `IBinaryContent` marker) would need this same version-aware mapping, so the two are worth designing together.

### .NET Version

11.0.100-preview.6.26315.102 (main)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.