Skip to main content
All docs
.NET 6.0+

Obtain a Report from a Web API Controller Endpoint

  • 7 minutes to read

This topic demonstrates how to obtain a report through HTTP requests to the DevExpress Web API Service.

The application must use the following technologies to enable such API requests:

DevExpress Reporting
Creates report definitions.
XPO | EF Core
These ORM tools enable storage for report definitions and bound data.
XAF Reports Module
Stores report definitions in a database with the help of specially designed classes: ReportDataV2 (XPO) / ReportDataV2 (EF Core).

HTTP request parameters allow you to filter or sort data before the API Controller generates the final report document.


This option of our Web API Service ships as part of the DevExpress Universal Subscription.

Report Controller Availability

The API described in this article only works if your project contains an MVC Controller that allows you to access reports – ReportController. You can use the following methods to enable this controller in your applications:

Enable the Module in the Solution Wizard

If you create your Backend Web API project with the help of the Solution Wizard, a dedicated wizard screen allows you to enable the Reports module. For additional information, see Create a Standalone Web API Application.

Add the Reports Module to a Standalone Web API or XAF Blazor Application

  1. Install the DevExpress.ExpressApp.ReportsV2.Blazor NuGet package.
  2. Register the Reports module and required services in Startup.cs. Use the Web API builder or XAF Application builder:

    File: MySolution.WebApi\Startup.cs (MySolution.Blazor.Server\Startup.cs)

    services.AddXafWebApi(builder => {
            .AddReports(options => {
    }, Configuration);
  3. Add an API controller with endpoints to download reports to your existing Blazor application. An example of this controller can be found in our demo application that ships with the XAF installation (%PUBLIC%\Documents\DevExpress Demos 23.2\Components\XAF\MainDemo.NET.EFCore\CS\MainDemo.Blazor.Server\API\Reports\ReportController.cs by default).

Report Controller API

The ReportController class includes the following methods:

public async Task<object> DownloadByKey(string key,
    [FromQuery] ExportTarget fileType = ExportTarget.Pdf,
    [FromQuery] string criteria = null) {

public async Task<object> DownloadByName(string displayName,
    [FromQuery] ExportTarget fileType = ExportTarget.Pdf,
    [FromQuery] string criteria = null) {

Basic Usage Example

This example demonstrates how to obtain a report as a PDF file. The code in this example sends a request to the Report/DownloadByName endpoint with the displayName parameter.

HttpClient httpClient = new HttpClient();

// Set up client Uri.
httpClient.BaseAddress = new Uri("https://localhost:5001/");

// Argument for the DownloadByName method.
var displayName = "Employee List Report";

// Send request for a report PDF.
var response = await httpClient.GetAsync(

// Parse the result from HttpResponseMessage.
var report = await response.Content.ReadAsStringAsync();

Configure the Request Locale

You can use the HttpRequestMessage API to configure the request locale:

var customRequest = new HttpRequestMessage(HttpMethod.Get, 

customRequest.Headers.Add("Accept-Language", "en-US");

var response = await httpClient.SendAsync(customRequest);

For additional information, refer to the following article: Accept-Language header in HttpRequestHeaders.

Specify a Filter Condition

To specify a filter condition, use a query parameter named criteria. For example, the following URL instructs the report to only include records where “FirstName” equals “Aaron”:

/api/Report/DownloadByName(ReportName)?criteria=[FirstName] = 'Aaron'


ReportController filters data on the server side.

The report itself can apply additional filter conditions to the data source. The criteria set by the report’s FilterString property take effect on the client side, after the data is loaded.

Specify Sort Order

You can use the sortProperty query parameter. For example, the following URL sorts records by FirstName in descending order:



The sort order can be overriden by the report. A report band’s SortFields property takes priority if it conflicts with the specified sort order.

Pass Report Parameters

If your report uses Parameters, you can pass their values in the query. For example, the following URL sets parameters FirstName and Position to “Mary” and “Manager”, respectively:


Tutorial Video: Create a Report Access Endpoint and Use It from a MAUI App

The video below shows how you can use our Web Service API in your applications. The instructions guide you through the following tasks:

  • Create a DevExpress Report and configure security permissions for different users.
  • Establish a Web API Service endpoint to allow report document downloads.
  • Utilize HTTP requests to display downloaded PDF documents in a MAUI app.

The full example solution is available on GitHub:

View Example: How to Create a Web API Service Backend for a .NET MAUI Application

Report Controller Customization

To use Web API Authentication, decorate the ReportController with AuthorizeAttribute.

Note that the ReportController uses a report export service: IReportExportService. This service loads a report and prepares it to be exported to the specified format.

using Microsoft.AspNetCore.Mvc;
using DevExpress.ExpressApp.ReportsV2;

public class ReportController : ControllerBase {
    private readonly IReportExportService service;
    // ...

You can modify the controller’s methods as required and even use IReportExportService to create custom endpoints.

The report export service includes helper methods that manage reports and their data sources. Note the SetupReport method that allows you to specify the following parameters before report export: a filter condition and an array of sort properties.

void SetupReport(XtraReport report, string criteria = null, 
                 SortProperty[] sortProperties = null);

Add Unit Tests for Report Controller

Follow the steps below to add unit tests for code that queries reports from the ReportController.

  1. If your solution does not have a testing project, add a new xUnit test project.
  2. Add a reference to the {SolutionName}.Blazor.Server project.
  3. Add the Microsoft.AspNetCore.Mvc.Testing package reference.
  4. Add the following test to the test project:

    using System.Net.Http.Headers;
    using System.Text;
    using DevExpress.Data.Filtering;
    using DevExpress.Xpo;
    using DevExpress.XtraPrinting;
    using MainDemo.Module.BusinessObjects;
    using Microsoft.AspNetCore.Mvc.Testing;
    using Xunit;
    public class ReportByNameUnitTests
        : IClassFixture<WebApplicationFactory<MainDemo.Blazor.Server.Startup>> {
        HttpClient httpClient;
        string ApiUrl = "/api/Report/DownloadByName";
        public ReportByNameUnitTests(WebApplicationFactory<MainDemo.Blazor.Server.Startup> webApplicationFactory) {
            httpClient = webApplicationFactory.CreateClient();
            httpClient.DefaultRequestHeaders.Add("Accept-Language", "de-DE");
        // A simple test example (no authentication)
        public async System.Threading.Tasks.Task LoadReport() {
            string url = CreateRequestUrl("Employee List Report");
            var response = await httpClient.GetAsync(url);
            string loadedReport = await response.Content.ReadAsStringAsync();
            // Validate the result
            // ...
        // An advanced test example
        public async System.Threading.Tasks.Task LoadReportWithCriteria() {
            string tokenString = await GetUserTokenAsync("Sam", "", "/api/Authentication/Authenticate");
            var authorizationToken = new AuthenticationHeaderValue("Bearer", tokenString);
            string criteria = CriteriaOperator.FromLambda<Employee>(x => x.FirstName == "Aaron" || x.LastName == "Benson").ToString();
            string url = CreateRequestUrl("Employee List Report", criteria, null, null, ExportTarget.Csv);
            var httpRequest = new HttpRequestMessage(HttpMethod.Get, url);
            httpRequest.Headers.Authorization = authorizationToken;
            var response = await httpClient.SendAsync(httpRequest);
            string loadedReport = await response.Content.ReadAsStringAsync();
            // Validate the result
            // ...
        private string CreateRequestUrl(
            string reportName, string? criteria = null, string? reportParameters = null,
            SortProperty[]? sortProperties = null, ExportTarget exportType = ExportTarget.Pdf) {
            string url = $"{ApiUrl}({reportName})";
            var q = $"fileType={exportType}";
            if(!string.IsNullOrEmpty(criteria)) {
                q += $"&criteria={criteria}";
            if(sortProperties != null && sortProperties.Length > 0) {
                foreach(var sortProperty in sortProperties) {
                    q += $"&sortProperty={$"{sortProperty.PropertyName},{sortProperty.Direction}"}";
            if(!string.IsNullOrEmpty(reportParameters)) {
                q += $"&{reportParameters}";
            url += "?" + q;
            return url;
        async Task<string> GetUserTokenAsync(string userName, string password, string requestPath) {
            var request = new HttpRequestMessage(HttpMethod.Post, requestPath);
            request.Content = new StringContent(
                $"{{ \"userName\": \"{userName}\", \"password\": \"{password}\" }}", Encoding.UTF8, "application/json");
            var httpResponse = await httpClient.SendAsync(request);
            if(!httpResponse.IsSuccessStatusCode) {
                throw new UnauthorizedAccessException($"Authorization request failed! Code {(int)httpResponse.StatusCode}, '{httpResponse.ReasonPhrase}'");
            var tokenString = await httpResponse.Content.ReadAsStringAsync();
            return tokenString;
See Also