Kanject.Core.Api.Annotations
1.8.0
Prefix Reserved
dotnet add package Kanject.Core.Api.Annotations --version 1.8.0
NuGet\Install-Package Kanject.Core.Api.Annotations -Version 1.8.0
<PackageReference Include="Kanject.Core.Api.Annotations" Version="1.8.0" />
<PackageVersion Include="Kanject.Core.Api.Annotations" Version="1.8.0" />
<PackageReference Include="Kanject.Core.Api.Annotations" />
paket add Kanject.Core.Api.Annotations --version 1.8.0
#r "nuget: Kanject.Core.Api.Annotations, 1.8.0"
#:package Kanject.Core.Api.Annotations@1.8.0
#addin nuget:?package=Kanject.Core.Api.Annotations&version=1.8.0
#tool nuget:?package=Kanject.Core.Api.Annotations&version=1.8.0
Kanject.Core.Api.Annotations
Kanject.Core.Api.Annotations is a Roslyn source generator plus analyzers for ASP.NET Core minimal APIs. You write endpoints as public static methods on a static class marked [ApiEndpoints]; at compile time it generates the MapGroup / MapGet / MapPost … registration code, carries your XML doc comments into OpenAPI summaries and descriptions, and writes a Markdown catalog of every route on each build.
Analyzers catch the mistakes that would otherwise make the generator skip an endpoint without telling you — a non-static class, a private method, duplicate routes — and four of them come with code fixes.
Installation
dotnet add package Kanject.Core.Api.Abstractions
dotnet add package Kanject.Core.Api.Annotations
<ItemGroup>
<PackageReference Include="Kanject.Core.Api.Abstractions" />
<PackageReference Include="Kanject.Core.Api.Annotations" PrivateAssets="all" />
</ItemGroup>
This package is not marked as a development dependency, so dotnet add package writes a plain reference without PrivateAssets or IncludeAssets. Add PrivateAssets="all" yourself so the generator and its build targets stay in your project instead of flowing to projects that reference it.
The attribute types live in Kanject.Core.Api.Abstractions (there is no separate .Attributes package). Kanject.Core.Api and Kanject.Core.ApiV2 both bring that package in, but neither references this generator — add it explicitly wherever you declare [ApiEndpoints] classes. It depends on Kanject.Core.Annotations, which NuGet restores automatically.
The analyzer assembly targets netstandard2.0. Use it in ASP.NET Core projects on .NET 8, .NET 9 or .NET 10: the attribute types come from Kanject.Core.Api.Abstractions (net8.0–net10.0), and the generated code uses minimal-API route groups.
Quick start
using Kanject.Core.Api.Abstractions.Attributes;
using Microsoft.AspNetCore.Authorization;
namespace Contoso.Orders.Endpoints;
/// <summary>Order management endpoints.</summary>
[ApiEndpoints("Orders", "api/orders")]
public static class OrderEndpoints
{
/// <summary>Lists the caller's orders.</summary>
[HttpGet]
public static async Task<IResult> ListOrders(IOrderStore store, CancellationToken ct)
=> Results.Ok(await store.ListAsync(ct));
/// <summary>Returns one order.</summary>
/// <remarks>Responds 404 when the order does not exist.</remarks>
[HttpGet("{id:guid}")]
public static async Task<IResult> GetOrder(Guid id, IOrderStore store, CancellationToken ct)
=> await store.FindAsync(id, ct) is { } order ? Results.Ok(order) : Results.NotFound();
/// <summary>Liveness probe for the orders group.</summary>
[AllowAnonymous]
[HttpGet("ping")]
public static IResult PingOrders() => Results.Ok();
}
using Contoso.Orders.Endpoints;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(/* your scheme */);
builder.Services.AddAuthorization();
builder.Services.AddScoped<IOrderStore, OrderStore>();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapKanjectEndpoints(); // every [ApiEndpoints] class in this project
app.Run();
Enable XML documentation in the project so the compiler parses your doc comments — the generator reads <summary> and <remarks> from them, and without it KANAPI011 reports every endpoint as undocumented:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
What gets generated
For each [ApiEndpoints] class, in the class's namespace (abridged):
public sealed class OrderEndpointsEndpointOptions
{
public Action<RouteHandlerBuilder>? ListOrders { get; set; }
public Action<RouteHandlerBuilder>? GetOrder { get; set; }
public Action<RouteHandlerBuilder>? PingOrders { get; set; }
}
public static class OrderEndpointsRouteRegistration
{
public static IEndpointRouteBuilder MapOrderEndpoints(this IEndpointRouteBuilder app,
Action<RouteGroupBuilder>? configureGroup = null,
Action<OrderEndpointsEndpointOptions>? configureEndpoints = null)
{
var group = app.MapGroup("api/orders")
.WithTags("Orders")
.RequireAuthorization()
.WithDescription("Order management endpoints.");
configureGroup?.Invoke(group);
// ...
var endpointGetOrder = group.MapGet("{id:guid}", OrderEndpoints.GetOrder)
.WithName(nameof(OrderEndpoints.GetOrder))
.WithSummary("Returns one order.")
.WithDescription("Responds 404 when the order does not exist.");
endpointOptions.GetOrder?.Invoke(endpointGetOrder);
// ...
return app;
}
}
Plus one KanjectEndpointRegistration.MapKanjectEndpoints() that calls every group's Map{ClassName}(). It is generated into the shortest namespace among your endpoint classes, so import that namespace where you call it.
Groups, routes and authorization
[ApiEndpoints] takes the OpenAPI tag and the route prefix positionally, or as named properties:
| Property | Default | Effect |
|---|---|---|
EndpointGroupName |
The class name | WithTags(...) on the group |
Route |
"" |
Prefix passed to MapGroup(...) |
RequireAuthorization |
true |
RequireAuthorization() on the group — register authentication and authorization, or set this to false |
AuthPolicy |
none | Group-wide policy, RequireAuthorization("<policy>") — applied only while RequireAuthorization is true |
Verb attributes take an optional route template relative to the group ([HttpGet("{id:guid}")]); no template maps the group root. Per method, use ASP.NET Core's own attributes from Microsoft.AspNetCore.Authorization: [AllowAnonymous] opts the endpoint out of the group requirement, and [Authorize(Policy = "...")] or [Authorize(Roles = "...")] adds a requirement. The generator mirrors them as AllowAnonymous() / RequireAuthorization(...) calls on the endpoint.
Configuring groups and endpoints
Call the per-group method instead of MapKanjectEndpoints() when a group needs extra conventions:
app.MapOrderEndpoints(
configureGroup: group => group.RequireCors("frontend"),
configureEndpoints: endpoints =>
{
endpoints.PingOrders = endpoint => endpoint.ExcludeFromDescription();
});
MapKanjectEndpoints() maps every group with no callbacks. Don't call it and a per-group Map…() for the same class, or the routes are registered twice.
Rules and gotchas
- The class must be
static(KANAPI001) and endpoint methodspublic static(KANAPI002); anything else is skipped by the generator. - Each endpoint is named after its method (
WithName(nameof(...))). ASP.NET Core requires endpoint names to be unique, and OpenAPI tools use them as operation IDs, so keep method names unique across all[ApiEndpoints]classes —ListOrders, notList. - Only the first verb attribute on a method is used (KANAPI013).
- Return
IResultor a concrete type; MVC'sIActionResultbelongs to controllers (KANAPI007), andvoid/Taskgive an implicit empty 200 (KANAPI008). - The Kanject verb attributes share their names with
Microsoft.AspNetCore.Mvc.HttpGetAttributeand friends. If an endpoint file also importsMicrosoft.AspNetCore.Mvc(for[FromBody],[FromServices], …),[HttpGet]becomes ambiguous — alias or fully qualify one of them.
API schema document
After each build the package's MSBuild targets write <AssemblyName>.apischema.md next to the project file: a Markdown catalog with an overview, endpoint groups, a route map, an authorization matrix, OpenAPI documentation coverage and registration snippets. It is useful as reference documentation and as context for AI coding assistants.
| MSBuild property | Default | Effect |
|---|---|---|
GenerateApiSchema |
true |
Set false to skip writing the file |
ApiSchemaOutputDir |
Project directory | Relative (to the project) or absolute output directory |
To extract the document, the targets set EmitCompilerGeneratedFiles to true when GenerateApiSchema is on and you haven't set it yourself, so generated sources are also written under obj/. The generator additionally embeds the same Markdown in your compiled assembly as [assembly: AssemblyMetadata("ApiSchema", ...)]; GenerateApiSchema=false does not remove that attribute.
Diagnostics
| ID | Severity | What it means |
|---|---|---|
| KANAPI001 | Error | [ApiEndpoints] class is not static, so the generator skips it (code fix: make static). |
| KANAPI002 | Error | A method with a verb attribute is not public static, so it is skipped (code fix: make public static). |
| KANAPI003 | Warning | A Kanject verb attribute is on a method whose class lacks [ApiEndpoints] and has no effect (code fix: add [ApiEndpoints]). |
| KANAPI004 | Warning | [ApiEndpoints] class contains no verb-annotated methods. |
| KANAPI005 | Error | Two methods in a group map the same verb and route template — a runtime route conflict. |
| KANAPI007 | Info | Endpoint returns MVC IActionResult / ActionResult; return IResult or a concrete type. |
| KANAPI008 | Warning | Endpoint returns void or bare Task; return an IResult for an explicit status code. |
| KANAPI009 | Info | [AllowAnonymous] is redundant because the group sets RequireAuthorization = false. |
| KANAPI010 | Info | [Authorize] without a policy or roles is redundant because the group already requires authorization. |
| KANAPI011 | Info | Endpoint method has no XML <summary>, so its OpenAPI summary will be empty. |
| KANAPI012 | Warning | Route template starts with /, which can produce // after the group prefix (code fix: remove it). |
| KANAPI013 | Warning | Method has more than one verb attribute; only the first is used. |
| KANJECTAPISCHEMA001 | Warning | Generating the API schema document failed; route registrations are still generated. |
Related packages
| Package | Role | Availability |
|---|---|---|
Kanject.Core.Api.Abstractions |
The [ApiEndpoints] and verb attribute types, plus response envelope helpers for minimal APIs |
nuget.org |
Kanject.Core.Api |
Controllers, exception middleware (Kanject envelope errors) and tenant middleware | nuget.org |
Kanject.Core.ApiV2 |
Controllers, exception middleware (Problem Details errors) and tenant middleware | nuget.org |
Kanject.Core.Annotations |
Shared Kanject source-generator infrastructure (dependency of this package) | nuget.org |
License
Licensed under the Kanject Code Libraries License Agreement (KCLLA); the full text ships in this package as LICENSE.md. Organizations whose trailing-twelve-month gross revenue and total funding raised are each below US$250,000 may use it at no cost under the Free Tier. At or above either threshold a commercial license is required — contact commercial@kanjectbusiness.com.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- Kanject.Core.Annotations (>= 3.15.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.8.0 | 45 | 10/5/2026 |
| 1.7.1 | 51 | 10/5/2026 |
| 1.7.0 | 57 | 10/2/2026 |
| 1.6.1 | 110 | 9/27/2026 |
| 1.6.0 | 93 | 9/27/2026 |
| 1.5.7 | 92 | 9/26/2026 |
| 1.5.6 | 134 | 9/7/2026 |
| 1.5.5 | 106 | 8/27/2026 |
| 1.5.4 | 104 | 8/22/2026 |
| 1.5.3 | 114 | 8/10/2026 |
| 1.5.2 | 111 | 8/9/2026 |
| 1.5.1 | 128 | 8/5/2026 |
| 1.5.0 | 119 | 8/5/2026 |
| 1.4.0 | 124 | 8/3/2026 |
| 1.3.5 | 125 | 7/30/2026 |
| 1.3.4 | 124 | 7/18/2026 |
| 1.3.3 | 154 | 7/13/2026 |
| 1.3.2 | 123 | 7/11/2026 |
| 1.3.1 | 125 | 7/11/2026 |
| 1.3.0 | 125 | 7/9/2026 |