By default, Report Viewer and Report Designer do not have the option to pass the authorization header for export request. You can find more details about the reason from How to resolve the image rendering and exporting issue with ASP.NET Core Authentication middleware.
You can add customized export functionality in application with authorization headers using exportItemClick
event, which will be invoked on exporting the reports from Report Viewer and Report Designer preview. You need to follow the below steps:
You need to set the “exportItemClick” on Bold Report Viewer initialization as shown in the following code sample.
$(function () {
$("#viewer").boldReportViewer({
reportServiceUrl: "/api/ReportViewer",
reportPath: 'sales-report.rdl',
exportItemClick: onExportItemClick
});
});
Implement the function and replace the following code samples to send export type to server side.
function onExportItemClick(args) {
var exportType;
var exportTypeFormat;
if (args.value != null) {
if (args.value == 'PDF') {
exportType = 'PDF';
exportTypeFormat = 'PDF';
} else if (args.value == 'Word') {
exportType = 'Word_' + args.model.exportSettings.wordFormat;
exportTypeFormat = 'Word';
} else if (args.value == 'Excel') {
exportType = 'Excel_' + args.model.exportSettings.excelFormat;
exportTypeFormat = 'Excel';
} else if (args.value == 'CSV') {
exportType = 'CSV';
exportTypeFormat = 'CSV';
} else {
var text = $(args.target.textContent);
exportTypeFormat = $(args.target).text();
}
}
if (args.value != null) {
var _this = this;
args.cancel = true;
var proxy = $('#viewer').data('boldReportViewer');
var requrl = '/api/ReportViewer/ExportReport';
var _json = {
exportType: exportType,
reportViewerToken: _this['_reportViewerToken']
};
$.ajax({
contentType: "application/json; charset=utf-8",
dataType: "json",
type: "POST",
url: requrl,
data: JSON.stringify(_json),
crossDomain: true,
success: (data) => {
if (data) {
//Implement function to start the export
}
},
error: (data) => {
alert("Ajax failure");
}
})
}
}
Implement the function ExportReport
and replace the following code samples to get the report in the specified export type.
public Dictionary<string, object> ExportReport([FromBody] Dictionary<string, object> jsonResult)
{
string types = null;
string fileName = null;
Guid fileId = Guid.NewGuid();
if (jsonResult.ContainsKey("exportType"))
{
type = null;
types = type = jsonResult.Single(jr => jr.Key == "exportType").Value.ToString();
}
token = jsonResult["reportViewerToken"].ToString();
if (types.Contains("PDF"))
{
fileName = "Name" + fileId.ToString() + ".pdf";
}
else if (types.Contains("Word"))
{
fileName = "Name" + fileId.ToString() + ".doc";
}
else if (types.Contains("Excel"))
{
fileName = "Name" + fileId.ToString() + ".xls";
}
else if (types.Contains("CSV"))
{
fileName = "Name" + fileId.ToString() + ".csv";
}
string _token = jsonResult["reportViewerToken"].ToString();
string type1 = jsonResult["exportType"].ToString();
var stream = ReportHelper.GetReport(_token, type1);
var reportLocation = AppDomain.CurrentDomain.BaseDirectory;
using (var fileStream = System.IO.File.Create(reportLocation + @"App_Data\\" + fileName))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fileStream);
}
Dictionary<string, object> jsonData = new Dictionary<string, object>();
filename = null;
filename = fileName;
jsonData.Add("fileName", fileName);
return jsonData;
}
After the ajax request gets successfully completed, getting the report data from the server side. You need to implement the function exportReport
at client side and replace the following code samples to pass the custom authentication in headers along with file data using blob.
function exportItemClick() {
...
...
$.ajax({
contentType: "application/json; charset=utf-8",
dataType: "json",
type: "POST",
url: requrl,
data: JSON.stringify(_json),
crossDomain: true,
success: (data) => {
if (data) {
//Implement function to start the export
exportReport(data);
}
},
error: (data) => {
alert("Ajax failure");
}
})
}
function exportReport(jsonData) {
let url = `/api/ReportViewer/ExportStart`;
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'authorization': 'authenticationtoken' },
responseType: 'json',
body: JSON.stringify(jsonData)
}).then(function (resp) {
return resp.blob();
}).then(function (data) {
var filename = null;
Object.keys(jsonData).forEach(function (fileName) {
filename = jsonData[fileName];
//Invoke the download file at client side
});
});
}
Implement the function ExportReport
at server side and replace the following code samples to start the export of the report with the file data.
[HttpPost]
public object ExportReport([FromBody] Dictionary<string, object> jsonResult)
{
string fileId = null;
if (jsonResult.ContainsKey("fileName"))
{
fileId = jsonResult["fileName"].ToString();
}
System.Net.Http.HttpResponseMessage response = new System.Net.Http.HttpResponseMessage();
try
{
string path = HttpContext.Current.Server.MapPath("~/App_Data/");
var stream = File.Open(path + fileId, FileMode.Open);
stream.Position = 0;
System.Web.HttpResponse httpResponse = System.Web.HttpContext.Current.Response;
httpResponse.ClearContent();
httpResponse.Expires = 0;
httpResponse.Buffer = true;
httpResponse.AddHeader("Content-Disposition", "attachment;filename=\"" + fileId + "\"");
httpResponse.AddHeader("Content-Type", "Application/" + type);
httpResponse.Clear();
byte[] buffer = new byte[stream.Length + 1];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
httpResponse.OutputStream.Write(buffer, 0, bytesRead);
}
response.StatusCode = System.Net.HttpStatusCode.OK;
}
catch (Exception ex)
{
response.StatusCode = System.Net.HttpStatusCode.NotFound;
}
return response;
}
You need to download the report at client side as shown in the following code sample.
function exportReport(jsonData) {
let url = `/api/ReportViewer/ExportStart`;
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'authorization': 'authenticationtoken' },
responseType: 'json',
body: JSON.stringify(jsonData)
}).then(function (resp) {
return resp.blob();
}).then(function (data) {
var filename = null;
Object.keys(jsonData).forEach(function (fileName) {
filename = jsonData[fileName];
//Invoke the downloaf file at client side
downloadFile(data, filename);
});
});
}
function downloadFile(fileData, filename) {
// Create an object URL for the blob object
const url = URL.createObjectURL(fileData);
const a = document.createElement('a');
a.href = url;
a.download = filename || 'download';
const clickHandler = () => {
setTimeout(() => {
URL.revokeObjectURL(url);
this.removeEventListener('click', clickHandler);
}, 150);
};
a.addEventListener('click', clickHandler, false);
a.click();
return a;
}
How to resolve the image rendering and exporting issue with ASP.NET Core Authentication middleware