Create PDF’s with ASP.NET Web Api

This blog describes how to generate PDF’s with ASP.NET Web Api using PdfSharp/MigraDoc.

 

Sample result:

http://servername/api/test.pdf?author=bert generates a pdf with  the authorname as parameter. This sample can be extended to create

complete dynamically  reports.

 

Sample code is available on https://github.com/bertt/DynamicPDF

 

Step 1: Generate new empty ASP.NET project with Visual Studio

image

Step 2: Install Microsoft.AspNet.WebApi from NuGet

image

Step 3: Install ‘PDFsharp-MigraDoc-GDI’ from NuGet:

image

 

Step 4: Change web.config

Add the following to in web.config file in the system.webServer section:

 

<modules runAllManagedModulesForAllRequests="true"/>

 

This is needed because otherwise the IIS webserver will search for a static PDF file and not a dynamic one generated by ASP.NET.

 

Step 5: Add global.asax with the following content:

 

    public class Global : System.Web.HttpApplication
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.Ignore("{resource}.axd/{*pathInfo}");

            routes.MapHttpRoute(
                name: "IdWithExt",
                routeTemplate: "api/{controller}.{ext}");

            routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

        protected void Application_Start(object sender, EventArgs e)
        {
            RegisterRoutes(RouteTable.Routes);

            var pdfFormatter = new PdfMediaTypeFormatter();
            GlobalConfiguration.Configuration.Formatters.Add(pdfFormatter);
        }
    }

In the Global.asax we register the PDF Mediatypeformatter, defined in the next step.

 

Step 6: Add a Mediatype formatter for PDF 

Add a new class ‘PdfMediaTypeFormatter’. This class overrides from MediaTypeFormatter. Important is the ‘SupportedType’, only controllers

who return the SupportedType will use this formatter. Another important parameter is the ‘object value’ in the WriteToStreamAsync method. This parameter

contains the specified PDF parameter (in our case the author name).

 

   public class PdfMediaTypeFormatter : MediaTypeFormatter
    { 
       private static readonly Type SupportedType = typeof(Person); 

       public PdfMediaTypeFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/pdf"));
            MediaTypeMappings.Add(new UriPathExtensionMapping("pdf", "application/pdf"));
        } 

       public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, TransportContext transportContext)
        { 
           var taskSource = new TaskCompletionSource<object>(); 
           try 
           { 
               var person = (Person)value; 

               var doc = PdfGenerator.CreatePdf(person.Name); 
               var ms = new MemoryStream();

                doc.Save(ms, false); 

               var bytes = ms.ToArray();
                writeStream.Write(bytes, 0, bytes.Length);
                taskSource.SetResult(null);
            } 
           catch (Exception e)
            {
                taskSource.SetException(e);
            } 
           return taskSource.Task;
        } 

       public override bool CanReadType(Type type)
        { 
           return SupportedType == type;
        } 

       public override bool CanWriteType(Type type)
        { 
           return SupportedType == type;
        }

Step 7:  Add a simple test controller class.

 

 

This controller returns a Person class, which will be rendered using the PDF Media Type.

    public class TestController:ApiController
    {
        public HttpResponseMessage GetTest(string author)
        {
            var person=new Person{Name=author};
            return Request.CreateResponse(HttpStatusCode.OK,person);
        }
    }
 

Step 8: Add a Person class

    public class Person
    {
        public String Name { get; set; }
    }

Step 9: Add code to generate a PDF (class PdfGenerator).

 

This code creates an in-memory PDF and returns it.

 

    public static class PdfGenerator
    {
        public static PdfDocument CreatePdf(string author)
        {
            var document = new Document();
            var sec = document.Sections.AddSection();
            sec.AddParagraph("Author:" + author);
            return RenderDocument(document);
        }

        private static PdfDocument RenderDocument(Document document)
        {
            var rend = new PdfDocumentRenderer {Document = document};
            rend.RenderDocument();
            return rend.PdfDocument;
        }
    }

 

Step 10: Test!

Run the project and open http://localhost:64118/api/test.pdf?author=piet

A PDF file with the specified authorname will be returned if all goes well

 

image

Advertisements

8 thoughts on “Create PDF’s with ASP.NET Web Api

  1. Hi, i follow your example, If i try to save the stream i have a perfect pdf but when i try to use the response in angularjs i just see the exact number of pages of pdf but blanks. The request is made by http post form but as i say the response is right. Maybe i’m wrong in the way i use the response data in html5 pages or i have to convert in base64 before sending. Could you help with an example of client?

    • I need to use authentication so a simple call is no accepted, now i modify your code in this way and so i manage to open the pdf :
      ….
      byte[] bytes = PdfGenerator.CreatePdf(params);
      string base64String;
      base64String = System.Convert.ToBase64String(bytes, 0, bytes.Length);
      using (var writer = new StreamWriter(writeStream))
      {
      writer.Write(base64String);
      writer.Flush();

      }
      it seems that i loose some bytes on the way maybe 🙂 In fact, lenght between the original bytes and the ones in response.
      After the use of base64 trasformation, in client (angularjs ) i simply take response and output :
      window.open(“data:application/pdf;base64, ” + response);
      and now i see the pdf as it is. I don’t know if there was a better way or why i need to encode in base64

      • sorry a mistake in my first sentece, i would observe that the length of var bytes and the one of the response in javascript were different ( more than 1k).

      • ok great it’s working 🙂 I think there should be a more simple solution (without converting to/from base64 and parsing the stream in the client) which will also work in other clients (so without the AngularJS code). But it depends on details of the security mechanism your are using.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s