Nine projects — three project types across three target frameworks — showing where the Retreever library fits and where it does not.
Retreever Backend/
├─ Retreever/ the library (multi-targets net8.0;net9.0;net10.0)
├─ dotnet8/ samples on .NET 8 ports 5800-5802
├─ dotnet9/ samples on .NET 9 ports 5900-5902
└─ dotnet10/ samples on .NET 10 ports 6000-6002
Each folder is self-contained and identical apart from its target framework, so diffing
dotnet8/ against dotnet10/ shows exactly what changes between runtimes — which is almost
nothing, and that is the point.
cd dotnet10 && dotnet build Retreever.Samples.slnx
cd WebApi && dotnet run # → http://localhost:6000/retreever| Type | Retreever works? | Use it when |
|---|---|---|
| WebApi | Yes — the primary case | Any controller-based JSON API. Start here. |
| MVC | Yes, with one caveat | Razor site and JSON API in one process. |
| Lambda | Yes | The API is already serverless. |
The full explanation of each — including the traps — is in the per-folder READMEs: dotnet8 · dotnet9 · dotnet10.
Retreever documents MVC controllers only. Discovery runs through MVC's
ApplicationPartManager, and eligibility requires ControllerBase, [ApiController],
[Route], or an HTTP-method attribute. Minimal API endpoints (app.MapGet(...)) are never
documented. Every sample here uses controllers for that reason.
In MVC, annotate your view controllers. Eligibility never inspects the return type, so an
action returning ViewResult is documented exactly like one returning ActionResult<T> —
your HTML pages would show up as JSON endpoints. [RetreeverSkip] on the view controller is
the fix.
For Lambda, prefer .NET 8. It is the only one of the three that is an AWS-managed runtime, so it deploys as a plain zip. .NET 9 and .NET 10 each need a container image, because managed runtimes track LTS releases only.
Request and response shapes, all in the WebApi project:
- Verbs — GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
- Parameters — route, query, header, with DataAnnotations constraints
- JSON bodies, including generic page wrappers and partial-update models
- Multipart — single file, many files, and fields-plus-file in one request
- Forms —
application/x-www-form-urlencoded, and an MVC antiforgery form post - Files — PDF and
application/octet-streamdownloads - Sheets — real
.xlsximport and export via ClosedXML, plus CSV; export round-trips back through import - Enums, maps and nesting — the Billing group: real
enums (as query params, body and response fields),Dictionarymaps, a three-level nested object graph, and thedecimal/DateOnly/TimeOnly/TimeSpan/Uriscalar family - Errors —
[ApiError]with status, code and body type, at class and method level - Hiding endpoints —
[RetreeverSkip]andRetreever:Docs:Skippath patterns - Test environment variables — including one derived from a live login call
Nothing in this repository is a real credential. The Retreever:Auth block in
WebApi/appsettings.json is commented out and its values are placeholders, and the login
sample returns a fabricated token. Keep it that way — appsettings.json is committed
because it is the sample.
Real values go in one of two places, neither of which is tracked:
A local overlay. Copy appsettings.Development.example.json
next to the project's appsettings.json as appsettings.Development.json and fill it in.
Every launchSettings.json profile sets ASPNETCORE_ENVIRONMENT=Development, so the
default host builder layers it on top with no code change. .gitignore excludes
appsettings.Development.json and the .Staging/.Production/.Local variants.
Environment variables, which is what you want in CI and on Lambda. __ is the
separator for : in a config key:
Retreever__Auth__Username=admin
Retreever__Auth__Password=…
Retreever__Auth__Secret=….gitignore also covers .env files, *.pfx/*.pem/*.key, AWS credentials, and the
bin/Release/lambda-publish/ staging directory that dotnet lambda writes — a published
Lambda bundle carries whatever was in the environment when it was built.
If a secret does reach a commit, rotate it first. Rewriting the history does not un-leak a value that was pushed; only rotation does.
Every claim above was run, not assumed:
- all three solutions build with 0 warnings, 0 errors
- all three WebApi samples serve the Studio UI, the embedded JS/PNG assets, and
/retreever/doc— 29 endpoints across 6 groups, byte-identical across the three frameworks (bar the version string in the doc description) - multipart upload, urlencoded form post, PDF and
.xlsxdownload, and the export→import spreadsheet round-trip all exercised over HTTP - the MVC antiforgery form post persists and is visible from both the rendered page and the API
Found while building these samples. None block the samples; all are worth a look before release. Details in dotnet10/README.md.
- Route constraints leak into documented paths —
[HttpGet("{id:guid}")]documents as/api/v1/products/{id:guid}while the parameter list correctly saysid. The path copied out of the Studio is not a usable URI template. - Collection routes gain a trailing slash —
/api/v1/products/for a route that is actually/api/v1/products. - Bodyless POSTs claim a JSON body —
POST /api/v1/auth/logouttakes no parameters yet reportsconsumes: application/jsonwith anullrequest. AddRetreeverneedsIConfigurationin the container — it takes one as an argument, but auth options bind viaOptionsBuilder.BindConfiguration(), which resolves from DI. Invisible in a web host, throws in any other host.- Enum members are not enumerated — an
enumdocuments with kindenum, but the schema records only the kind, not the allowed values, so the type template shows"VALUE"instead of the member set. Surfaced by the Billing group; a[FieldInfo(Example = …)]is the current workaround. - Minimal APIs are not documented (see above).