Skip to content

Commit 00a2078

Browse files
authored
Merge pull request #13 from sheepla/devin/1777641381-docs-simplification
docs: simplify README and create Astro Starlight documentation pages
2 parents 966df77 + ba792f4 commit 00a2078

20 files changed

Lines changed: 1025 additions & 845 deletions

README.md

Lines changed: 16 additions & 812 deletions
Large diffs are not rendered by default.

docs/astro.config.mjs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,37 @@ export default defineConfig({
1818
},
1919
],
2020
sidebar: [
21-
//
21+
{
22+
label: "Getting Started",
23+
items: [
24+
{ label: "Installation", slug: "getting-started/installation" },
25+
{ label: "Quick Start", slug: "getting-started/quick-start" },
26+
],
27+
},
28+
{
29+
label: "Guides",
30+
items: [
31+
{ label: "Defining Endpoints", slug: "guides/defining-endpoints" },
32+
{ label: "Request Binding", slug: "guides/request-binding" },
33+
{ label: "Response Types", slug: "guides/response-types" },
34+
{ label: "Validation", slug: "guides/validation" },
35+
{ label: "Authorization", slug: "guides/authorization" },
36+
{ label: "Error Responses", slug: "guides/error-responses" },
37+
{ label: "Endpoint Groups", slug: "guides/endpoint-groups" },
38+
{ label: "Filters", slug: "guides/filters" },
39+
{ label: "HTTP Context", slug: "guides/http-context" },
40+
],
41+
},
42+
{
43+
label: "Extensions",
44+
items: [
45+
{ label: "CSV Helper", slug: "extensions/csv-helper" },
46+
{ label: "CSV Import", slug: "extensions/csv-helper/csv-import" },
47+
{ label: "CSV Export", slug: "extensions/csv-helper/csv-export" },
48+
{ label: "Row Validation", slug: "extensions/csv-helper/row-validation" },
49+
{ label: "Class Map", slug: "extensions/csv-helper/class-map" },
50+
],
51+
},
2252
],
2353
}),
2454
react(),
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
title: "Class Map"
3+
description: "Customize CSV column-to-property mapping using CsvHelper ClassMap."
4+
---
5+
6+
Override `CreateClassMap()` on the derived request type to supply a CsvHelper `ClassMap` instead of relying on attributes:
7+
8+
## Import
9+
10+
```csharp
11+
public sealed class ImportUsersRequest : CsvRequest<UserImportRow>
12+
{
13+
public static ValueTask<ImportUsersRequest> BindAsync(HttpContext context)
14+
=> BindCsvAsync<ImportUsersRequest>(context);
15+
16+
protected override ClassMap CreateClassMap() => new UserImportRowMap();
17+
}
18+
19+
public sealed class UserImportRowMap : ClassMap<UserImportRow>
20+
{
21+
public UserImportRowMap()
22+
{
23+
Map(r => r.Name).Name("full_name");
24+
Map(r => r.Email).Name("email_address");
25+
}
26+
}
27+
```
28+
29+
## Export
30+
31+
The same `CreateClassMap()` override is available on `CsvResponse<TRow>` via the `classMap` parameter of `CsvResponse.From`:
32+
33+
```csharp
34+
CsvResponse.From(rows, classMap: new UserExportRowMap(), fileName: "users.csv")
35+
```
36+
37+
For the full CSV import example, see [CSV Import](/extensions/csv-helper/csv-import/). For export, see [CSV Export](/extensions/csv-helper/csv-export/).
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
title: "CSV Export"
3+
description: "Stream typed rows as a CSV download using CsvResponse<TRow>."
4+
---
5+
6+
Return `CsvResponse<TRow>` directly from `HandleAsync`. It implements `IResult`, so the framework streams the rows to the response without buffering the entire dataset in memory.
7+
8+
```csharp
9+
public sealed class UserExportRow
10+
{
11+
[Name("id")] public int Id { get; init; }
12+
[Name("name")] public string Name { get; init; } = string.Empty;
13+
[Name("email")] public string Email { get; init; } = string.Empty;
14+
}
15+
16+
public sealed class ExportUsersEndpoint : IEndpoint<CsvResponse<UserExportRow>>
17+
{
18+
private readonly IUserRepository _repository;
19+
20+
public ExportUsersEndpoint(IUserRepository repository) => _repository = repository;
21+
22+
public void Configure(IEndpointConfiguration config)
23+
{
24+
config.Get("/users/export").Summary("Export users as CSV");
25+
}
26+
27+
public Task<CsvResponse<UserExportRow>> HandleAsync(CancellationToken cancel)
28+
{
29+
// IAsyncEnumerable<T> is written row-by-row without loading everything into memory.
30+
var rows = _repository.GetAllAsync(cancel);
31+
return Task.FromResult(CsvResponse.From(rows, fileName: "users.csv"));
32+
}
33+
}
34+
```
35+
36+
`CsvResponse.From` also accepts `IEnumerable<T>` for synchronous sequences.
37+
38+
For custom column mapping on export, see [Class Map](/extensions/csv-helper/class-map/).
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
title: "CSV Import"
3+
description: "Import CSV data into typed rows using CsvRequest<TRow>."
4+
---
5+
6+
Derive your request type from `CsvRequest<TRow>`. After binding, `request.Rows` contains the parsed rows as `IReadOnlyList<TRow>`.
7+
8+
Minimal API requires `BindAsync` to be declared as a non-generic static method on the concrete type, so it cannot be provided by the base class. Declare it in the derived class and delegate to `BindCsvAsync<T>`:
9+
10+
```csharp
11+
public sealed class UserImportRow
12+
{
13+
[Name("name")] public string Name { get; init; } = string.Empty;
14+
[Name("email")] public string Email { get; init; } = string.Empty;
15+
[Name("role")] public string Role { get; init; } = string.Empty;
16+
}
17+
18+
public sealed class ImportUsersRequest : CsvRequest<UserImportRow>
19+
{
20+
public static ValueTask<ImportUsersRequest> BindAsync(HttpContext context)
21+
=> BindCsvAsync<ImportUsersRequest>(context);
22+
}
23+
24+
public sealed class ImportUsersEndpoint : IEndpoint<ImportUsersRequest, Response<EmptyResponse>>
25+
{
26+
public void Configure(IEndpointConfiguration config)
27+
{
28+
config.Post("/users/import")
29+
.AddFilter<CsvBindingExceptionFilter>()
30+
.Summary("Import users from CSV");
31+
}
32+
33+
public Task<Response<EmptyResponse>> HandleAsync(
34+
ImportUsersRequest request,
35+
CancellationToken cancel)
36+
{
37+
foreach (var row in request.Rows) { /* persist row */ }
38+
return Task.FromResult(Response.NoContent);
39+
}
40+
}
41+
```
42+
43+
Both `text/csv` direct body and `multipart/form-data` file uploads are supported automatically.
44+
45+
For per-row validation, see [Row Validation](/extensions/csv-helper/row-validation/). For custom column mapping, see [Class Map](/extensions/csv-helper/class-map/).
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
title: "CSV Helper"
3+
description: "Add typed CSV import and export to your AxisEndpoints with the CsvHelper extension package."
4+
---
5+
6+
The `AxisEndpoints.Extensions.CsvHelper` package adds typed CSV import and export to AxisEndpoints endpoints, backed by [CsvHelper](https://joshclose.github.io/CsvHelper/).
7+
8+
## Installation
9+
10+
```sh
11+
dotnet add package AxisEndpoints.Extensions.CsvHelper
12+
```
13+
14+
## Setup
15+
16+
Register the extension services in `Program.cs`:
17+
18+
```csharp
19+
using AxisEndpoints.Extensions;
20+
using AxisEndpoints.Extensions.CsvHelper;
21+
22+
builder.Services.AddAxisEndpoints();
23+
builder.Services.AddAxisEndpointsCsvHelper(); // registers CsvBindingExceptionFilter
24+
```
25+
26+
## Next steps
27+
28+
- [CSV Import](/extensions/csv-helper/csv-import/) — parse incoming CSV data into typed rows
29+
- [CSV Export](/extensions/csv-helper/csv-export/) — stream typed rows as a CSV download
30+
- [Row Validation](/extensions/csv-helper/row-validation/) — validate each row with DataAnnotations
31+
- [Class Map](/extensions/csv-helper/class-map/) — customize column-to-property mapping
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: "Row Validation"
3+
description: "Validate CSV rows with DataAnnotations and surface errors as structured responses."
4+
---
5+
6+
DataAnnotations attributes on `TRow` are validated during binding, one row at a time. All errors are collected before the handler is invoked and surfaced as an RFC 9457 `ValidationProblem` response by `CsvBindingExceptionFilter`.
7+
8+
Error keys follow the pattern `"row {n}: {MemberName}"`:
9+
10+
```json
11+
{
12+
"status": 400,
13+
"errors": {
14+
"row 3: Email": ["The Email field is not a valid e-mail address."],
15+
"row 5: Name": ["The Name field is required."]
16+
}
17+
}
18+
```
19+
20+
## Registering the filter
21+
22+
Register `CsvBindingExceptionFilter` on any endpoint that accepts a `CsvRequest<TRow>` parameter:
23+
24+
```csharp
25+
config.Post("/users/import").AddFilter<CsvBindingExceptionFilter>();
26+
```
27+
28+
For general DataAnnotations validation on request types, see the [Validation](/guides/validation/) guide.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: "Installation"
3+
description: "Install AxisEndpoints and optional extension packages in your ASP.NET Core project."
4+
---
5+
6+
## Install from nuget.org
7+
8+
Add the core package to your project:
9+
10+
```sh
11+
dotnet add package AxisEndpoints
12+
```
13+
14+
For the CSV extension:
15+
16+
```sh
17+
dotnet add package AxisEndpoints.Extensions.CsvHelper
18+
```
19+
20+
## Install from local nupkg
21+
22+
If you are building from source or testing a pre-release version, you can install from a local `.nupkg` file:
23+
24+
```sh
25+
# Build the NuGet package
26+
dotnet pack src/AxisEndpoints/AxisEndpoints.csproj -o <LocalNupkgDirectory>
27+
28+
# Add it to your project
29+
dotnet add <YourProject> package AxisEndpoints --source <LocalNupkgDirectory>
30+
```
31+
32+
Once installed, proceed to the [Quick Start](/getting-started/quick-start/) guide to set up your first endpoint.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: "Quick Start"
3+
description: "Set up AxisEndpoints in your ASP.NET Core project and create your first endpoint."
4+
---
5+
6+
## Setup in Program.cs
7+
8+
Call `AddAxisEndpoints()` on the service collection and `MapAxisEndpoints()` on the application. Both methods scan the entry assembly automatically. Pass an `Assembly` argument to target a specific project.
9+
10+
```csharp
11+
using AxisEndpoints.Extensions;
12+
13+
var builder = WebApplication.CreateBuilder(args);
14+
15+
builder.Services.AddOpenApi();
16+
builder.Services.AddAxisEndpoints(); // Discovers and registers all endpoints
17+
18+
var app = builder.Build();
19+
20+
app.MapOpenApi();
21+
app.MapAxisEndpoints(); // Maps all discovered endpoints to the Minimal API pipeline
22+
23+
app.Run();
24+
```
25+
26+
## Your first endpoint
27+
28+
Here is a complete example of a POST endpoint that creates a user and returns a JSON response:
29+
30+
```csharp
31+
public class CreateUserRequest
32+
{
33+
public required string Name { get; init; }
34+
public required string Email { get; init; }
35+
}
36+
37+
public class CreateUserResponse
38+
{
39+
public required int Id { get; init; }
40+
}
41+
42+
public class CreateUserEndpoint : IEndpoint<CreateUserRequest, Response<CreateUserResponse>>
43+
{
44+
private readonly IUserRepository _repository;
45+
46+
public CreateUserEndpoint(IUserRepository repository) => _repository = repository;
47+
48+
public void Configure(IEndpointConfiguration config)
49+
{
50+
config.Post("/users")
51+
.Tags("Users")
52+
.Summary("Create a new user");
53+
}
54+
55+
public async Task<Response<CreateUserResponse>> HandleAsync(
56+
CreateUserRequest request,
57+
CancellationToken cancel)
58+
{
59+
var id = await _repository.CreateAsync(request.Name, request.Email, cancel);
60+
61+
return new Response<CreateUserResponse>
62+
{
63+
StatusCode = HttpStatusCode.Created,
64+
Headers = [("Location", $"/users/{id}")],
65+
Body = new CreateUserResponse { Id = id },
66+
};
67+
}
68+
}
69+
```
70+
71+
For more details on defining endpoints, see the [Defining Endpoints](/guides/defining-endpoints/) guide.

0 commit comments

Comments
 (0)