OneSchema.AspNetCore
0.4.0
See the version list below for details.
dotnet add package OneSchema.AspNetCore --version 0.4.0
NuGet\Install-Package OneSchema.AspNetCore -Version 0.4.0
<PackageReference Include="OneSchema.AspNetCore" Version="0.4.0" />
<PackageVersion Include="OneSchema.AspNetCore" Version="0.4.0" />
<PackageReference Include="OneSchema.AspNetCore" />
paket add OneSchema.AspNetCore --version 0.4.0
#r "nuget: OneSchema.AspNetCore, 0.4.0"
#:package OneSchema.AspNetCore@0.4.0
#addin nuget:?package=OneSchema.AspNetCore&version=0.4.0
#tool nuget:?package=OneSchema.AspNetCore&version=0.4.0
OneSchema.AspNetCore
ASP.NET Core integration for OneSchema validation webhooks. Provides minimal-API endpoint mapping, strongly-typed row models, optional JWT request validation, helpers for batch lookups against your data store, and an API client for retrieving imported rows.
Requires .NET 10+
Install
dotnet add package OneSchema.AspNetCore
Quick Start
The minimal setup is outlined below:
using OneSchema.AspNetCore.Authentication;
using OneSchema.AspNetCore.Validation;
var builder = WebApplication.CreateBuilder(args);
// 1. Register your handlers
builder.Services.AddValidationHookHandler<ContactValidationHandler, ContactRow>();
builder.Services.AddValidationHookHandler<ProductUniquenessHandler, ProductRow>();
// 2. (Optional) Configure JWT validation
builder.Services.AddOneSchemaJwtValidation(opts =>
{
opts.ClientId = builder.Configuration["OneSchema:ClientId"]!;
opts.ClientSecret = builder.Configuration["OneSchema:ClientSecret"]!;
});
var app = builder.Build();
// 3. Map endpoints (one per handler)
app.MapValidationHook<ContactValidationHandler, ContactRow>("/webhooks/validate-contacts")
.RequireOneSchemaJwtValidation();
app.MapValidationHook<ProductUniquenessHandler, ProductRow>("/webhooks/validate-products");
// 4. Generate a JWT to send to OneSchema (optional, required if using JWT validation).
app.MapGet("/validation/jwt", (OneSchemaJwtContext jwtContext, HttpContext httpContext) =>
{
var userId = httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier)!;
var additionalClaims = new Dictionary<string, object>()
{ { "org-id", httpContext.User.FindFirstValue("org-id")! } };
var jwt = jwtContext.GenerateEmbedToken(userId, additionalClaims);
return jwt;
});
app.Run();
Each handler is bound to its own URL. Configure that URL in the OneSchema dashboard as a validation hook for the matching template.
Handlers
Handlers come in both dynamic and strongly typed flavors: BatchValidationHookHandler
exposes each row's values as a dictionary, while BatchValidationHookHandler<TRow> maps
them directly to the template columns. You'll receive every row in the webhook batch and a
ValidationResultBuilder to attach errors/warnings against specific rows.
public class ContactValidationHandler : BatchValidationHookHandler<ContactRow>
{
protected override Task ValidateAsync(
ValidationHookRequest<ContactRow> request,
ValidationResultBuilder results,
CancellationToken cancellationToken)
{
foreach (var row in request.Rows)
{
if (string.IsNullOrWhiteSpace(row.Values.Email))
results.ForRow(row).Error("email", "Email is required.");
else if (!row.Values.Email.Contains('@'))
results.ForRow(row).Warning("email", "Email appears invalid.")
.WithSuggestion("user@example.com");
}
return Task.CompletedTask;
}
}
Because the whole batch is available, checks that look across rows or query an external system (such as a DB) need no extra machinery:
public class ProductUniquenessHandler : BatchValidationHookHandler<ProductRow>
{
protected override Task ValidateAsync(
ValidationHookRequest<ProductRow> request,
ValidationResultBuilder results,
CancellationToken cancellationToken)
{
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var row in request.Rows)
{
if (string.IsNullOrWhiteSpace(row.Values.Sku))
{
results.ForRow(row).Error("sku", "SKU is required.");
continue;
}
if (!seen.Add(row.Values.Sku))
results.ForRow(row).Error("sku", "Duplicate SKU.");
}
return Task.CompletedTask;
}
}
Defining a Row Model
To define a strongly typed model, map your OneSchema template columns to a POCO using JsonPropertyName:
public class ContactRow
{
[JsonPropertyName("first_name")] public string? FirstName { get; set; }
[JsonPropertyName("last_name")] public string? LastName { get; set; }
[JsonPropertyName("email")] public string? Email { get; set; }
[JsonPropertyName("phone")] public string? Phone { get; set; }
}
Database Lookups - RowIndex helper
For bulk checks involving database queries, use IndexBy to build a key → rows
index, issue a single batch query, then pass the matching values back to ErrorForMatches (or WarningForMatches).
public class ContactEmailExistsHandler : BatchValidationHookHandler<ContactRow>
{
protected override async Task ValidateAsync(
ValidationHookRequest<ContactRow> request,
ValidationResultBuilder results,
CancellationToken cancellationToken)
{
var byEmail = request.Rows
.Where(r => !string.IsNullOrWhiteSpace(r.Values.Email))
.IndexBy(v => v.Email!, StringComparer.OrdinalIgnoreCase);
var taken = await db.Users
.Where(u => byEmail.Keys.Contains(u.Email))
.Select(u => u.Email)
.ToListAsync(cancellationToken);
byEmail.ErrorForMatches(
taken,
results,
column: "email",
messageFactory: email => $"'{email}' is already registered.");
}
}
JWT Validation
OneSchema signs webhook requests with an embed_user_jwt field in the request body.
If present, the JWT claims are available on the webhook request via context.Request.Identity.
To enforce JWT validation:
- Register your configuration with
AddOneSchemaJwtValidation. - Chain
.RequireOneSchemaJwtValidation()on each endpoint that should enforce it.
builder.Services.AddOneSchemaJwtValidation(opts =>
{
opts.ClientId = builder.Configuration["OneSchema:ClientId"]!;
opts.ClientSecret = builder.Configuration["OneSchema:ClientSecret"]!;
});
app.MapValidationHook<ContactValidationHandler, ContactRow>("/webhooks/validate-contacts")
.RequireOneSchemaJwtValidation();
- Generate a JWT to use in your frontend via
OneSchemaJwtContext.
app.MapGet("/validation/jwt", (OneSchemaJwtContext ctx, HttpContext httpContext) =>
{
var userId = httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier)!;
var additionalClaims = new Dictionary<string, object>()
{ { "org-id", httpContext.User.FindFirstValue("org-id")! } };
return ctx.GenerateEmbedToken(userId, additionalClaims);
});
Endpoints without RequireOneSchemaJwtValidation() accept unsigned requests.
Invalid or missing tokens produce 401 Unauthorized.
OneSchema API Client
The library includes a typed client for the OneSchema REST API, currently wrapping the Get Imported Rows endpoint. Register it with your API key from the OneSchema dashboard:
using OneSchema.AspNetCore.Client;
builder.Services.AddOneSchemaApiClient(opts =>
{
opts.ApiKey = builder.Configuration["OneSchema:ApiKey"]!;
// Defaults to the US region; EU and Canada are also available:
// opts.BaseAddress = OneSchemaApiOptions.EuropeBaseAddress;
});
Inject IOneSchemaApiClient to fetch imported rows for an embed session. Records can be
mapped to the same POCO row models used by validation hooks, or accessed as dictionaries:
app.MapGet("/imports/{embedId:int}/rows", async (int embedId, IOneSchemaApiClient client) =>
{
// Strongly typed page fetch
var page = await client.GetImportedRowsAsync<ContactRow>(embedId, startRow: 0, count: 100);
Console.WriteLine($"{page.Count} rows from {page.SheetMetadata.OriginalFileName}");
// Or untyped, one dictionary per record keyed by template column key
var untyped = await client.GetImportedRowsAsync(embedId);
return page.Records;
});
For larger imports, the streaming variants page through all rows automatically:
await foreach (var contact in client.StreamImportedRowsAsync<ContactRow>(embedId, pageSize: 1000))
{
// process each row
}
Non-success responses throw HttpRequestException with the StatusCode populated.
AddOneSchemaApiClient returns an IHttpClientBuilder, so resilience or custom handlers
can be chained, e.g. .AddStandardResilienceHandler().
Webhook Submission
The ExportWebhookRequest type is included for convenience for handling webhook exports, but no specific abstraction is provided for working with this type. To pull validated data via the API instead, use the OneSchema API Client.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.IdentityModel.JsonWebTokens (>= 8.22.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.