Hello Community
I am working with Reporting using HTML5 Report Viewer. I have designed Custom Report Resolver for showing up the reports. It works like a charm. I am facing issue regarding how to pass custom error message from Report Controller. I have checked some links regarding localization given in forums which says to add JS files but i am not exactly getting how to proceed. Can someone provide code sample?
13 Answers, 1 is accepted
You can subscribe in the report for its Error event and you will be able to log errors during the processing of the report.
To get the server error message on displaying the report in the HTML5 Report Viewer, the viewer also exposes an error event where you can get the actual error message and include logic for further handling (hide the viewer, display a message, and etc.). The errors caught here are errors thrown during the report processing on the server or errors occurring during the information exchange between the client(viewer) and the service.
For example:
$(document).ready(
function
() {
$(
"#reportViewer1"
)
.telerik_ReportViewer({
serviceUrl:
"api/reports/"
,
templateUrl:
'ReportViewer/templates/telerikReportViewerTemplate-9.1.15.702.html'
,
reportSource: {
report:
"Telerik.Reporting.Examples.CSharp.ReportCatalog, CSharp.ReportLibrary"
,
parameters: { }
},
viewMode: telerikReportViewer.ViewModes.INTERACTIVE,
scaleMode: telerikReportViewer.ScaleModes.SPECIFIC,
scale: 1.0,
error:
function
(e, args) {
log(args);
alert(
'The error indicates an issue with '
+ args);
}
});
});
I hope the provided information is helpful.
Regards,
Stef
Telerik
Thanks for the quick response.
I had seen in documentation regarding error event. But i was not sure that we need to throw exception from server side for appropriate message to show on client side.
here it shows that if we add this js in application we can show messages given there. can we add our custom message in this JS and how these messages will be retrieved from JS?
Below is an update from your support ticket on the same question:
"If there are errors during the report processing, they can be caught in the report's Error event which event arguments allow you to Cancel the further execution of the report and throw a custom error message that will be seen at the client.
//the report
public
partial
class
rptTest : Telerik.Reporting.Report
{
public
rptTest()
{
//
// Required for telerik Reporting designer support
//
InitializeComponent();
this
.Error +=
new
Telerik.Reporting.ErrorEventHandler(
this
.rptTest_Error);
//
// TODO: Add any constructor code after InitializeComponent call
//
}
private
void
rptTest_Error(
object
sender, ErrorEventArgs eventArgs)
{
eventArgs.Cancel =
true
;
throw
new
Exception(
"manually cancelled"
);
}
}
//////////////////////////////////////
//the client
$(document).ready(function () {
$(
"#reportViewer1"
)
.telerik_ReportViewer({
serviceUrl:
"api/reports/"
,
templateUrl:
'ReportViewer/templates/telerikReportViewerTemplate-9.1.15.702.html'
,
reportSource: {
report:
"Telerik.Reporting.Examples.CSharp.ReportCatalog, CSharp.ReportLibrary"
,
parameters: { }
},
viewMode: telerikReportViewer.ViewModes.INTERACTIVE,
scaleMode: telerikReportViewer.ScaleModes.SPECIFIC,
scale: 1.0,
error: function (e, args) {
$(
'.trv-error-pane'
).toggle();
alert(args);
console.log(
"error!"
);
}
});
});
About the error messages listed in HTML5 Viewer Localization, you can change only the string next to the identifier of the error. Extending the error list with custom entries is not supported."
Regards,
Stef
Telerik
Hi,
I have tried implementing the custom report class by inheriting Telerik.Reporting.Report as suggested here but InitializeComponent() cannot be found and Me.Error is an event that cannot be assigned to directly. (I am using VB).
Can you suggest how I can customise the error message that is appearing in the report viewer when an exception is thrown from my customReportResolver.Resovle() function?
I am setting my reportsource and serviceurl in the code behind so cannot add the client side error tag in Javascript as suggested in this thread: http://www.telerik.com/forums/custom-report-resolver-exception
The report's Error event will be fired if there is an error during the report processing. Such exceptions are returned to the client by the viewer, and they can be handled further in the viewer's error event.
The viewer's error event can be used for handling errors on registering the viewer or other related to the connection with the Reporting REST service specified by the viewer's serviceUrl.
About errors result of resolving a report in a custom resolver, the thrown Exception will not be bubbled directly to the viewer. Actually, there will be an error on the attempt to get parameters information (the first request the viewer will send on displaying a report) - the request will go through the custom resolver's Resolve method and it will return an error, which can be handled by overriding the ReportscontrollerBase GetParameters method:
public
override
System.Net.Http.HttpResponseMessage GetParameters(
string
clientID, ClientReportSource reportSource)
{
try
{
return
base
.GetParameters(clientID, reportSource);
}
catch
(Exception ex) {
throw
new
Exception(
"report resolving failure."
);
}
}
I hope this helps.
Regards,
Stef
Telerik
Hi
I would like to know if this approach would also work in a scenario where I'm using .trdx files instead of Csharp class ones?
(in a .net MVC project)
//the report
public
partial
class
rptTest : Telerik.Reporting.Report
{
public
rptTest()
{
//
// Required for telerik Reporting designer support
//
InitializeComponent();
this
.Error +=
new
Telerik.Reporting.ErrorEventHandler(
this
.rptTest_Error);
//
// TODO: Add any constructor code after InitializeComponent call
//
}
private
void
rptTest_Error(
object
sender, ErrorEventArgs eventArgs)
{
eventArgs.Cancel =
true
;
throw
new
Exception(
"manually cancelled"
);
}
}
//////////////////////////////////////
//the client
$(document).ready(function () {
$(
"#reportViewer1"
)
.telerik_ReportViewer({
serviceUrl:
"api/reports/"
,
templateUrl:
'ReportViewer/templates/telerikReportViewerTemplate-9.1.15.702.html'
,
reportSource: {
report:
"Telerik.Reporting.Examples.CSharp.ReportCatalog, CSharp.ReportLibrary"
,
parameters: { }
},
viewMode: telerikReportViewer.ViewModes.INTERACTIVE,
scaleMode: telerikReportViewer.ScaleModes.SPECIFIC,
scale: 1.0,
error: function (e, args) {
$(
'.trv-error-pane'
).toggle();
alert(args);
console.log(
"error!"
);
}
});
});
Reports saved in TRDX|TRDP files have to be deserialized|unpackaged in order to be subscribed for their Error events. This can be done by using a custom resolver for the Reporting REST service.
Without subscribing the reports to their Error events, the processing error will be bubbled directly to the viewer's error event.
Regards,
Stef
Telerik by Progress
Thank you for you fast answer Stef.
Sorry but i asked the wrong question, I ased about Error handling but what I realy wanted to know is Exception handling.
We currently want to use TRDX files and also ObjectData source. It all works until there is an Exception thrown. Then it kind of displays a red Exception message inside the ReportViewer which I can't seem to get a hold of, nomatter what I try. (We also want to throw custom exceptions to let the user know whats wrong in a talkative error pop up message of some sort.
As i understood from the documentation it would be easier if we would just leave the idea of useing TRDX files and just use the Visual Studio report builder plugin.
And my last question would be, is it even a good practice to throw exceptions in the objectdata class and let them bubble up to the ReportViewer, or should they be handled earlier?
Best regards
You can keep using TRDX files, where in a custom resolver you can deserialize them and subscribe the produced Telerik.Reporting.Report instances to their Error events. In each Error event you can cancel the further processing and throw your own message. The message will be displayed in an yellow box in the viewer's page area. the error message can be further handled in the viewer's error event as well.
In case the error occurs in the data-retrieval method, you can throw an exception in it, which will be displayed in the report in a white box with red border.
Regards,
Stef
Telerik by Progress
Hi Stef,
I've made some progress according to what you recommended. I'm very gratefull for your help sofar.
I made a custom resolver that serializes/unpacks the report files according to file type (trdx, or trdp)
Now I can sign up to the error event and also catch exceptions.
But another problem arised becouse of my custom resolver. The Subreport paths seem to be broken.
Everything goes well until there is a subreport within a report.
The main report is located in a /ReportFiles folder and so is the subreport. While debuging I can see that the path for the main report is always valid at brakepoints. But the subreport doesn't even run in to brakepoints and the path is set to the root of the project by default for some reason.
As I understand the subreport is infact not going throug my custom resolver, nor is it going to the fallback resolver, but somewhere else. I'm a bit confused at this point.
Could you please help shine some light upon how subreports are being resolved? It would help me out a lot.
Best Regards
János
If you are using the code from Changing the connection string dynamically according to runtime data
check the Report DeserializeReport(UriReportSource uriReportSource) method and verify the file path retrieved from the UriReportSource is mapped correctly on the server. You can use method like Server.MapPath in case of getting a relative file path.
Regards,
Stef
Telerik by Progress
Hi Stef,
I managed to find the problem but, not the solution. The link what you've sent wasn't exactly the same situation that I have, but guided me in the right direction. (I don't intent to change the ConnectionString in runtime) Just want to resolve the main and the subreports from the same path.
But I seem to be missunderstanding something, I'll write down how I think the resolveing process goes. Please correct me if I am wrong in something.
Let's assume the reports (also the subreports) are in this path: C:\Project\ReportFiles
The resolving process goes as this:
-I request a report by file name: main.trdx
-The Custom or Regular Resolver steps in to action and returns the ReportSource.
-I deserialize/unpack the report and add an exepction handler to the error event. And return the ReportSource.
-The report has a subreport which is a type of ReportItemBase that also has a ReportSource.Uri. The main report tries to resolve the subreport by invoking a ReportResolver.And it fails if I deserialize the report.
It evaluates to main report: C:\Project\ReportFiles\main.trdx (which is correct)
while the subreport is: C:\Project\main.trdx (which is incorrect)
If I use the default resolvert it resolves both path just fine.
The only way i managed to get it working is by recursively chechking each and every ReportItemBase item of the main report to check if it is a SubReport item. And manualy resolve the subreport. Which I thought worked for onece but, it doesn't. Since I loose the parameters somehow. And also this method sounds very resource consuming and sub-optimal.
I'll paste my code:
public
class
ReportsController : ReportsControllerBase
{
static
ReportServiceConfiguration configurationInstance;
static
ReportsController()
{
var appPath = HttpContext.Current.Server.MapPath(
"~/"
);
var reportsPath = Path.Combine(appPath,
"/ReportFiles"
);
var resolver =
new
CustomResolver2(reportsPath)
.AddFallbackResolver(
new
CustomResolver(reportsPath));
//Setup the ReportServiceConfiguration
configurationInstance =
new
ReportServiceConfiguration
{
HostAppId =
"Project"
,
Storage =
new
FileStorage(),
ReportResolver = resolver
// ReportSharingTimeout = 0,
// ClientSessionTimeout = 15,
};
}
public
ReportsController()
{
//Initialize the service configuration
this
.ReportServiceConfiguration = configurationInstance;
}
}
class
CustomResolver : ReportFileResolver
{
public
CustomResolver(
string
path) :
base
(path) { }
protected
override
ReportSource ResolveReport(
string
report)
{
return
base
.ResolveReport(report);
}
public
ReportSource GetReportSource(
string
report){
return
base
.ResolveReport(report);
}
}
class
CustomResolver2 : ReportFileResolver
{
private
CustomResolver customResolver;
public
CustomResolver2(
string
path) :
base
(path) {
customResolver =
new
CustomResolver(path);
}
public
ReportSource GetReportSource(
string
report)
{
return
base
.ResolveReport(report);
}
protected
override
ReportSource ResolveReport(
string
report)
{
ReportSource reportSource = customResolver.GetReportSource(report);
//TRDX
if
(report.Split(
'.'
).Last().ToLower().Equals(
"trdx"
))
{
var uriReportSource = (UriReportSource)reportSource;
var reportInstance = DeserializeReport(uriReportSource);
FixSubreportPath(reportInstance);
return
CreateInstanceReportSource(reportInstance, uriReportSource);
}
//TRDP
else
{
var uriReportSource = (UriReportSource)reportSource;
var reportInstance = UnpackReport(uriReportSource);
FixSubreportPath(reportInstance);
return
CreateInstanceReportSource(reportInstance, uriReportSource);
}
}
void
FixSubreportPath(ReportItemBase reportItemBase) {
foreach
(var item
in
reportItemBase.Items)
{
FixSubreportPath(item);
if
(item
is
SubReport)
{
var appPath = HttpContext.Current.Server.MapPath(
"~/"
);
var reportsPath = Path.Combine(appPath,
"/ReportFiles/"
);
var subReport = (SubReport)item;
var subUriReportSource = (UriReportSource)subReport.ReportSource;
var cr =
new
CustomResolver2(reportsPath);
subReport.ReportSource = cr.GetReportSource(subUriReportSource.Uri);
}
}
}
ReportSource CreateInstanceReportSource(IReportDocument report, ReportSource originalReportSource)
{
var instanceReportSource =
new
InstanceReportSource { ReportDocument = report };
instanceReportSource.Parameters.AddRange(originalReportSource.Parameters);
return
instanceReportSource;
}
Report DeserializeReport(UriReportSource uriReportSource)
{
var settings =
new
System.Xml.XmlReaderSettings();
settings.IgnoreWhitespace =
true
;
using
(var xmlReader = System.Xml.XmlReader.Create(uriReportSource.Uri, settings))
{
var xmlSerializer =
new
Telerik.Reporting.XmlSerialization.ReportXmlSerializer();
var report = (Telerik.Reporting.Report)xmlSerializer.Deserialize(xmlReader);
report.Error += HandleError;
return
report;
}
}
Report UnpackReport(UriReportSource uriReportSource)
{
var reportPackager =
new
ReportPackager();
using
(var sourceStream = System.IO.File.OpenRead(uriReportSource.Uri))
{
var report = reportPackager.Unpackage(sourceStream);
return
report;
report.Error += HandleError;
}
}
private
void
HandleError(
object
sender, Telerik.Reporting.ErrorEventArgs eventArgs)
{
throw
new
Exception(
"Exception: "
+ eventArgs.Exception.Message);
}
}
(I know CustomResolver is the same as a default resolver, its just for troubleshooting, And so is the GetReportSource method)
Thank you for you help
Best Regards
János
Damn, I broke the code formating section...
And also made a mistake:
"It evaluates to main report: C:\Project\ReportFiles\main.trdx (which is correct)
while the subreport is: C:\Project\sub.trdx (which is incorrect)"
Thanks