Skip to content

HttpClient - incorrect ContentType for boolean #38924

@devxzero

Description

@devxzero

🐞 bug report

Affected Package

The issue is caused by package @angular/common/http

Is this a regression?

No.

Description

TLDR:

A boolean as body is seen as text/plain where is should be seen as application/json, since it is valid JSON, like numbers. See:
https://jsonlint.com/
https://www.json.org/json-en.html

Detailed description:

The post() method of HttpClient (and other request methods) accept a body method argument of type any. When the HTTP request is about to execute, HttpClient will determine the content type of body, so that it can set the Content-Type header to for example application/json or text/plain.

When an object is used as body, HttpClient will send it as application/json as expected.

When a plain number is used as body (e.g. 123, without quotes), HttpClient will also send it as application/json, because even a plain number (without curly braces and without quotes) is valid JSON.

When a TypeScript string is used as body argument (e.g. "abc"), HttpClient will send it as text/plain because it will be send without quotes in the HTTP request body, and thus isn't valid JSON. So it will use text/plain as expected.

But when boolean is used, this behavior is different. When a plain boolean (e.g. true) is used as body argument, HttpClient will send it as text/plain even though a simple plain boolean is valid JSON.

The code where this problems is caused is:

if (typeof this.body === 'object' || typeof this.body === 'number' ||

    // Arrays, objects, and numbers will be encoded as JSON.
    if (typeof this.body === 'object' || typeof this.body === 'number' || Array.isArray(this.body)) {
      return 'application/json';
    }

Plain objects, numbers, booleans and arrays values are all valid JSON code. Whereas a plain string without quotes it not valid JSON.
So plain objects, numbers, booleans and arrays values should be send as application/json and strings as text/plain. But in HttpClient this behavior unexpectedly differs for booleans.

🔬 Minimal Reproduction

https://stackblitz.com/edit/angular-ivy-umexba?file=src/app/app.component.ts

import { Component, VERSION } from '@angular/core';
import {HttpClient} from '@angular/common/http';

@Component({
  selector: 'my-app',
  template: `
    <button (click)="sendObject()">Send object</button>
    <button (click)="sendNumber()">Send number</button>
    <button (click)="sendBoolean()">Send boolean</button>
    <button (click)="sendString()">Send string</button>
  `
})
export class AppComponent  {
  constructor(private httpClient: HttpClient) {}

  sendObject() {
    // 'application/json'
    this.httpClient.post<any>("http://localhost", {x: 1}).subscribe();
  }

  sendNumber() {
    // 'application/json'
    this.httpClient.post<any>("http://localhost", 123).subscribe();
  }

  sendBoolean() {
    // `text/plain`, but should be 'application/json'
    this.httpClient.post<any>("http://localhost", true).subscribe();
  }

  sendString() {
    // `text/plain`
    this.httpClient.post<any>("http://localhost", "abc").subscribe();
  }
}

🌍 Your Environment

Angular Version:

10.1.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    P4A relatively minor issue that is not relevant to core functionsarea: common/httpIssues related to HTTP and HTTP Clientstate: confirmed

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions