Issue serving PDF files to Google Chrome - c#

I wrote a webservice using ASMX style calls to serve PDF files. The service processes data sent to it as a POST operation, writes the data to the response, and sends the data back after adding a new mime type to the headers.
The PDF files are generated client side in a flex application using AlivePDF.
It's worked fine for a while, but it recently began failing in google chrome - Instead of opening the PDF in either a new window or a PDF viewer (depending on the browser's configuration), chrome simply displays an empty page.
Is there a reason why this code would fail to open a PDF if it has been passed valid PDF data in the input stream?
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Print : System.Web.Services.WebService
{
[WebMethod]
public string PrintPDF()
{
HttpRequest request = HttpContext.Current.Request;
HttpResponse response = HttpContext.Current.Response;
string requestMethod = request.Params["method"];
string requestFilename = request.Params["name"];
if(!validateRequest(request))
{
throw new ArgumentException(String.Format("Error downloading file named '{0}' using disposition '{1}'", requestFilename, requestMethod));
}
response.AddHeader("Content-Disposition", "attachment; filename=\"" + requestFilename + "\"");
byte[] pdf = new byte[request.InputStream.Length];
request.InputStream.Read(pdf, 0, (int)request.InputStream.Length);
response.ContentType = "application/pdf";
response.OutputStream.Write(pdf, 0, (int)request.InputStream.Length);
response.Flush();
response.End();
return "Fail";
}
private bool validateRequest(HttpRequest request)
{
string requestMethod = request.Params["method"];
string requestFilename = request.Params["name"];
Regex cleanFileName = new Regex("[a-zA-Z0-9\\._-]{1, 255}\\.[a-zA-Z0-9]{1, 3}");
return (requestMethod == "attachment" || requestMethod == "inline") &&
cleanFileName.Match(requestFilename) != null;
}
}

This is a common problem with Chrome. It has to do with Chrome's homebrewed pdf viewer being really picky.
While this doesn't fix the display issue, you can force a download, fixing the accessibility issue.
Broken
Works

Related

Asp.Net Core: Download SSRS reports in PDF format on client browser

I have one SSRS report URL which if I enter that URL in the browser then it shows the SSRS report with Print and Save option like:
Which works fine on the browser.
What I want, there is one button on .cshtml page so by clicking on that button I need to download the report on client browser.
URL: http://xyz/ReportS/report/UAT/SampleReport_V1?Srno=X123
What I tried:
by setting &rs:Format=PDF and made a HTTP Request from Asp.Net Core Application but it generated PDF but in corrupt format.
Code:
public void Download_SSRSInPDF()
{
string URL = "http://xyz/ReportS/report/UAT/SampleReport_V1";
string Command = "Render";
string Format = "PDF";
URL = URL + "?SrNo=X123&rs:Command=" + Command + "&rs:Format=" + Format;
System.Net.HttpWebRequest Req = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(URL);
Req.Credentials = System.Net.CredentialCache.DefaultCredentials;
Req.Method = "GET";
string path = #"E:\New folder\Test.pdf";
System.Net.WebResponse objResponse = Req.GetResponse();
System.IO.FileStream fs = new System.IO.FileStream(path, System.IO.FileMode.Create);
System.IO.Stream stream = objResponse.GetResponseStream();
byte[] buf = new byte[1024];
int len = stream.Read(buf, 0, 1024);
while (len > 0)
{
fs.Write(buf, 0, len);
len = stream.Read(buf, 0, 1024);
}
stream.Close();
fs.Close();
}
How to do it in Asp.Net Core?
EDIT::
I have that asmx url of my report:
http://server-name/ReportServer/reportexecution2005.asmx
But unable to proceed with this, can someone points me towards documentation?
If you want a simple URL to download the report in PDF format, don't add parameters to the web portal URL but add them to an URL that points to the web service:
string URL = "http://xyz/reportserver/?/UAT/SampleReport_V1";
string Command = "Render";
string Format = "PDF";
URL = URL + "&SrNo=X123&rs:Command=" + Command + "&rs:Format=" + Format;
You're better off doing this using the supported ReportExecution2005.asmx endpoint with something like WCF, instead of trying to use HTTP web request to impersonate a user interaction in a browser.
Once you've got it setup, there are Render methods on the report execution instance that make it really easy to get the report as a PDF.
you are converting to pdf with default converter of code. we do not follow this method to convert reports in to pdf. If you wanna ask more option which is lengthy but not tough to do. You can use WKHTMLtopdf method to convert your html in to pdf. it is easy to use. I am not saying that the method you are using is wrong. but we need to use latest technologies. Asp.netcore is working fine with wkhtmltopdf to convert html page in to pdf.

A simple reverse proxy using ASP.NET, C# with authentication

I am attempting to forward custom parameters to a RESTful API server and return the proxied response to the client-facing server. I don't want the client to have access to or be able to read the API HTTP request/response interactions, so I decided to perform this action using a reverse proxy. I have no problem forwarding the request and returning a response. The problem lies in the authentication. The client-facing server always wants to redirect to the login page because it doesn't believe the client is authenticated. I have tried using HTTPS and HTTP with similar results.
I have been researching this problem for quite some time and found quite a variety of answers, none of which seem to quite encompass my specific use case. I am following this example, which is the closest to what I specifically need. However, the credentials portion the author commented out (//request.Credentials = CredentialCache.DefaultCredentials;) doesn't seem to cover the authentication portion I am attempting to implement. Please help me understand this problem and solution.
Here is the code I am using from the controller:
public ActionResult ProxyEndpoint(string custom_string, string another_custom_string)
{
//Bunch of code here to grab the remoteUrl from AppConfig and do stuff to the parameters and store them in queryString, unnecessary to show here.
//Here's the important bits:
remoteUrl = remoteUrl + "?" + queryString; // create my remoteUrl
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(remoteUrl);
request.Credentials = CredentialCache.DefaultCredentials;
// Also tried this to no avail:
request.Credentials = CredentialCache.DefaultNetworkCredentials;
return ProxyActionResult(request.GetResponse());
}
Here is the ProxyActionResult class:
public class ProxyActionResult : ActionResult
{
WebResponse _response;
public ProxyActionResult(WebResponse response)
{
_response = response;
}
public override void ExecuteResult(ControllerContext controllerContext)
{
HttpContextBase httpContext = controllerContext.HttpContext;
WebResponse response = _response;
// Read the byte stream from the response:
Stream responseStream = response.GetResponseStream();
// Pulled this next piece from http://www.codeproject.com/Articles/7135/Simple-HTTP-Reverse-Proxy-with-ASP-NET-and-IIS
// Seemed to fit our use case.
if ((response.ContentType.ToLower().IndexOf("html") >= 0) || (response.ContentType.ToLower().IndexOf("javascript") >= 0))// || (response.ContentType.ToLower().IndexOf("image") >= 0))
{
//If the response is HTML Content, parse it like HTML:
StreamReader readStream = new StreamReader(responseStream, Encoding.Default);
String content;
content = ParseHtmlResponse(readStream.ReadToEnd(), httpContext.Request.ApplicationPath);
//Write the updated HTML to the client(and then close the response):
httpContext.Response.Write(content);
httpContext.Response.ContentType = response.ContentType;
response.Close();
httpContext.Response.End();
}
else
{
// If the response is not HTML Content, write the stream directly to the client:
var buffer = new byte[1024];
int bytes = 0;
while ((bytes = responseStream.Read(buffer, 0, 1024)) > 0)
{
httpContext.Response.OutputStream.Write(buffer, 0, bytes);
}
// from http://www.dotnetperls.com/response-binarywrite
httpContext.Response.ContentType = response.ContentType; // Set the appropriate content type of the response stream.
// and close the stream:
response.Close();
httpContext.Response.End();
}
//throw new NotImplementedException();
}
// Debating whether we need this:
public string ParseHtmlResponse(string html, string appPath)
{
html = html.Replace("\"/", "\"" + appPath + "/");
html = html.Replace("'/", "'" + appPath + "/");
html = html.Replace("=/", "=" + appPath + "/");
return html;
}
It turns out that nothing is wrong with the reverse proxy code. The remote server was an ArcGIS OpenLayers API and it had a setting that said crossOrigin: anonymous. I commented out this setting and it worked perfectly.
Check out the documentation if you have this particular ArcGIS OpenLayers problem:
http://openlayers.org/en/v3.14.2/apidoc/ol.source.ImageWMS.html

WebMethod to return an html file

i am writing a web method to return an html file to android client
here is the code i have tried
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
string file = Server.MapPath("index.html");
return file;
}
}
and defiantly its not working, i am not sure about the return type of the method, which to choose.
do i need to convert that html file to string and then return it to client?
Your initial post returns without doing anything else at the first line:
return "Hello World";
Remove this line if you want the rest of it to work.
In order to return the contents of the file, just do a File.ReadAll:
string filePath = Server.MapPath("index.html");
string content=File.ReadAll(filePath);
return content;
EDIT:
In order to send a file to the client, you need to send the file's bytes AND set the proper headers. This has already been answered here. You need to set the content type, content disposition and content length headers. You need to write something like this:
var fileBytes=File.ReadAllBytes(filePath);
Response.Clear();
Response.ClearHeaders();
Response.ContentType = "text/html; charset=UTF-8";
Response.AddHeader("Content-Disposition", "attachment; filename=\"" + filePath + "\"");
Response.AddHeader("Content-Length", fileBytes.Length);
Response.OutputStream.Write(fileBytes, 0, fileBytes.Length);
Response.Flush();
Response.End();
Just calling Response.WriteFile isn't enough because you need to set the proper headers

Client found response content type of 'application/pdf', but expected 'text/xml'. The request failed with the error message:

Hi all I have written this code in a web-service http://www.hiqpdf.com/demo/ConvertHtmlToPdf.aspx as follows
[WebMethod]
public void convert(string strURL)
{
HtmlToPdf htmlToPdfConverter = new HtmlToPdf();
string url = strURL;
byte[] pdfBuffer = htmlToPdfConverter.ConvertUrlToMemory(url);
HttpContext.Current.Response.AddHeader("Content-Type", "application/pdf");
// let the browser know how to open the PDF document, attachment or inline, and the file name
HttpContext.Current.Response.AddHeader("Content-Disposition", String.Format("attachment; filename=ConvertHtmlPart.pdf; size={0}",
pdfBuffer.Length.ToString()));
// write the PDF buffer to HTTP response
HttpContext.Current.Response.BinaryWrite(pdfBuffer);
// call End() method of HTTP response to stop ASP.NET page processing
HttpContext.Current.Response.End();
}
I added this as a reference in my project from my local host but when trying to convert I am getting an exception as Client found response content type of 'application/pdf', but expected 'text/xml'. The request failed with the error message: can some one help me.
".ToString()" - it is not necessary
is it?

.NET, ASHX, "Server cannot append header after HTTP headers have been sent" after sending PDF Streem

I visit my ASHX file, and it outputs a PDF perfectly. If I visit the very same ASHX with a different query string (I append DateTime.Now.Ticks to the end each visit), and I get this error:
Server cannot append hader after HTTP headers have been sent.
My code is below:
copy.CloseStream = false;
document.Close();
var r = context.Response;
r.ExpiresAbsolute = DateTime.Now;
r.BufferOutput = true;
r.ContentType = "application/pdf";
r.AppendHeader("Content-Type", r.ContentType);
r.AppendHeader("Content-disposition", "inline; filename=" + context.Server.UrlEncode(formType.File_Name));
r.BinaryWrite(copyStream.ToArray());
r.StatusCode = 200;
r.End();
originalReader.Close();
copy.CloseStream = true;
copy.Close();
There is no other place in this code that headers are sent. You are seeing the entire interaction with the Response object.
I've tried to use r.Flush(); and r.End(); I've also tried not sending them if they are already there, but this causes other issues.
The problem is with r.StatusCode = 200;, which corresponds to setting the header's first line.
Since this occurs after sending payload, this is unacceptable in HTTP protocol.
You have to do that earlier.

Categories