76

I trying to make http request to the spring rest API.. API returns a string value ("success" or "fail")... but I dont know how to set the response type as string value while making call to the API..its throwing error as Backend returned code 200, body was: [object Object]

My angular code is like below,

order.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { ProductSearch } from '../_models/product-search';
import { ProductView } from '../_models/product-view';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ErrorHandlerService } from './error-handler.service';
import { Category } from '../_models/category';


@Injectable({
  providedIn: 'root'
})
export class OrderService {

  constructor(private http: HttpClient, private errorHandlerService: ErrorHandlerService) { }

addToCart(productId: number, quantity: number): Observable<any> {
    const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
    console.log("--------order.service.ts----------addToCart()-------productId:"+productId+":------quantity:"+quantity);
     return this.http.post<any>('http://localhost:8080/order/addtocart', 
              { dealerId: 13, createdBy: "-1", productId: productId, quantity: quantity}, 
              {headers: headers})
              .pipe(catchError(this.errorHandlerService.handleError));
    }
}

error-handler.service.ts

import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';

import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ErrorHandlerService {

  constructor() { }

  public handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError(
      'Something bad happened; please try again later.');
  };

}
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
rahul shalgar
  • 1,198
  • 5
  • 24
  • 39

9 Answers9

100

You should not use those headers, the headers determine what kind of type you are sending, and you are clearly sending an object, which means, JSON.

Instead you should set the option responseType to text:

addToCart(productId: number, quantity: number): Observable<any> {
  const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');

  return this.http.post(
    'http://localhost:8080/order/addtocart', 
    { dealerId: 13, createdBy: "-1", productId, quantity }, 
    { headers, responseType: 'text'}
  ).pipe(catchError(this.errorHandlerService.handleError));
}
Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • 31
    its getting compile error...[ts] Argument of type '{ responseType: "text"; }' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: Ht...'. Types of property 'responseType' are incompatible. Type '"text"' is not assignable to type '"json"'. – rahul shalgar Jun 11 '18 at 13:25
  • @rahulshalgar I've updated my answer. Change the `` to `` – Poul Kruijt Jun 11 '18 at 13:33
  • 6
    @rahulshalgar what if you remove the generic type annotation? I see no reason why it should not work – Poul Kruijt Jun 11 '18 at 13:38
  • sorry.. its was my mistake.. but I am now getting another error error-handler.service.ts:21 Backend returned code 415, body was: {"timestamp":"2018-06-11T13:45:32.875+0000","status":415,"error":"Unsupported Media Type","message":"Content type 'application/json' not supported","path":"/order/addtocart"} – rahul shalgar Jun 11 '18 at 13:46
  • @rahulshalgar apparently the backend you are connecting to does not like the type i made it sent. You should either change your backend to accept JSON requests (which will make your life so much easier, because you are sending objects). Or put the headers backs. (I'll update my answer for the latter) – Poul Kruijt Jun 11 '18 at 13:50
89

To get rid of error:

Type '"text"' is not assignable to type '"json"'.

Read the Angular HTTP guide and use

responseType: 'text' as const

import { HttpClient, HttpHeaders } from '@angular/common/http';
.....
 return this.http
        .post<string>(
            this.baseUrl + '/Tickets/getTicket',
            JSON.stringify(value),
        { headers, responseType: 'text' as const }
        )
        .map(res => {
            return res;
        })
        .catch(this.handleError);
devmiles.com
  • 9,895
  • 5
  • 31
  • 47
ozanmut
  • 2,898
  • 26
  • 22
  • 11
    I can not find what is doing here 'text' as 'json'. Could you add a name of that construction, or even better- explain that part? – Krystian Dec 23 '19 at 13:25
  • 3
    Not sure what 'text' as 'json' does but it is not a clean solution as stated here: https://github.com/angular/angular/issues/18672#issuecomment-455435341 – adrianko Jun 25 '20 at 07:41
  • 4
    Actually, `responseType` only allows 'json' value. Typescript knows that. So writing `'text' as 'json'` means "I give you 'text' value, but for type-checking, consider I gave you 'json'". So typescript won't complain. This only allows you to "lie" to typescript. – Random Nov 12 '20 at 21:58
  • @Random There's no need to 'lie' typescript. If you think you have to do that, you're using it wrong. The accepted answer is correct. – Michael Westcott Feb 18 '21 at 05:36
  • 2
    @MichaelWestcott That's not the point of my comment, but you're right. I was just explaining what does `'text' as 'json'` means, as asked by previous comments. – Random Feb 18 '21 at 09:10
  • As pointed out in the link from @adrianko, `'text' as const` is not needed, as long 1.) you don't use the generified version of the method - i.e. just leave off the `` and 2.) you use an inline object literal for the options object (as was done in this example). For me, the big thing I was missing was #1. I was still specifying `` on my method call which was resolving to a different method signature which triggered the error. See the "OBSERVE AND RESPONSE TYPES" header here: https://angular.io/guide/http#requesting-data-from-a-server – matt forsythe Jun 30 '22 at 20:15
13

On your backEnd, you should add:

@RequestMapping(value="/blabla",  produces="text/plain" , method = RequestMethod.GET)

On the frontEnd (Service):

methodBlabla() 
{
  const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
  return this.http.get(this.url,{ headers, responseType: 'text'});
}
Saad Joudi
  • 595
  • 4
  • 5
12

To fix the compiler error, remove the generic type argument from the post method call.

Angular doesn't want this generic type argument, because it should always return a string when responseType is "text".

DO THIS

return this.http.post('example', postBody, {
  responseType: 'text'
});

NOT THIS

return this.http.post<any>('example', postBody, {
  responseType: 'text'
});

The error appears because the post method signature does not contain a generic type argument when responseType: 'text.

See the different method signatures below:

With responseType: 'json' (the default)

post<T>(url: string, body: any | null, options?: {
    ...
    responseType?: 'json';
    ...
}): Observable<T>;

With responseType: 'text'

post(url: string, body: any | null, options: {
    ...
    responseType: 'text';
    ...
}): Observable<string>;

Notice the generic type argument only exists for type 'json'. Remove it to fix the error.

Ryan
  • 550
  • 4
  • 9
9

Use like below:

yourFunc(input: any):Observable<string> {
 var requestHeader = { headers: new HttpHeaders({ 'Content-Type': 'text/plain', 'No-Auth': 'False' })};
 const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
 return this.http.post<string>(this.yourBaseApi+ '/do-api', input, { headers, responseType: 'text' as 'json'  });
}
Abolfazl Roshanzamir
  • 12,730
  • 5
  • 63
  • 79
Abdus Salam Azad
  • 5,087
  • 46
  • 35
8

For me this way worked. Like requestOptions as object

 returnObservable(): Observable<any> {
    const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
    const requestOptions: Object = {
      headers: headers,
      responseType: 'text'
    }
    return this.http.get<any>(this.streamURL , requestOptions);
 }
Amir
  • 1,066
  • 1
  • 13
  • 26
  • The best approach is to remove generic type argument. Angular does not want this type argument when "responseType" is text, because it's always going to return a string! – Ryan May 23 '23 at 14:59
  • esto si que me funciono de maravilla, muchas gracias – cdcaiza Jul 28 '23 at 14:16
2

The default assumption of the HttpClient is 'json' responseType. If you want to change it to 'text', you should do it like so:

  public signIn(dto: UserCredentialsDto): Promise<string> {
    return this.http.post<string>(
      `${this.url}/userCredentials/signIn`, dto, { responseType: 'text' as 'json'}).toPromise();
  }
  • This is without header in case the API return text response: this.http.post(URL,department,{ responseType: 'text' as 'json' } – aswininayak Jan 25 '22 at 16:48
  • It's simpler to omit the generic argument. Using "as 'json'" to force Angular to call the different method signature is not the best approach. – Ryan Mar 07 '23 at 00:04
1

By Default angular return responseType as Json, but we can configure below types according to your requirement.

responseType: 'arraybuffer'|'blob'|'json'|'text'

Ex:

this.http.post(
    'http://localhost:8080/order/addtocart', 
    { dealerId: 13, createdBy: "-1", productId, quantity }, 
    { headers, responseType: 'text'});
Pradeep
  • 1,192
  • 2
  • 12
  • 30
0

Have you tried not setting the responseType and just type casting the response?

This is what worked for me:

/**
 * Client for consuming recordings HTTP API endpoint.
 */
@Injectable({
  providedIn: 'root'
})
export class DownloadUrlClientService {
  private _log = Log.create('DownloadUrlClientService');


  constructor(
    private _http: HttpClient,
  ) {}

  private async _getUrl(url: string): Promise<string> {
    const httpOptions = {headers: new HttpHeaders({'auth': 'false'})};
    // const httpOptions = {headers: new HttpHeaders({'auth': 'false'}), responseType: 'text'};
    const res = await (this._http.get(url, httpOptions) as Observable<string>).toPromise();
    // const res = await (this._http.get(url, httpOptions)).toPromise();
    return res;
  }
}
Jason Hu
  • 31
  • 2