cdalto
cdalto

Reputation: 857

Making a POST request that returns a PDF using Angular's HttpClient

I am working with a web API service (DocRaptor) that returns a PDF as the response. While initially working with the API, I was able generate a PDF and view the PDF on their site. I received a 200 Status Code telling me that the response was successful. However, the result of the post was still returned as an error on the Angular side using this code:

    return this.http
  .post("https://docraptor.com/docs", {
    headers: {
      "Content-Type": "application/json"
    },
    user_credentials: "WEB_API_KEY",
    doc: {
      test: true,
      name: "testDocument.pdf",
      document_content: documentContent,
      type: "pdf"
    }
  })
  .subscribe(
    res => {
      console.log(res);
    },
    err => {
      console.log("Error occured: " + err);
    }
  );

I am assuming this is because the code was expecting JSON as a result, and instead the API returned a PDF.

Angular's documentation on Requesting non-JSON Data does not go into enough detail on how to consume the response using the HttpClient (and neither does DocRaptor's for that matter). When debugging, I can't tell (using the Network tab in Chrome) that the API call is even being sent, or a response returned, using the code below. This is likely a problem in the syntax below, but the following code is an approach I've tried using the documentation I know that's available:

   return this.http
  .post("https://docraptor.com/docs", {
    responseType: "blob",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/pdf"
    },
    user_credentials: "WEB_API_KEY",
    doc: {
      test: true,
      name: "testDocument.pdf",
      document_content: documentContent,
      type: "pdf"
    }
  })
  .pipe(tap(data => this.downloadFile(data, "application/pdf")));

UPDATE:

I have rearchitected my code as suggested in the responses here. However, I am still receiving the original error, which is that I'm getting a 200 Status back from DocRaptor API, but Angular's HttpClient isn't parsing the response as a PDF. I've added in the headers to accept a PDF, and I still receive an error message that "Unexpected token % in JSON at position 0".

repl.component.ts

  generatePdf() {
    var htmlCard = this.cards.filter((card: Card) => card.language === "html")[0];
    var cssCard = this.cards.filter((card: Card) => card.language === "css")[0];

    var documentContent = this.generateCode(
      htmlCard.editorContent,
      cssCard.editorContent
    );

    this.docRaptorService.generatePdf(documentContent).subscribe(results => {
      let blob = results;
      let filename = "testDocument.pdf";
      FileSaver.saveAs(blob, filename);
    });
  }

doc-raptor.service.ts

  generatePdf(documentContent: string) {
    return this.http
      .post("https://docraptor.com/docs", {
        headers: {
          "Content-Type": "application/json",
          Accept: "application/pdf"
        },
        user_credentials: "WEB_API_KEY",
        doc: {
          test: true,
          name: "testDocument.pdf",
          document_content: documentContent,
          type: "pdf"
        },
        responseType: "blob"
      })
      .pipe(
        tap(
          data => console.log(documentContent, data),
          error => console.log(documentContent, error)
        )
      );
  }

Upvotes: 3

Views: 11177

Answers (2)

shanti
shanti

Reputation: 367

I also had a similar need where my Api returned a Pdf file object. I first tried by passing headers object with "Accept" set as "Accept": "application/pdf". But that did not work.

Then I simply set "responseType" in httpOptions as:

this._httpClient.post(apiUrl, filterParams, { responseType: 'blob' });

and it worked.

Upvotes: 1

joh04667
joh04667

Reputation: 7437

 .pipe(tap(data => this.downloadFile(data, "application/pdf")));

You have to subscribe to an Observable before it will do anything. The Angular example is returning the Observable with a tap operator 'attached' to it. Your own example is returning the Subscription, which is useless to anything calling your method.

It's the recommended design pattern for Angular services; the service creates the Observable, which is like a contract for a stream of work and data transformations, then the caller can subscribe to it and get the result of the work.

Upvotes: 0

Related Questions