简体   繁体   中英

Using a webservice to Create PDF only works when the service is called directly

I'm a novice with webservices, so this one has me stumped. I have created a webservice that will (eventually) accept a block of html and create a pdf file from it. To keep it simple, currently I'm not passing any parameters into the service; I'm just creating a pdf document with "hello world" in it. In debug mode, when I call the service directly (ie start debugging from that asmx page), I can invoke the exportPDF() method and the results are perfect -- it creates the pdf just as I'd hoped.

The problem is when I call the webservice from a javascript, nothing happens. I've set up a breakpoint inside the service, so I know it's getting called, and as I mentioned there are no parameters being passed in, so I don't understand why it works when it's invoked directly, but not when it's invoked from a javascript call.

My javascript and webservice code is below...any help would be greatly, greatly appreciated!!

Javascript:

    function getPDF(elem) {
    var param = { html: elem.innerHTML };

    $.ajax({
        type: "POST",
        contentType: "application/json; charset=UTF-8",
        url: "../WebServices/exporting.asmx/exportPDF",
        data: "{ }",
        dataType: "json",
        success: function (response) {
        }
    })
}

WebService:

using DMC.Classes;
using NReco.PdfGenerator;
using System;
using System.IO;
using System.Web;
using System.Web.Services;
using System.Web.UI;

namespace DMC.WebServices
{
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    [System.Web.Script.Services.ScriptService]

    public class exporting : System.Web.Services.WebService
    {
        [WebMethod]
        public void exportPDF()
        {
            WriteDocument("htmlToPDF.pdf", "application/pdf", ConvertHtmlToPDF());
        }

        public byte[] ConvertHtmlToPDF()
        {
            HtmlToPdfConverter nRecohtmltoPdfObj = new HtmlToPdfConverter();
            nRecohtmltoPdfObj.Orientation = PageOrientation.Portrait;
            nRecohtmltoPdfObj.PageFooterHtml = CreatePDFFooter();
            nRecohtmltoPdfObj.CustomWkHtmlArgs = "--margin-top 35 --header-spacing 0 --margin-left 0 --margin-right 0";
            return nRecohtmltoPdfObj.GeneratePdf(CreatePDFScript() + "Hello world" + "</body></html>");
        }

        public string CreatePDFScript()
        {
            return "<html><head><style>td,th{line-height:20px;} tr { page-break-inside: avoid }</style><script>function subst() {var vars={};var x=document.location.search.substring(1).split('&');for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}" +
            "var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];for(var i in x) {var y = document.getElementsByClassName(x[i]);" +
            "for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];}}</script></head><body onload=\"subst()\">";
        }

        public string CreatePDFFooter()
        {
            return "<div style='text-align:center;font-family:Tahoma; font-size:9px;'>Page <span class=\"page\"></span> of <span class=\"topage\"></span></div>";
        }

        public void WriteDocument(string fileName, string contentType, byte[] content)
        {
            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.ContentType = contentType;
            HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + fileName);
            HttpContext.Current.Response.CacheControl = "No-cache";
            HttpContext.Current.Response.BinaryWrite(content);
            HttpContext.Current.Response.Flush();
        }
    }
}

Thanks for the response, Mason! I've been working on this and found a solution; and while I admit it's not perfect, I don't think it's too bad. From all the different material I read, I started to get the feeling that a web service is more of a "go between" for passing data and not really meant to handle functionality like posting PDF documents. It would let you get away with it when debugging and invoking it directly, but that's about it.

So instead of using a web service, I created a class object. I also created a hidden field in my html. This field gets populated with the desired div.innerHtml content via JavaScript when somebody clicks the "Export to PDF" button. Upon postback, my codebehind checks to see if the hidden field is empty and if it isn't, it calls the exportPDF function, which in turn instantiates the class object that creates/downloads the PDF. The biggest pitfall to doing it this way, and some may consider this a big pitfall, is that to read in a field in the codebehind that has html markup in it you have to turn off validation for the web page, which obviously opens up your code for malicious attacks.

Below are the highlights of my code:

Web.Config

Add requestValidationMode = "2.0" to the web.config file

<system.web>
  <httpRuntime 
    requestValidationMode="2.0" 
    targetFramework="4.5" 
  />
</system.web>

.aspx Page:

Set ValidateRequest="false" in Page reference

<%@ Page Title="Referrals" Language="C#" MasterPageFile="~/Behavior/Behavior.master" AutoEventWireup="true" CodeBehind="Referrals.aspx.cs"
Inherits="DMC.Behavior.Referrals" ClientIDMode="Static" EnableEventValidation="false" ValidateRequest="false" %>
.
.
.
    <asp:LinkButton ID="LinkButton2" runat="server" OnClientClick="createPDF();">
        <img src='../Images/icons/pdf.png'>PDF</asp:LinkButton>
.
.
.
<div id="export_pdf" class="pdfWidth_Portrait pdfSection" style="margin-top: 10px;" runat="server">
    <div class="alert-info text-center" style="margin: 0; padding: 0;">
        <table class="table table-condensed" style="margin-top: 0; padding: 30px; width: 100%;">
    .
    .
    .   
        </table>
    </div>
</div>
.
.
.
<asp:HiddenField ID="pdfData" runat="server" />
.
.
.
<script type="text/javascript">
    function createPDF() {
        document.getElementById("pdfData").value = document.getElementById("export_pdf").innerHTML;
    }
</script>

Code Behind:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            //Set the hidden pdf field to null initially
            pdfData.Value = "";
        }

        //If this field is no longer null, it means somebody is wanting to export into PDF
        if (pdfData.Value != "")
        {
            exportPDF();
        }
    }

    public void exportPDF()
    {
        string fileName = null;
        export dmc = new export();

        fileName = lblLocation.Text + " Behavior Statistics YTD " + lblDate.Text;

        dmc.exportPDF(fileName, "Portrait", pdfData.Value);

        //PDF downloaded, reset value to ""
        pdfData.Value = "";
    }

Export Class

using NReco.PdfGenerator;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace DMC.Classes
{
    public class export 
    {
        public void exportPDF(string fileName, string Orientation, string html)
        {
            HtmlToPdfConverter pdf = new HtmlToPdfConverter();

            //Remove these control characters, they interfere with the formatting of the pdf document
            html = html.Replace("\n", "");
            html = html.Replace("\t", "");
            html = html.Replace("\r", "");

            switch (Orientation)
            {
                case "Portrait":
                    pdf.Orientation = PageOrientation.Portrait;
                    break;
                case "Landscape":
                    pdf.Orientation = PageOrientation.Landscape;
                    break;
                default:
                    pdf.Orientation = PageOrientation.Default;
                    break;
            }

            //In case needed for future
            //pdf.CustomWkHtmlArgs = "--margin-top 35 --header-spacing 0 --margin-left 0 --margin-right 0";

            pdf.Margins.Top = 25;
            pdf.PageFooterHtml = createPDFFooter();

            var pdfBytes = pdf.GeneratePdf(createPDFScript() + html + "</body></html>");

            HttpContext.Current.Response.ContentType = "application/pdf";
            HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.UTF8;
            HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + fileName + ".pdf");
            HttpContext.Current.Response.BinaryWrite(pdfBytes);
            HttpContext.Current.Response.Flush();
            HttpContext.Current.Response.End();
        }

        private string createPDFScript()
        {
            return "<html><head><style>td,th{line-height:20px;} tr { page-break-inside: avoid }</style><script>function subst() {var vars={};var x=document.location.search.substring(1).split('&');for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}" +
        "var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];for(var i in x) {var y = document.getElementsByClassName(x[i]);" +
        "for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];}}</script></head><body onload=\"subst()\">";
        }

        private string createPDFFooter()
        {
            return "<div><table style='font-family:Tahoma; font-size:9px; width:100%'><tr><td style='text-align:left'>Research Dept|RR:mm:jpg</td><td style='text-align:right'>Page <span class=\"page\"></span> of <span class=\"topage\"></span></td></div>";
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM