33

I'm trying to make a request in Angular and I know that the HTTP response will not be in JSON but in text. However, Angular seems to be expecting a JSON response since the error is the following:

SyntaxError: Unexpected token < in JSON at position 0 at JSON.parse () at XMLHttpRequest.c

As well as

Http failure during parsing for http://localhost:9...

This is the post method:

return this.http.post(this.loginUrl, this.createLoginFormData(username, password), this.httpOptions)
  .pipe(
    tap( // Log the result or error
      data => console.log(data);
      error => console.log(error)
    )
  );

and the headers.

private httpOptions = {

  headers: new HttpHeaders({
    'Accept': 'text/html, application/xhtml+xml, */*',
    'Content-Type': 'application/x-www-form-urlencoded',
    responseType: 'text'
  },

) };

I thought that responseType: 'text' would be enough to make Angular expect a non JSON response.

Kirk Larkin
  • 60,745
  • 11
  • 150
  • 162
Amy
  • 427
  • 1
  • 4
  • 10
  • Can you post the response as a sample? – ShellNinja Jun 12 '18 at 22:42
  • Hey Amy are you trying to convert it to JSON ? Can you show full code ? And also response you getting from API ? – Ulrich Dohou Jun 12 '18 at 22:45
  • 2
    _“I thought that responseType: 'text' would be enough to make Angular expect a non JSON response.”_ - it would, if you specified it in the right place to begin with … this is not one of the HTTP headers! – CBroe Jun 18 '18 at 10:59
  • 1
    Angular 7 HttpClient - If you came here from a Google Search this linked question and comment may be relevant to you if you are trying to get a string and NOT have the automatic JSON parse assumption. https://stackoverflow.com/a/57084925/2080879 – Sql Surfer Jul 17 '19 at 22:58

6 Answers6

39

You've put responseType: 'text' in the wrong section of your httpOptions - It should sit outside of headers, like so:

private httpOptions = {
  headers: new HttpHeaders({
    'Accept': 'text/html, application/xhtml+xml, */*',
    'Content-Type': 'application/x-www-form-urlencoded'
  }),
  responseType: 'text'
};

With what you had before, a request header of responseType was being sent to the server, rather than simply having an instruction to Angular to actually treat the response as text.

Kirk Larkin
  • 60,745
  • 11
  • 150
  • 162
16

This code finally worked for me to xhr download a pdf file (Angular 6 / Laravel 5.6). The specialty for downloading a PDF file vs a text file was 'responseType': 'blob' as 'json'

showPdf(filename: String){
  this.restService.downloadFile(
     'protected/getpdf',
     {'filename': filename}
  )
}

//method from restService
public downloadFile(endpoint:String, postData:Object){

  var restService = this

  var HTTPOptions = {
     headers: new HttpHeaders({
        'Accept':'application/pdf'
     }),
     'responseType': 'blob' as 'json'
  }

  this.http.post(this.baseurl+endpoint,postData,HTTPOptions )
  .subscribe(
     res => {
        console.log(res) //do something with the blob
     },
     error => {
        console.error('download error:', error)
     }, 
     () => {
        console.log('Completed file download.')
     }
  )
}

I found the Solution through Kirk Larkins Answer (thank you a lot!) and a long angular github issue thread https://github.com/angular/angular/issues/18586#issuecomment-323216764

Martin Eckleben
  • 633
  • 7
  • 22
  • Had a json request, but response was an xml, angular expect that if you have a json in request, you should have a response as json. If you don't, it throw. Probably good pratice server side. I used your trick `'test/xml' as 'json'` to avoid having error when request is status == 200, but still get error if the status is wrong. – Ambroise Rabier Jan 18 '19 at 10:42
6

If you just want to receive a plain text. You can set Http option without a header.

this.http.get("http://localhost:3000/login",{responseType: 'text'})
.subscribe((result)=>console.log(result))
Joe
  • 531
  • 1
  • 6
  • 12
2

By default, Angular sets the response type to JSON. To override it, you can use headers and set the responseType to 'text' or a simple method would be like this

this.http.get(url, {responseType: 'text'})
Lenzman
  • 150
  • 9
1

Below given is the call from component which downloads the blob, compatible with IE and chrome:

    this.subscribe(this.reportService.downloadReport(this.reportRequest, this.password), response => {
        let blob = new Blob([response], { type: 'application/zip' });
        let fileUrl = window.URL.createObjectURL(blob);
        if (window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveOrOpenBlob(blob, fileUrl.split(':')[1] + '.zip');
        } else {
            this.reportDownloadName = fileUrl;
            window.open(fileUrl);
        }
        this.spinner = false;
        this.changeDetectorRef.markForCheck();
    },
    error => {
        this.spinner = false;
    });

Below given is the service method which specifies the response type to be 'blob'

downloadReport(reportRequest: ReportRequest, password: string): Observable<any> {
    let servicePath = `${basePath}/request/password/${password}`;
    this.httpOptions.responseType = 'blob';
    return this.endpointService.post(endpoint, servicePath, reportRequest, this.httpOptions);
}

Below is the code that makes httpClient call:

    //Make the service call:
    let obs = this.httpClient.request(method, url, options);
    //Return the observable:
    return obs;
Dilip Nannaware
  • 1,250
  • 1
  • 13
  • 22
1

I had the same problem after an angular update due http client was updated to parse the response as JSON, failing when the response doesn't contains a valid json (i.e a text or a raw html).

To avoid automatic json parsing, add the header "responseType" as a parameter in the get or post call:

this.http.get(template,{responseType:'text'})
  .subscribe(result => {

    // result contains the "treated as text content"

    });   

In general: If expect a Json result (like in a rest api):

 HttpClient.get(url) // returns Observable Json formatted

If text or raw html is expected:

HttpClient.get(url, {responseType:'text'}) // returns a string Observable 

If the return type is unexpected (You'll get the headers as well so you can parse your data properly):

HttpClient.get(url, {observe:response}) //returns Observable<HttpResponse <T>> 
Jorge Valvert
  • 736
  • 7
  • 13