Pixata.AspNetCore
2.3.0
See the version list below for details.
dotnet add package Pixata.AspNetCore --version 2.3.0
NuGet\Install-Package Pixata.AspNetCore -Version 2.3.0
<PackageReference Include="Pixata.AspNetCore" Version="2.3.0" />
<PackageVersion Include="Pixata.AspNetCore" Version="2.3.0" />
<PackageReference Include="Pixata.AspNetCore" />
paket add Pixata.AspNetCore --version 2.3.0
#r "nuget: Pixata.AspNetCore, 2.3.0"
#:package Pixata.AspNetCore@2.3.0
#addin nuget:?package=Pixata.AspNetCore&version=2.3.0
#tool nuget:?package=Pixata.AspNetCore&version=2.3.0
Pixata.AspNetCore 

Server-side helpers for ASP.NET Core apps - entity auditing, payload encryption, request logging, endpoint validation, document generation, receiving file uploads and route dumping.
A Nuget package is available for this project.
Version 2.3.0 adds
modelBuilder.ConfigureAuditing(), which keeps the audit table's two JSON columns unlimited when your context gives strings a default maximum length inConfigureConventions. Without it, they picked up that length, and on SQL Server, saving an entity with a longer snapshot threw "String or binary data would be truncated". Call it fromOnModelCreating, as described in step 3 of setting up auditing. This version also depends on Pixata.Extensions 2.22.0, which has the grid types thatToGridResultAsync()uses. The Pixata.Extensions 2.21.0 that earlier versions depended on was published before those types were added.
Version 2.2.0 makes auditing work with an
IDbContextFactory<TContext>, as Microsoft recommend for Blazor Server. The interceptor now keeps each context's save separate, so the contexts that share it (as all the ones a factory creates in a circuit do) no longer mix up each other's audit rows or transactions, and the audit services read through the factory rather than needing a scoped context. The audit viewer also lists entity IDs in numerical order (1, 2, 10 rather than 1, 10, 2) when they're whole numbers. See using a DbContext factory.
Version 2.1.1 lists Pixata.AspNetCore.Pdf.Playwright alongside Pixata.AspNetCore.Pdf.Telerik in the message
DocumentTemplateHelperthrows when no PDF converter is registered, and in theRegisterPdfConverterobsolete message. No other changes.
Breaking changes in v2.0.0
- No PDF engine. This package no longer references wkhtmltopdf (which was archived in 2023, and is unsafe with user-supplied HTML), so apps that don't generate PDFs no longer deploy its native binary.
DocumentTemplateHelpernow converts through aPdfConverterInterface, which comes from a separate package. Add Pixata.AspNetCore.Pdf.Telerik and callAddPixataTelerikPdf(), or, if you need to keep wkhtmltopdf for now, add the obsolete Pixata.AspNetCore.Pdf.WkHtmlToPdf and callAddPixataWkHtmlToPdf(). The wkhtmltopdfIConverteris no longer registered, andRegisterPdfConverteris obsolete and does nothing. See generating documents and PDFs. DocumentTemplateHelperno longer needs a request. Set the newDocumentBaseUrloption to generate documents from interactive Blazor Server components or background services. Without it, the helper uses the current request as before, and throws a clearInvalidOperationExceptionwhen there isn't one, rather than aNullReferenceException. Its constructor has changed, which only matters if you construct it yourself.- Auditing.
AddAuditing<TContext>()now takes anAction<AuditingOptions>(which inherits fromAuditRetentionOptions, so existing lambdas still compile), andAuditingInterceptor's constructor takes the options and anIServiceProvider. SynchronousSaveChanges()calls are now audited, users in interactive Blazor Server pages are now recorded rather than"System", and added entities are saved in a transaction with their audit rows. See auditing entities.
Important
Everything in this package is designed to be used server-side, and it references EF Core and other server-only libraries. Reference it from a server-side project. If you add it to a WASM project, you will get errors.
The client-side halves of the features that have one live in Pixata.Blazor, and the types that both ends share live in Pixata.Extensions.
Documentation
The documentation is split over the following pages...
| Page | What's in it |
|---|---|
| Auditing entities | The EF Core interceptor that records every entity change, how to set it up, opting entities out, identifying the user, serving the trail to the viewer, and retention policies |
| Payload encryption | ECDH + AES-256-GCM encryption of request and response bodies, the server-side setup, and the .NET 10 caveats |
| Request logging middleware | Logging incoming requests to help debug API calls, with redaction of anything that looks sensitive |
| Validation endpoint filter | Running your FluentValidation validators on the server, so a WASM client can't bypass them |
| Generating documents and PDFs | DocumentTemplateHelper, which renders a Blazor component to HTML or (with a PDF converter package) to a PDF |
| Serving data to PixataGrid | ToGridResultAsync(), which applies a grid's paging, sorting and filtering to an EF Core query in the database |
| Route dumping | An endpoint that lists every route in your app |
| Receiving uploads | Saving files posted to an endpoint (eg by the Pixata.Blazor PixataUpload component), including chunked uploads, below |
Registering services
The code in this package requires certain dependencies to be registered in the DI container. In order to make this easier, there is an extension method to add them all. In Program.cs add this line...
builder.Services.AddPixataAspNetCore<ContactModel>();
...where ContactModel is any type in your project. If you are using the validation filter, then it is used here to point the framework to the assembly containing your models.
This registers everything in the package. If you only want some of it, you can say so...
builder.Services.AddPixataAspNetCore<ContactModel>(o => {
o.RegisterDocumentTemplateHelper = false;
});
The options are...
| Option | Default | What it registers |
|---|---|---|
RegisterDocumentTemplateHelper |
true |
DocumentTemplateHelper, along with the HtmlRenderer and IHttpContextAccessor that it needs |
DocumentBaseUrl |
"" |
Not a registration, but the base URL DocumentTemplateHelper passes to templates. When empty, it uses the current request, which doesn't exist in interactive Blazor Server components or background services |
RegisterValidation |
true |
Your FluentValidation validators (from the assembly containing the type you pass in) and the ValidationEndpointFilter |
RegisterPdfConverter is obsolete as of v2.0.0, and does nothing. This package no longer includes a PDF engine, see breaking changes in v2.0.0.
If you don't use the validation filter, there is a non-generic overload, which registers everything apart from the validation services...
builder.Services.AddPixataAspNetCore(o => o.DocumentBaseUrl = "https://www.example.com");
Note that AddPixataAspNetCore does not register the auditing, encryption or request logging services. Those have their own registration methods, as most apps only want some of them. See the pages listed above.
If you want to use the route dump feature then you'll also need the following...
app.MapPixataAspNetCoreApiEndpoints();
Receiving uploads
ReceiveUpload() is an extension method on HttpRequest that saves the files posted to an endpoint. It was written for the Pixata.Blazor PixataUpload component, but works with any form post. It needs no registration...
string uploadFolder = Path.Combine(builder.Environment.ContentRootPath, "Uploads");
app.MapPost(RoutesHelper.UploadSave, async (HttpRequest request) => {
UploadReceiveResult result = await request.ReceiveUpload(uploadFolder, o => {
o.AllowedExtensions = [".pdf", ".docx"];
o.MaxFileSize = 20 * 1024 * 1024;
});
return result.Success ? Results.Ok(result.Files.Select(f => f.FileName)) : Results.BadRequest(result.Error);
});
It saves each file in the files form field to the folder (creating it if need be), and...
- Sanitises the file names, so a request can't write outside the folder, and the name is safe on any server: it removes any folders, replaces characters that Windows doesn't allow (on every platform, so a name is saved the same way wherever you run), control characters and invisible formatting characters (such as the ones that reverse text, used to disguise
fdp.exeasexe.pdf), puts an underscore in front of names that Windows reserves for devices (CON,nul.txt,COM1.pdfand so on), and shortens names over 200 bytes, keeping the extension.UploadRequestExtensions.Sanitise()is public, if you want to do the same elsewhere. - Checks the extension and size again, as the browser's checks can be bypassed. A rejected file isn't saved, and
Errorsays why. If a request holds several files, they are all checked before any is saved. - Writes each file under a temporary name first, and renames it when it's complete, so nobody sees a half-written file, and two requests saving files with the same name don't trip over each other (with
Overwriteoff, each gets its own name, even if they arrive at the same moment). - Puts chunked uploads back together. If the request has a
chunkMetadatafield (the JSON thatPixataUploadsends with each chunk), the chunk is written to a.partfile at its offset, and the file gets its real name when the last chunk arrives. A chunk sent twice overwrites itself rather than being added twice.Filesis empty until the last chunk, andCompleteis true once the file is saved.
Return a 2xx response when Success is true (for every chunk, not just the last), and a 400 otherwise, so the component shows the file as uploaded or failed.
The options are...
| Option | Default | Description |
|---|---|---|
SaveField |
files |
The form field that holds the file (or chunk) |
MetadataField |
chunkMetadata |
The form field that holds a chunk's JSON metadata |
AllowedExtensions |
empty | The extensions accepted (the dot is optional, and case is ignored). Empty accepts any |
MaxFileSize, MinFileSize |
null | The largest and smallest files accepted, in bytes. Null means no limit, other than the server's limit on request size, which doesn't limit a chunked upload, as each chunk is a separate request. So set MaxFileSize if you accept chunked uploads |
Overwrite |
true |
When false, a file with the same name as one already there is saved as eg report (1).pdf |
FileName |
null | A Func<string, string> that chooses the name to save under, given the uploaded name (eg to add a GUID). Its result is sanitised too |
The result has Success, Complete, Error, Chunk (the chunk's metadata, for a chunked upload) and Files, each of which is a ReceivedFile with the FileName it was saved under, the OriginalFileName, the full Path, the Size and the ContentType. If a file couldn't be written (eg the disk is full), Error just says so, as it's usually sent back to the browser, and Exception holds the exception for you to log.
To delete files when the user removes them, point the component's RemoveUrl at an endpoint that calls RemoveUploads(). It deletes the files named in the files field from the folder (again, sanitising the names), and returns the names it deleted...
app.MapPost(RoutesHelper.UploadRemove, async (HttpRequest request) =>
Results.Ok(await request.RemoveUploads(uploadFolder)));
The component sends the name the file had on the user's machine, so RemoveUploads() only suits a folder where files are saved under that name. If you set FileName, or turn Overwrite off, the file may have been saved under another name (and the one sent may belong to another upload), so keep track of the names yourself (eg send the saved name back from the save endpoint, and look it up in the remove endpoint). Bear in mind too that anyone who can post to the remove endpoint can delete any file in the folder whose name they can guess, so protect it, or use a folder per user.
A chunked upload that is cancelled, or abandoned when the user closes the page, leaves a .part file behind. Call UploadRequestExtensions.DeleteAbandonedChunks(uploadFolder, TimeSpan.FromHours(1)) now and then (eg from a background service) to delete the ones that haven't been written to for a while.
A few things to bear in mind...
- Don't save uploads under
wwwroot, unless you want anyone to be able to download them. - Your endpoint is a normal endpoint, so protect it with
RequireAuthorization()as you would any other. The browser sends its cookies with the upload. - Kestrel limits a request to about 30 MB by default. Either upload in chunks (which keeps every request small), or raise the limit for the endpoint with
[RequestSizeLimit]or.WithMetadata(new RequestSizeLimitAttribute(...)). ReceiveUpload()reads the form itself, so the endpoint doesn't bind any form parameters, and isn't subject to ASP.NET Core's antiforgery check. If your endpoint also bindsIFormFileor[FromForm]parameters and you useapp.UseAntiforgery(), either send the antiforgery token as a header fromOnUpload, or call.DisableAntiforgery()on the endpoint.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- FluentValidation (>= 12.1.1)
- FluentValidation.DependencyInjectionExtensions (>= 12.1.1)
- Microsoft.EntityFrameworkCore (>= 10.0.0)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.0)
- Pixata.Extensions (>= 2.22.0)
-
net8.0
- FluentValidation (>= 12.1.1)
- FluentValidation.DependencyInjectionExtensions (>= 12.1.1)
- Microsoft.EntityFrameworkCore (>= 8.0.22)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.22)
- Pixata.Extensions (>= 2.22.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Pixata.AspNetCore:
| Package | Downloads |
|---|---|
|
Pixata.AspNetCore.Pdf.Telerik
Telerik Document Processing PDF converter for Pixata.AspNetCore's DocumentTemplateHelper. Needs a Telerik licence. Pixata.AspNetCore.Pdf.Playwright is free, and gives much better results, so is recommended instead |
|
|
Pixata.AspNetCore.Pdf.WkHtmlToPdf
Obsolete wkhtmltopdf PDF converter for Pixata.AspNetCore's DocumentTemplateHelper. Use Pixata.AspNetCore.Pdf.Playwright instead |
|
|
Pixata.AspNetCore.Pdf.Playwright
The recommended PDF converter for Pixata.AspNetCore's DocumentTemplateHelper, using Playwright and Chromium. Free, and prints templates as Chrome would. Chromium is copied into the app's build and publish output, so nothing needs installing on the server |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.3.1 | 35 | 10/20/2026 |
| 2.3.0 | 75 | 10/19/2026 |
| 2.2.0 | 69 | 10/19/2026 |
| 2.1.0 | 88 | 10/15/2026 |
| 2.0.0 | 269 | 10/6/2026 |
| 1.11.0 | 381 | 9/8/2026 |
| 1.10.0 | 132 | 9/3/2026 |
| 1.9.0 | 159 | 8/25/2026 |
| 1.8.0 | 170 | 8/17/2026 |
| 1.6.0 | 182 | 6/9/2026 |
| 1.5.2 | 148 | 6/1/2026 |
| 1.5.1 | 151 | 5/31/2026 |
| 1.5.0 | 154 | 5/28/2026 |
| 1.4.0 | 166 | 5/27/2026 |
| 1.3.2 | 153 | 4/21/2026 |
| 1.3.1 | 141 | 4/21/2026 |
| 1.3.0 | 135 | 4/21/2026 |
| 1.2.0 | 180 | 3/8/2026 |
| 1.1.0 | 146 | 3/5/2026 |
| 1.0.3 | 139 | 2/23/2026 |