| name | devexpress-reports-aspnetcore |
| description | Integrate DevExpress Report Viewer and Report Designer into ASP.NET Core MVC and Razor Pages. Configure Program.cs with AddDevExpressControls, ConfigureReportingServices. Set up npm bundling (bundleconfig.json, libman.json). Implement ReportStorageWebExtension, IReportProvider. Customize viewer toolbar, tab panel, export options, parameter editors. Security: authorization, CSRF, Content Security Policy. Report caching with UseCachedReportSourceBuilder. Deploy to Linux/macOS with Skia. Troubleshoot blank viewer, 400/404/500 errors, "Unable to process binding", "Report not found", CORS, version mismatches. Use when: add document viewer, end-user report designer, ASP.NET Core reporting, web farm document storage, upgrade DevExpress reporting packages.
|
| compatibility | Requires .NET 8.0+. DevExpress NuGet packages are published on NuGet.org. Node.js (LTS) required for npm bundling. DevExpress.Drawing.Skia required for Linux/macOS hosting. All DevExpress packages must share the same version. |
| metadata | {"author":"DevExpress","version":"26.1","source-commit":"4ffd3390ef53dde5c173f83c51a71d523d0c6eb1"} |
DevExpress ASP.NET Core Reporting — Viewer & Designer Integration
Integrates the DevExpress Web Document Viewer (read-only preview) and Web Report Designer (full end-user designer) into ASP.NET Core MVC and Razor Pages applications. The viewer renders reports in the browser via server-side document generation; the designer lets end-users create and save reports without developer involvement.
This skill covers UI component integration only. For creating reports programmatically (bands, controls, data binding, expressions, export), use the devexpress-reports-core skill.
When to Use This Skill
- Add a Document Viewer to an ASP.NET Core MVC or Razor Pages application
- Add an End-User Report Designer to an ASP.NET Core application
- Configure Program.cs services and middleware for reporting
- Set up npm bundling (bundleconfig.json, libman.json) for reporting assets
- Implement
ReportStorageWebExtension or IReportProvider
- Customize viewer/designer toolbar, tab panel, export options, parameter editors
- Configure security: authorization, CSRF protection, CSP nonce
- Deploy reporting to Linux or macOS (Skia renderer setup)
- Troubleshoot blank viewers, HTTP errors, or script loading failures
- Set up multi-server (web farm/garden) document storage
Prerequisites & Installation
NuGet Packages
| Package | Purpose |
|---|
DevExpress.AspNetCore.Reporting | Document Viewer + Report Designer server-side |
BuildBundlerMinifier | Bundles npm output into wwwroot/ |
Microsoft.Web.LibraryManager.Build | Copies icon fonts to wwwroot/ |
DevExpress.Drawing.Skia | Required on Linux and macOS hosts |
npm Packages (package.json)
{
"dependencies": {
"bootstrap": "^4.3.1",
"devextreme-dist": "26.1-stable",
"@devexpress/analytics-core": "26.1-stable",
"devexpress-reporting": "26.1-stable"
}
}
npm and NuGet versions must match. All DevExpress packages — both npm and NuGet — must use the same major.minor version (e.g., 26.1). A mismatch causes configurator.UseDevelopmentMode() to report version errors and the viewer to silently fail.
Mandatory Setup Order
Complete all steps in order before running the app. Missing any step causes the viewer/designer to fail silently or throw 500 errors.
Step 1 — Install NuGet packages (DevExpress.AspNetCore.Reporting, BuildBundlerMinifier, Microsoft.Web.LibraryManager.Build)
Step 2 — Install Node.js (LTS) if not already installed
Step 3 — Add package.json with npm dependencies above; right-click → Restore Packages or run npm install. npm install must complete before dotnet build — LibMan's filesystem provider requires node_modules to exist.
Step 4 — Add bundleconfig.json (see references/getting-started.md for full content). This defines CSS/JS bundles that are copied to wwwroot/ at build time.
Step 5 — Add libman.json (see references/getting-started.md) to copy icon font files to wwwroot/css/icons/.
Step 6 — Program.cs — add three lines:
using DevExpress.AspNetCore;
using DevExpress.AspNetCore.Reporting;
builder.Services.AddDevExpressControls();
builder.Services.ConfigureReportingServices(configurator => {
configurator.ConfigureWebDocumentViewer(c => c.UseCachedReportSourceBuilder());
});
app.UseDevExpressControls();
app.UseStaticFiles();
Step 7 — Add controllers (see Component Overview below)
Step 8 — _ViewImports.cshtml:
@using DevExpress.AspNetCore
Step 9 — _Layout.cshtml — add thirdparty bundle to <head>:
<link rel="stylesheet" href="~/css/thirdparty.bundle.css" />
<script src="~/js/thirdparty.bundle.js"></script>
Step 10 — View file — add reporting bundle + HTML helper (see Component Overview)
Before You Start — Ask the Developer
Before generating code, ask:
- Component: Document Viewer only, or also End-User Report Designer?
- App template: MVC (Model-View-Controller) or Razor Pages?
- Report source: Instance binding (
new MyReport()) for quickstart, or storage-backed (ReportStorageWebExtension) for load/save by name?
- Target OS: Windows, Linux, or macOS? (Skia package required for non-Windows)
- Existing project or new? If existing, check for conflicting npm packages (jQuery, Knockout, Bootstrap, DevExtreme) already in bundleconfig.json.
- Security: Is authorization required on the reporting endpoints?
Component Overview
Document Viewer (read-only)
Requires: 1 controller, viewer-specific bundles.
using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services;
public class CustomWebDocumentViewerController : WebDocumentViewerController {
public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService s)
: base(s) { }
}
@* Views/Home/Index.cshtml — viewer page *@
<link rel="stylesheet" href="~/css/viewer.part.bundle.css" />
<script src="~/js/viewer.part.bundle.js"></script>
@Html.DevExpress().WebDocumentViewer("DocumentViewer").Height("1000px").Bind(new TestReport())
End-User Report Designer
Requires: 3 controllers + ReportStorageWebExtension, designer-specific bundles.
using DevExpress.AspNetCore.Reporting.QueryBuilder;
using DevExpress.AspNetCore.Reporting.QueryBuilder.Native.Services;
using DevExpress.AspNetCore.Reporting.ReportDesigner;
using DevExpress.AspNetCore.Reporting.ReportDesigner.Native.Services;
using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services;
public class CustomWebDocumentViewerController : WebDocumentViewerController {
public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService s) : base(s) { }
}
public class CustomReportDesignerController : ReportDesignerController {
public CustomReportDesignerController(IReportDesignerMvcControllerService s) : base(s) { }
}
public class CustomQueryBuilderController : QueryBuilderController {
public CustomQueryBuilderController(IQueryBuilderMvcControllerService s) : base(s) { }
}
@* Views/Home/Index.cshtml — designer page *@
<link rel="stylesheet" href="~/css/designer.part.bundle.css" />
<link rel="stylesheet" href="~/css/ace/ace.bundle.css" />
<script src="~/js/designer.part.bundle.js"></script>
@(Html.DevExpress().ReportDesigner("reportDesigner")
.Height("1000px")
.Bind(new TestReport()))
Critical: For multi-line fluent chains, always wrap in @(...). Without it, trailing method calls render as literal text in the page output.
Critical: Do NOT load viewer.part.bundle.js on a designer page. The designer bundle already includes the viewer scripts. Loading both causes Component dx-filtereditor-plain is already registered and DxReportDesigner undefined.
Documentation & Navigation Guide
Full MVC Setup (bundleconfig.json, libman.json, Program.cs, controllers, views)
📄 references/getting-started.md
📄 references/getting-started-razor-pages.md
Report Storage and Name Resolution (ReportStorageWebExtension, IReportProvider)
📄 references/report-storage.md
When you need to: enable Save/Load/Open in the designer; serve reports by name string; use Bind("ReportName") vs. Bind(new Report()).
Viewer Customization (toolbar, tab panel, export options, parameters, client-side events)
📄 references/viewer-customization.md
Designer Customization (toolbar, toolbox, wizards, data sources, parameter editing)
📄 references/designer-customization.md
Security (authorization, CSRF, CSP nonce, token auth, multi-tenancy)
📄 references/security.md
Troubleshooting (symptom→fix table, development mode, logging, web farms)
📄 references/troubleshooting.md
Quick Start Example
See examples/quickstart-viewer.cs and examples/quickstart-designer.cs for complete, runnable projects.
Minimal Program.cs (MVC, Document Viewer):
using DevExpress.AspNetCore;
using DevExpress.AspNetCore.Reporting;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.AddDevExpressControls();
builder.Services.ConfigureReportingServices(configurator => {
if (builder.Environment.IsDevelopment())
configurator.UseDevelopmentMode();
configurator.ConfigureWebDocumentViewer(c => c.UseCachedReportSourceBuilder());
});
var app = builder.Build();
app.UseDevExpressControls();
app.UseStaticFiles();
app.UseRouting();
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
app.Run();
Key API Surface
Program.cs Registration
| Method | Namespace | Purpose |
|---|
AddDevExpressControls() | DevExpress.AspNetCore | Register core DX services |
ConfigureReportingServices(Action<ReportingConfigurationBuilder>) | DevExpress.AspNetCore.Reporting | Register viewer/designer services |
UseDevExpressControls() | DevExpress.AspNetCore | Add middleware — before UseStaticFiles |
UseCachedReportSourceBuilder() | (on WebDocumentViewerConfigurationBuilder) | Enable document caching |
UseDevelopmentMode() | (on ReportingConfigurationBuilder) | Verbose error output + version check |
Controller Routes
| Controller | Route | Required For |
|---|
WebDocumentViewerController | DXXRDV | Viewer + Designer |
ReportDesignerController | DXXRD | Designer only |
QueryBuilderController | DXXQB | Designer only |
Report Name Resolution (choose one)
| Service | Use When |
|---|
IReportProvider | Viewer-only; map name → XtraReport instance |
ReportStorageWebExtension | Designer present; full save/load/list |
IWebDocumentViewerReportResolver | Highest priority; viewer-only; no async support |
Common Patterns
Pattern 1: Serve a report by name (IReportProvider)
using DevExpress.XtraReports.Services;
public class ReportProvider : IReportProvider {
public XtraReport GetReport(string id, ReportProviderContext context) {
return id switch {
"SalesReport" => new SalesReport(),
"InvoiceReport" => new InvoiceReport(),
_ => throw new InvalidOperationException($"Report '{id}' not found.")
};
}
}
builder.Services.AddScoped<IReportProvider, ReportProvider>();
@Html.DevExpress().WebDocumentViewer("DocumentViewer").Height("1000px").Bind("SalesReport")
Pattern 2: Async export from a controller action
public async Task<IActionResult> ExportPdf(string reportName) {
var report = new SalesReport();
report.RequestParameters = false;
await report.CreateDocumentAsync();
using var stream = new MemoryStream();
report.ExportToPdf(stream);
return File(stream.ToArray(), "application/pdf", "report.pdf");
}
Pattern 3: Pass parameters to viewer at load time
@Html.DevExpress().WebDocumentViewer("DocumentViewer")
.Height("1000px")
.Bind("SalesReport?StartDate=2025-01-01&Region=North")
public XtraReport GetReport(string id, ReportProviderContext context) {
var uri = new Uri("http://x/" + id);
var qs = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query);
var report = new SalesReport();
var startDateValue = qs["StartDate"];
if (!string.IsNullOrWhiteSpace(startDateValue) &&
DateTime.TryParse(startDateValue, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out var startDate))
report.Parameters["StartDate"].Value = startDate;
report.Parameters["StartDate"].Visible = false;
return report;
}
Troubleshooting
| Symptom | Cause | Fix |
|---|
| Viewer blank, no errors in console | UseDevExpressControls() missing or after UseStaticFiles() | Move app.UseDevExpressControls() before app.UseStaticFiles() |
| "Unable to process binding" in browser | Script load order wrong — DevExtreme or Analytics not loaded before viewer init | Ensure thirdparty.bundle.js loads in layout <head>, not in @section Scripts {} |
Component dx-filtereditor-plain is already registered | Both viewer and designer bundles loaded on designer page | Remove viewer bundle from designer page — designer bundle includes viewer scripts |
DevExpress is not defined | dx.all.js missing from bundle or loaded after reporting scripts | Verify dx.all.js is first entry in designer.part.bundle.js / viewer.part.bundle.js |
Literal fluent-call text in page (e.g. .Height("...")) | Multi-line Razor helper not wrapped in @(...) | Wrap: @(Html.DevExpress().ReportDesigner(...).Height(...).Bind(...)) |
| HTTP 400 on DXXRDV | Anti-forgery token rejection | Add [IgnoreAntiforgeryToken] to viewer/designer controllers |
| HTTP 404 on DXXRDV / DXXRD | Controller missing or MapDefaultControllerRoute() absent | Add controllers; in Razor Pages apps also call endpoints.MapDefaultControllerRoute() |
| HTTP 500, no details | Server exception; development mode off | Add configurator.UseDevelopmentMode() and set "DevExpress": "Debug" in appsettings.json |
DllNotFoundException for Skia at runtime | Missing DevExpress.Drawing.Skia on Linux/macOS | dotnet add package DevExpress.Drawing.Skia |
| "Report not found: ReportName" | Designer binding by name with no storage resolving it | Use Bind(new MyReport()) for quickstart, or implement ReportStorageWebExtension.GetData("ReportName") |
FaultException compile error in storage class | WCF type used in storage; not available in ASP.NET Core | Replace FaultException with InvalidOperationException |
| npm package version ≠ NuGet version | devextreme-dist or @devexpress/analytics-core version mismatch | Align all to same major.minor (26.1). Enable UseDevelopmentMode() to confirm. |
Build error: libman.json file not found | npm install did not run before build | Run npm install first; LibMan filesystem provider requires node_modules |
ko is not defined in browser console | Knockout missing from bundle or loaded after dx.all.js | Ensure knockout-latest.js is in thirdparty.bundle.js before dx.all.js in viewer.part.bundle.js. Order: jQuery → Knockout → Bootstrap → (then) dx.all.js → analytics-core → webdocumentviewer |
DevExtreme library is included before Knockout warning in console | dx.all.js loads before Knockout | Move Knockout into thirdparty.bundle.js (first bundle, loaded in layout <head>). dx.all.js must come after Knockout — it goes in viewer.part.bundle.js (loaded per-page) |
Constraints & Rules
CRITICAL — follow in every interaction:
- Build verification: Run
dotnet build --project <path> after changes. Do not report success without seeing a clean build.
- npm before build: Run
npm install before dotnet build when libman.json uses the filesystem provider. node_modules must exist for LibMan to copy icon files.
- Namespace for ConfigureReportingServices: Always add
using DevExpress.AspNetCore.Reporting;. Without it, ConfigureReportingServices is not resolved.
- UseDevExpressControls order: Must come before
app.UseStaticFiles(). Reversing this causes the viewer to silently fail.
- Designer bundle self-containment:
designer.part.bundle.js must include dx.all.js + dx-analytics-core.min.js + dx-webdocumentviewer.min.js + dx-querybuilder.min.js + dx-reportdesigner.min.js — all in that order. Do not load a separate viewer bundle on the designer page.
- Thirdparty bundle in layout head:
thirdparty.bundle.js must be in <head> of _Layout.cshtml, not in @section Scripts {}. Reporting HTML helpers render inline JS immediately — all base scripts must already be available.
- Razor @(...) wrapper: Multi-line Html.DevExpress() fluent chains must be wrapped in
@(...).
- Version consistency: All DevExpress NuGet and npm packages must use the same major.minor version.
- Skia on non-Windows: Add
DevExpress.Drawing.Skia NuGet package for any Linux or macOS deployment.
- CreateDocumentAsync in web: Never call
report.CreateDocument() synchronously in ASP.NET Core. Always await report.CreateDocumentAsync().
- FaultException: Do not use
System.ServiceModel.FaultException in report storage implementations for ASP.NET Core. Use InvalidOperationException.
- Storage registration order: Register
ReportStorageWebExtension after AddDevExpressControls() in Program.cs.
- Knockout before dx.all.js: Knockout (
knockout-latest.js) must be loaded before dx.all.js. Put Knockout in thirdparty.bundle.js (loaded in layout <head>); put dx.all.js in viewer.part.bundle.js or designer.part.bundle.js (loaded per-page). Reversing this order causes ko is not defined or DevExtreme library is included before Knockout console errors.
- Post-build browser verification: After any change to bundles, Program.cs, or views, always verify in the browser —
dotnet build succeeding does not confirm JS runtime health. Open the viewer page, open browser DevTools, and confirm: (a) Console shows 0 JS errors, (b) Network shows HTTP 200 for all .bundle.js files including knockout-latest.js, dx.all.js, dx-analytics-core, and dx-webdocumentviewer, (c) The viewer renders without a blank or error state.
Using DevExpress Documentation MCP
If DxDocs MCP tools are available, use them to supplement this skill:
- Search:
devexpress_docs_search(technology="AspNetCore", query="your question")
- Fetch:
devexpress_docs_get_content(url="docs.devexpress.com/...")
Use MCP for: exact method signatures, event arguments, enum values, advanced scenarios not covered in references, or when generating code for features not in this skill's references. Always prefer MCP over guessing.
Useful queries:
"AddDevExpressControls UseDevExpressControls Program.cs ASP.NET Core" — startup setup
"ReportStorageWebExtension GetData SetData GetUrls" — report storage
"IReportProvider GetReport ASP.NET Core" — viewer-only provider
"CustomizeMenuActions ActionId hide toolbar" — toolbar customization
"IWebDocumentViewerAuthorizationService CanReadDocument" — user authorization
"IgnoreAntiforgeryToken reporting 400" — CSRF fix
"UseCachedReportSourceBuilder ConfigureWebDocumentViewer" — caching