question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Angular 2.0.0 generates invalid Content-Type header for multipart/form-data requests if a default Content-Type header is configured

See original GitHub issue

I’m submitting a … (check one with “x”)

[X] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior If defaultOptions with header Content-Type is set for http, firing a post request with a FormData body and with header Content-Type null or “” leads to the generation of a corrupt Content-Type header. The Content-Header includes a comma: content-type:, multipart/form-data; boundary=----WebKitFormBoundarylBX4sMHGfDQcsyPM.

Expected behavior Passing an empty Content-Type header triggers angular’s multipart/form-data routines if the body is a FormData object. Without the defaultOptions, the Content-Type header generation works correctly. The Content-Type header generation should not insert a comma if the header Content-Type is set in the http defaultOptions.

Reproduction of the problem See https://github.com/Nick-Triller/angular2-typescript-webpack

Relevant files:

  • app/app.component.ts
  • app/app.module.ts
  • app/http-interceptor.ts

app.module.ts:

import {NgModule}      from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {HttpModule, RequestOptions, XHRBackend, Http} from '@angular/http';

import {AppComponent}  from './app.component';
import {HttpInterceptor} from "./http-interceptor";

@NgModule({
    imports: [
        BrowserModule,
        HttpModule
    ],
    declarations: [
        AppComponent
    ],
    providers: [
        // HttpInterceptor
        {
            provide: Http,
            useFactory:
                (xhrBackend: XHRBackend, requestOptions: RequestOptions) =>
                    new HttpInterceptor(xhrBackend, requestOptions),
            deps: [XHRBackend, RequestOptions]
        }
    ],
    bootstrap: [
        AppComponent
    ]
})

export class AppModule {}

app.component.ts:

/// <reference path="../typings/index.d.ts" />

import {Component} from '@angular/core';
import {Http, RequestOptionsArgs, RequestOptions, Headers} from "@angular/http";

@Component({
    selector: 'my-app',
    template: `
<input type="file" (change)="filePicked($event)" style="width: 100%">
<button (click)="reproduceBug()">Reproduce bug</button>
`
})

export class AppComponent {
    file: File;

    constructor(private http: Http) {
    }

    reproduceBug() {
        const url = "url";

        let fileReader: FileReader = new FileReader();
        let body = new FormData();

        fileReader.addEventListener("load", $event => {
            body.append("file", fileReader.result);
            let options: RequestOptionsArgs = new RequestOptions();
            options.headers = new Headers();
            options.headers.append("Content-Type", "");

            return this.http.post(url, body, options)
                .toPromise();
        });

        // async
        fileReader.readAsBinaryString(this.file);
    }

    filePicked($event) {
        this.file = $event.target.files[0];
    }
}

http-interceptor.ts

import {ConnectionBackend, Http, Request, RequestOptionsArgs, Response, RequestOptions, Headers} from "@angular/http";
import {Injectable} from "@angular/core";

import "./rxjs-extensions";
import {Observable} from "rxjs/Observable";

/**
 * Sets RequestOptions
 */
@Injectable()
export class HttpInterceptor extends Http {

    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
        super(backend, defaultOptions);
        // defaultOptions get merged into other options that are passed into request functions
        defaultOptions.headers = new Headers();
        defaultOptions.headers.append('Content-Type', 'application/json');
    }
}

What is the motivation / use case for changing the behavior? It should be possible to set default Content-Type header and still send multipart/form-data requests.

Please tell us about your environment: Windows 7, Webstorm IDE

Angular version: Angular 2.0.0

Browser: Tested with Firefox 43.0.1 (NO corrupt header) and with Chrome 53.0.2785.116 m (64-bit) with disabled extensions and plugins (corrupt header).

Language: Typescript 1.8.10

Node (for AoT issues): No AoT compilation

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Reactions:2
  • Comments:15 (6 by maintainers)

github_iconTop GitHub Comments

24reactions
darincardincommented, Mar 31, 2017

I had the same problem! you MUST delete the content type, like this: request.headers.delete(“Content-Type”);

3reactions
Nick-Trillercommented, Sep 23, 2016

Plunker: https://plnkr.co/edit/Fzo5uD?p=preview

As stated before, the Bug occurs with Chrome 53.0.2785.116 m (64-bit), but not with Firefox 47.0.1. Other browsers or browser versions were not tested.

To reproduce the bug, open the chrome dev tools network tab, pick a file and click the “reproduce bug” button. Check the content-type header for the mutlipart/form-data request. It starts with a comma.

Edit: Changed plunker link

Read more comments on GitHub >

github_iconTop Results From Across the Web

Angular 2 doesn't set Content-Type to 'multipart/form-data' ...
I look into Chrome's Network tab and I can see that my Request headers are wrong: Content-Type: application/json.
Read more >
Request
When doing so, content-type and content-length are preserved in the PUT headers. request.get(' ...
Read more >
API — Flask Documentation (2.2.x)
A data structure of functions to call at the end of each request, ... applied in order to obtain the media-type referenced by...
Read more >
dio | Dart Package
A powerful Http client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc.
Read more >
Changelog
testIsolation=false caused invalid configuration validation when running ... The Sec-Fetch-Dest metadata request header is now set to document when the ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found