Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: HttpClient error with empty 200/201 response (always calls JSON.parse(""))

Tags:

json

angular

While using Angular HttpClient post, it seems the default is to treat the response as JSON string. This caused error even for 201 response when the response body is empty as empty string "" fails JSON.parse().

The solution is to specify responseType: "text" as additional option, so that empty body is not treated as error.

However, the API endpoint does return error description in JSON when the request fails (i.e. empty on success, JSON on error).

How do you construct the HttpClient post so that I can get the error message object back when it fails and success does not count as error?

e.g.:

.subscribe(() => {
    // do something for success, no return object as the body is empty
  , error => {
    // do something with the returned error object
    // right now success is still counted as an error due to the issue above
  }
);
like image 960
user1589188 Avatar asked Aug 08 '19 02:08

user1589188


1 Answers

A server that returns response code 200 or 201 with an empty response body and Content-Type specified as application/json is misconfigured, since an empty string is not valid JSON.

As the OP indicated, specifying responseType: "text" fixes the error, since the empty body is not parsed as JSON.

A workaround is to proceed with responseType: "text" and check if the response body is empty. If the response body is not empty, then call JSON.parse(response).

Example

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';


type HttpOptions = {
  headers?: HttpHeaders | { [header: string]: string | string[]; };
  observe?: "body"; params?: HttpParams | { [param: string]: string | string[]; };
  reportProgress?: boolean; responseType?: "json" /* or "text" as "json" */;
  withCredentials?: boolean;
}

let get_http_options_text = (): HttpOptions => {
  return {
    headers: {'Content-Type': 'text/plain'},
    observe: "body",
    responseType: "text" as "json",  // @see https://github.com/angular/angular/issues/18586
    withCredentials: true
  }
}


@Injectable()
export class MyHttpService {

  constructor(private http: HttpClient) {}

  public post_body_as_string(url: string, body: any, http_params: HttpParams = null):
    Observable<any> {

    let options = get_http_options_text();
    if (http_params != null) {
      options['params'] = http_params;
    }

    return this.http.post<string>(url, body, options).pipe(
      map(response => {
        if (response !== '') {
          return JSON.parse(response);
        } else {
          return {}
        }
      })
    );
  }
}
like image 134
Christopher Peisert Avatar answered Sep 28 '22 07:09

Christopher Peisert