2

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)
        )
      );
  }
cdalto
  • 797
  • 1
  • 15
  • 33
  • in your second example, the Observable isn't being subscribed to. Can't tell if you're subscribing elsewhere, but Observables will do nothing until subscribed to. – joh04667 Dec 11 '18 at 19:48
  • I'm unsure how `pipe` and `tap` work in relation to `subscribe`. Angular's documentation omits `subscribe` from this section, so I am assuming that `pipe` and `tap` are used in place of `subscribe` when you are trying to consume non-JSON data in the return. – cdalto Dec 11 '18 at 20:02
  • `pipe` and `tap` are operators, and are a core part of `Observable` streams. They alter the data as it comes through the stream, in this case blobbing the data into an object. Angular omits `subscribe` here because it's considered bad practice to `subscribe` to `Observables` in a service, and instead return the `Observable` to a caller. `Observables` only run when subscribed to. – joh04667 Dec 11 '18 at 20:07
  • The below link solves my issue.. https://stackoverflow.com/questions/50332447/how-to-get-pdf-through-api-in-angular-service – Karthikeyan Vellingiri Jun 10 '20 at 14:54

2 Answers2

1

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.

shanti
  • 359
  • 5
  • 20
0
 .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.

joh04667
  • 7,159
  • 27
  • 34
  • See my update to the original post. I've rearchitected the solution as you and Angular's documentation have suggested. That still doesn't fix my original problem, which is that I can't access the non-JSON response from the web API using Angular's HttpClient. – cdalto Dec 12 '18 at 16:08