Reputation: 2416
I've seen a number of examples of returning a PDF (or other file type) using Web API from a file that was stored on disk. However, in my case, I'm attempting to generate the document on the fly using SSRS's URL Access. Here is an example of the URL:
https://reporting.mydomain.biz/_layouts/ReportServer/RSViewerPage.aspx?rv:RelativeReportUrl=%2fquest%2fQUEST%2520Reports%2fReview.rdl&rp%3aReview=220
I've tried a number of approaches but most of them were from way back in 2012 and relevant to ASP.Net MVC
such as:
public async Task<FileStreamResult> GenerateReport()
{
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri("http://domainORipaddress"), "NTLM", new NetworkCredential(
ConfigurationManager.AppSettings["username"],
ConfigurationManager.AppSettings["password"]
));
Stream report = null;
using (var httpClient = new HttpClient(new HttpClientHandler { Credentials = credentialCache }))
{
httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
report = await httpClient.GetStreamAsync("reportUrl");
}
var contentDisposition = new ContentDisposition
{
FileName = "Report.pdf",
Inline = false
};
Response.AppendHeader("Content-Disposition", contentDisposition.ToString());
return File(report, "application/pdf");
//or use
//Response.AppendHeader("Content-Disposition", String.Format("attachment;filename=\"{0}\"", reportName));
//return File(reportPath, MediaTypeNames.Application.Pdf);
}
This example code came from this website: http://webstackoflove.com/sql-server-reporting-service-with-asp-net-mvc/
Here's a similar question but based on ASP.NET
: Reporting services: Get the PDF of a generated report
I tried to use this code above returning a HttpResponseMessage
but I'm getting back a file with no data.
Here's what I'm using now and it's returning a file with 0 KB:
public HttpResponseMessage PrintQualityReview([FromUri] int reviewId)
{
var reportUrl = String.Format("https://reporting.mydomain.biz/quest/_vti_bin/reportserver?https://reporting.mydomain.biz/quest/QUEST%20Reports/{0}Review.rdl&rs:Format=PDF&Review={1}",
ConfigurationManager.AppSettings["ReportingServiceDatabase"],
reviewId);
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri("https://reporting.mydomain.biz"), "NTLM", new NetworkCredential(
ConfigurationManager.AppSettings["ReportingServiceAccount"],
ConfigurationManager.AppSettings["ReportingServiceAccountPwd"]
));
MemoryStream mStream = new MemoryStream();
using (WebClient wc = new WebClient())
{
wc.Credentials = credentialCache;
using (Stream stream = wc.OpenRead(reportUrl))
{
stream.CopyTo(mStream);
}
}
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(mStream);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentDisposition.FileName = "Report.pdf";
return response;
}
I'm wondering how to do his with ASP.Net Web API
?
Upvotes: 0
Views: 5308
Reputation: 2416
Unfortunately, simply adding content-length to the header didn't do the trick. The trick was to have the AngularJS $http service return an ArrayBuffer response.
Web API
[System.Web.Http.HttpGet]
[System.Web.Http.Route("api/print/qualityreview/{reviewId:int}")]
public HttpResponseMessage PrintQualityReview([FromUri] int reviewId)
{
logger.Info("Printing quality review for QR Id {0}", reviewId);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
var reportUrl = String.Format("https://example.com/quest/_vti_bin/reportserver?https://example.com/quest/QUEST%20Reports/{0}Review.rdl&rs:Format=PDF&Review={1}",
ConfigurationManager.AppSettings["ReportingServiceDatabase"],
reviewId);
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri("https://example.com"), "NTLM", new NetworkCredential(
ConfigurationManager.AppSettings["ReportingServiceAccount"],
ConfigurationManager.AppSettings["ReportingServiceAccountPwd"]
));
using (WebClient webClient = new WebClient())
{
webClient.Credentials = credentialCache;
var reportStream = webClient.OpenRead(reportUrl);
using (MemoryStream memoryStream = new MemoryStream())
{
reportStream.CopyTo(memoryStream);
response.Content = new ByteArrayContent(memoryStream.ToArray()); //new StreamContent(reportStream);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
//response.Content.Headers.ContentLength = reportStream.Length;
response.Content.Headers.ContentDisposition.FileName = "QualityReview.pdf";
response.StatusCode = HttpStatusCode.OK;
return response;
}
}
}
}
AngularJS
In the controller:
/**
* Prints the quality review
*/
function printQualityReview() {
questAPIService.printQualityReview(reviewId)
.then(function (response) {
var outputFilename = vm.reviewee.lastName + ', ' + vm.reviewee.firstName + ' ' + $filter('date')(vm.review.reviewDate, 'MMddyyyy') + '.pdf';
var blob = new Blob([response.data], { type: 'application/pdf' });
saveAs(blob, outputFilename);
}, function (error) {
$log.error('Printing failed: ', error);
toastr.error('There was an error attempting to print the quality review.');
});
}
The service call:
/**
* Prints a quality review
*
* @param {number} reviewId Id of the quality review to print
* @returns {Promise} Promise including content and response data
*/
function printQualityReview(reviewId) {
return $http({
url: 'api/print/qualityReview/' + reviewId,
method: 'GET',
responseType: 'arraybuffer'
});
}
The trick was to make sure to return a byte array in the response content and set the response type as ArrayBuffer when calling the API method.
Upvotes: 0
Reputation: 247451
The reason you are getting 0 bytes is because you are not setting the ContentLength
of the response header.
Here is your updated code
public HttpResponseMessage PrintQualityReview([FromUri] int reviewId) {
var reportUrl = String.Format("https://reporting.mydomain.biz/quest/_vti_bin/reportserver?https://reporting.mydomain.biz/quest/QUEST%20Reports/{0}Review.rdl&rs:Format=PDF&Review={1}",
ConfigurationManager.AppSettings["ReportingServiceDatabase"],
reviewId);
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri("https://reporting.mydomain.biz"), "NTLM", new NetworkCredential(
ConfigurationManager.AppSettings["ReportingServiceAccount"],
ConfigurationManager.AppSettings["ReportingServiceAccountPwd"]
));
MemoryStream mStream = new MemoryStream();
using (WebClient wc = new WebClient()) {
wc.Credentials = credentialCache;
using (Stream stream = wc.OpenRead(reportUrl)) {
stream.CopyTo(mStream);
}
}
var contentLength = mStream.Length; //content length for header
var contentType = new MediaTypeHeaderValue("application/pdf");
var contentDispositionValue = "attachment; filename=Report.pdf";
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(mStream);
response.Content.Headers.ContentType = contentType;
response.Content.Headers.ContentLength = contentLength;
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse(contentDispositionValue , out contentDisposition)) {
response.Content.Headers.ContentDisposition = contentDisposition;
}
return response;
}
Upvotes: 0