SQL Reporting Services - Viewer broken in Non-IE Browsers: FIX
Download Source
SSRS Browser Fix Installer
A while back I was tasked with getting SQL Reporting Services reports rendering in MVC while deployed on the Cloud with Windows Azure, and the reports must render in IE, Firefox and Safari. The scenario is somewhat similar to trying to ride a bicycle backwards while chewing bubble gum and reciting the last 18 pages of the 2nd volume of War and Peace... in Russian. I was eventually able to complete the task, though it was not without much hair pulling and profanity. That solution involved using local reports, an embedded Report Viewer control, and a custom state manager (I may blog about that later if there is any interest).
Fast forward to my current project, and Lo And Behold there is a requirement for a cross browser reporting solution using SSRS (though thankfully this time sans Azure). The difference here is we are able to use the SSRS Reporting Server's Report Viewer as opposed to embedding our own. However, the same monster reared it's ugly head: Rendering reports in Non-IE browsers was broken.
The Problem:
When the Report Viewer control renders some reports, a small 1x1 gif (Blank.gif) is used to provide artificial padding to some table elements or images. During the rendering process, an asynchronous call to ReportViewerWebControl.axd is made to retrieve this blank image. In Internet Explorer, this call obviously has no issues, however in non-IE browsers this call fails with varying results. The best case scenario is as seen above, where the image is simply not found and is replaced by the standard "I don't know WTF you were trying to load" broken page image. The worst case is the dreaded Yellow Screen of Death. Both the ASP.NET web control and the report viewer provided by the SSRS Web Interface are effected by this bug.
The underlying problem is that the request for the Blank.gif requires the parameter IterationId in the query string. However in non-IE browsers this parameter is curiously absent in the request. The ReportViewerWebControl.axd doesn't handle this missing parameter gracefully and delivers varying levels of pain and suffering in the resulting response.
The Solution:
There are two ways this can be addressed, depending on if you are hosting the Report Viewer in your web application, or using the SSRS Web Report Viewer control. Truly, the fix is the same in both instances, however it is implemented differently in each case. Basically we create a handler that intercepts web requests, monitors it for requests to the ReportViewerWebControl.axd with key words "blank.gif" and "iteration". If found, we append the IterationId to the request.
The fix can be found in this Microsoft Support Bug Report. Credit goes to Stefan Mohr for his original Workaround.
Embedded Report Viewer Fix:
In your MVC Web application, open the Global.asax.cs file and add the following code:
SSRS Report Server Viewer Fix:
The SSRS fix is largely the same, only here a custom HttpModule is required, in addition to modification of the SSRS web.config files. The original fix is credited to MikeDotNet22 as seen in the Bug Report workaround tab. I've created an installer that will do all of this for you, so if you don't feel like doing the heavy lifting yourself, you can download it here. This installer has been tested on SSRS 2008 R2, and should handle multiple instances. However the code is provided above, so you can update for your particular situation if needed.
Note: While the HttpModule is built using .NET 3.5, the installer custom code uses .NET 4. Therefore .NET Framework 4.0 is required on the Report Server to run the installer. Administrator Privileges are required, and of course I'm not responsible if your server blows up or anything like that.
The HttpModule is a simple class library (.NET 3.5 as SSRS 2008 R2 is build on 3.5) with a single class implementing IHttpModule. Start by creating a class library and adding the System.Web reference. The implementation IHttpModule is simple. We need to Implement methods for Init and Dispose, as well as a single property: IsReusable. Generally this is set to true, so that the module is initialized only once.
The only thing left is is to hook into the BeginRequest event on the HttpApplication object. The following code is essentially the same fix as the one above, only in a handy Http Handler that we will deploy to the GAC of the SSRS Server.
The assembly will need to be signed with a strong name. In Visual Studio, select the project choose Properties from the context menu. In the Signing tab, select the checkbox, and choose New... from the drop down. Give the key a name (generally the same as your assembly, and password protect it if you like.
Build your new DLL, open a CMD window (Start -> Run -> CMD) and deploy it to the GAC with the following command line:
The last step is to edit the web.config of both the ReportServer and ReportManager in order to load the module. For SSRS 2008 R2, the path to these files is generally "C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER\Reporting Services\[One of the above]". Open the Web.config files in each directory and add this line inside the httpModules node:
You can find the relevant information by running the gacutil program with the /l switch and passing it the name of your dll:
Restart your SSRS Reporting Service and you should see the issues go away.
Now I hear this issue is even affecting IE9 and 10. I wonder if Microsoft will provide their own fix soon?
SSRS Browser Fix Installer
A while back I was tasked with getting SQL Reporting Services reports rendering in MVC while deployed on the Cloud with Windows Azure, and the reports must render in IE, Firefox and Safari. The scenario is somewhat similar to trying to ride a bicycle backwards while chewing bubble gum and reciting the last 18 pages of the 2nd volume of War and Peace... in Russian. I was eventually able to complete the task, though it was not without much hair pulling and profanity. That solution involved using local reports, an embedded Report Viewer control, and a custom state manager (I may blog about that later if there is any interest).
Fast forward to my current project, and Lo And Behold there is a requirement for a cross browser reporting solution using SSRS (though thankfully this time sans Azure). The difference here is we are able to use the SSRS Reporting Server's Report Viewer as opposed to embedding our own. However, the same monster reared it's ugly head: Rendering reports in Non-IE browsers was broken.
The Problem:
When the Report Viewer control renders some reports, a small 1x1 gif (Blank.gif) is used to provide artificial padding to some table elements or images. During the rendering process, an asynchronous call to ReportViewerWebControl.axd is made to retrieve this blank image. In Internet Explorer, this call obviously has no issues, however in non-IE browsers this call fails with varying results. The best case scenario is as seen above, where the image is simply not found and is replaced by the standard "I don't know WTF you were trying to load" broken page image. The worst case is the dreaded Yellow Screen of Death. Both the ASP.NET web control and the report viewer provided by the SSRS Web Interface are effected by this bug.
The underlying problem is that the request for the Blank.gif requires the parameter IterationId in the query string. However in non-IE browsers this parameter is curiously absent in the request. The ReportViewerWebControl.axd doesn't handle this missing parameter gracefully and delivers varying levels of pain and suffering in the resulting response.
The Solution:
There are two ways this can be addressed, depending on if you are hosting the Report Viewer in your web application, or using the SSRS Web Report Viewer control. Truly, the fix is the same in both instances, however it is implemented differently in each case. Basically we create a handler that intercepts web requests, monitors it for requests to the ReportViewerWebControl.axd with key words "blank.gif" and "iteration". If found, we append the IterationId to the request.
The fix can be found in this Microsoft Support Bug Report. Credit goes to Stefan Mohr for his original Workaround.
Embedded Report Viewer Fix:
In your MVC Web application, open the Global.asax.cs file and add the following code:
- protected void Application_BeginRequest(object sender, EventArgs e)
- {
- // Original fix credit to Stefan Mohr
- // Bug fix for MS SSRS Blank.gif 500 server error missing parameter IterationId
- // https://connect.microsoft.com/VisualStudio/feedback/details/556989/
- if (HttpContext.Current.Request.Url.PathAndQuery.StartsWith("/Reserved.ReportViewerWebControl.axd") &&
- !HttpContext.Current.Request.Url.ToString().ToLower().Contains("iteration") &&
- !String.IsNullOrEmpty(HttpContext.Current.Request.QueryString["ResourceStreamID"]) &&
- HttpContext.Current.Request.QueryString["ResourceStreamID"].ToLower().Equals("blank.gif"))
- {
- Context.RewritePath(String.Concat(HttpContext.Current.Request.Url.PathAndQuery, "&IterationId=0"));
- }
- }
SSRS Report Server Viewer Fix:
The SSRS fix is largely the same, only here a custom HttpModule is required, in addition to modification of the SSRS web.config files. The original fix is credited to MikeDotNet22 as seen in the Bug Report workaround tab. I've created an installer that will do all of this for you, so if you don't feel like doing the heavy lifting yourself, you can download it here. This installer has been tested on SSRS 2008 R2, and should handle multiple instances. However the code is provided above, so you can update for your particular situation if needed.
Note: While the HttpModule is built using .NET 3.5, the installer custom code uses .NET 4. Therefore .NET Framework 4.0 is required on the Report Server to run the installer. Administrator Privileges are required, and of course I'm not responsible if your server blows up or anything like that.
The HttpModule is a simple class library (.NET 3.5 as SSRS 2008 R2 is build on 3.5) with a single class implementing IHttpModule. Start by creating a class library and adding the System.Web reference. The implementation IHttpModule is simple. We need to Implement methods for Init and Dispose, as well as a single property: IsReusable. Generally this is set to true, so that the module is initialized only once.
- using System;
- using System.Web;
- namespace SSRSReportViewerPatch
- {
- public class ReportViewerPatchModule : IHttpModule
- {
- public bool IsReusable
- {
- get { return true; }
- }
- public void Dispose()
- {
- }
- public void Init(HttpApplication context)
- {
- }
- }
- }
The only thing left is is to hook into the BeginRequest event on the HttpApplication object. The following code is essentially the same fix as the one above, only in a handy Http Handler that we will deploy to the GAC of the SSRS Server.
- public void Dispose()
- {
- }
- public void Init(HttpApplication context)
- {
- context.BeginRequest += context_BeginRequest;
- }
- void context_BeginRequest(object sender, EventArgs e)
- {
- HttpApplication context = (HttpApplication)sender;
- HttpRequest request = context.Request;
- // Original fix credit to Stefan Mohr, SSRS Server fix to MikeDotNet22
- // Bug fix for MS SSRS Blank.gif 500 server error missing parameter IterationId
- // https://connect.microsoft.com/VisualStudio/feedback/details/556989/
- if (request.Url.ToString().ToLower().Contains("blank.gif")
- && !request.Url.ToString().ToLower().Contains("iteration"))
- {
- context.Context.RewritePath(String.Concat(request.Url.PathAndQuery, "&IterationId=0"));
- }
- }
The assembly will need to be signed with a strong name. In Visual Studio, select the project choose Properties from the context menu. In the Signing tab, select the checkbox, and choose New... from the drop down. Give the key a name (generally the same as your assembly, and password protect it if you like.
Build your new DLL, open a CMD window (Start -> Run -> CMD) and deploy it to the GAC with the following command line:
- gacutil /i [Full Path To Assembly]\MyAssembly.dll
The last step is to edit the web.config of both the ReportServer and ReportManager in order to load the module. For SSRS 2008 R2, the path to these files is generally "C:\Program Files\Microsoft SQL Server\MSRS10_50.MSSQLSERVER\Reporting Services\[One of the above]". Open the Web.config files in each directory and add this line inside the httpModules node:
- <add name="YouNamespace" type="YourNamespace.YourClassName, YournameSpame, Version=1.0.0.0, Culture=neutral, PublicKeyToken=YourPublicKeyToken"/>
You can find the relevant information by running the gacutil program with the /l switch and passing it the name of your dll:
- C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC>gacutil /l NonIEReportViewerHandler
- Microsoft (R) .NET Global Assembly Cache Utility. Version 4.0.30319.1
- Copyright (c) Microsoft Corporation. All rights reserved.
- The Global Assembly Cache contains the following assemblies:
- NonIEReportViewerHandler, Version=1.0.0.2, Culture=neutral, PublicKeyToken=5b354a229218a144, processorArchitecture=MSIL
- Number of items = 1
Restart your SSRS Reporting Service and you should see the issues go away.
Now I hear this issue is even affecting IE9 and 10. I wonder if Microsoft will provide their own fix soon?
Or, in Chrome add this to your CSS
ReplyDeletebody:nth-of-type(1) img[src*="Blank.gif"]{display:none;}
Tried your solution bob, wasn't clear enough for me to implement. I ended up sticking with the top solution, but it was an evolution of yours Devin and Mr. Mohr's simply because the 500 was being tucked away somewhere and not showing its ugly face, but the image was still broken on the report (and all that matters is cosmetics obviously ;0)
ReplyDeleteThanks for taking the time to post this and hopefully MS comes up with something for this bug. To me it seems like a pretty simple fix, but who knows where it may lie in their priority list.
Devin, thank you so much for.. existing!
ReplyDeleteI'm going through the same problem and I don't know how much time I'd spend untill I discovered it was a missing parameter on the querystring.
The post is very clear and objective, and I'll implement this solution righ now.
Thanks Rodrigo. Glad I could help!
ReplyDeleteI've been searching for an official update on this issue, but found none.. Even the bug report on connect isn't available anymore.
ReplyDeleteHaving to administer a large codebase at my company, I hate having to maintain these little hacky components just to keep thing running at a BASIC level...
I hate browser wars..
Thanks a lot, Devin!
ReplyDeleteOne minor clarification. Gacutil.exe is included with Visual Studio (as part of the Microsoft SDK) and the .Net framework (v1 and 1.1 only) So it might be in one of the following:
%programfiles%\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin\gacutil.exe
%programfiles%\Microsoft SDKs\Windows\v6.0\Bin
%programfiles%\Microsoft SDKs\Windows\v6.0A\Bin
...
%windir%\Microsoft.NET\Framework\v1.0.3705
%windir%\Microsoft.NET\Framework\v1.1.4322
Thanks a million, it helped us to fix the report viewer issue.
ReplyDeleteHappy to help!
DeleteThank you very much. Your installer worked for me.
ReplyDeleteSolve MS SQL Server Error amid Connecting with Online MS SQL Server Support
ReplyDeleteConfronting associating blunder in MS SQL Server? At that point rapidly attempt beneath recorded strides to take care of this issue. Well! On the off chance that you are utilizing site board then you need to go board then site, select site and now check "empower compose consent" in home "organizer tab". By attempting these means you can without much of a stretch take care of this issue however in the event that not then contact to Remote Infrastructure Management Support Microsoft SQL Server or SQL Server Database Support to settle this blunder.
For More Info: https://cognegicsystems.com/
Contact Number: 1-800-450-8670
Email Address- info@cognegicsystems.com
Company’s Address- 507 Copper Square Drive Bethel Connecticut (USA) 06801