New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpClient fails to parse an empty 200 response in IE11 #18680

Closed
alexeikostevich opened this Issue Aug 14, 2017 · 63 comments

Comments

Projects
None yet
@alexeikostevich

alexeikostevich commented Aug 14, 2017

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

The HttpClient throws an error "Http failure during parsing for ..." in the case of an empty 200 response in IE11.
The issue is not reproducible in Chrome and Firefox.
Setting a responseType option to "text" helps to resolve the issue.

Expected behavior

IE11 returns null by analogue with other browsers.

Minimal reproduction of the problem with instructions

Make an HttpClient.get request to an endpoint with the an empty file in IE11.
See this plunker for an example.

What is the motivation / use case for changing the behavior?

It is expected to have a consistent behavior across all browsers.

Environment

Angular version: 4.3.4

Browser:

  • Chrome (desktop) version XX
  • Chrome (Android) version XX
  • Chrome (iOS) version XX
  • Firefox version XX
  • Safari (desktop) version XX
  • Safari (iOS) version XX
  • IE version 11
  • Edge version XX

For Tooling issues:

  • Node version: 6.10
  • Platform: Windows
@jonathanp

This comment has been minimized.

Show comment
Hide comment
@jonathanp

jonathanp Sep 11, 2017

I get the exact same error in Chrome desktop v60.

jonathanp commented Sep 11, 2017

I get the exact same error in Chrome desktop v60.

@brentos99

This comment has been minimized.

Show comment
Hide comment
@brentos99

brentos99 Sep 19, 2017

Hi,
I'm now getting the same error in Chrome and Edge. I recently upgraded to Angular 4.4.1 and i dont remember seeing it in angular 4.3

Http failure during parsing for
HttpErrorResponse

JSON.parse Error: Unexpected input at position:0
SyntaxError

Brent

brentos99 commented Sep 19, 2017

Hi,
I'm now getting the same error in Chrome and Edge. I recently upgraded to Angular 4.4.1 and i dont remember seeing it in angular 4.3

Http failure during parsing for
HttpErrorResponse

JSON.parse Error: Unexpected input at position:0
SyntaxError

Brent

@brentos99

This comment has been minimized.

Show comment
Hide comment
@brentos99

brentos99 Sep 19, 2017

For my error, I was able to fix the problem by setting the responseType: to 'text' in the options. This stopped the HttpClient from trying to parse the empty response as a JSON object. (just realised this was mentioned in the initial bug submission.)

this.http.put(
     url,
     data,
     {
        headers: new HttpHeaders().set('Content-Type', 'application/json'),
        responseType: 'text' 
     }
  ).subscribe(  ....   );

This appears to work in Chrome and Edge.

brentos99 commented Sep 19, 2017

For my error, I was able to fix the problem by setting the responseType: to 'text' in the options. This stopped the HttpClient from trying to parse the empty response as a JSON object. (just realised this was mentioned in the initial bug submission.)

this.http.put(
     url,
     data,
     {
        headers: new HttpHeaders().set('Content-Type', 'application/json'),
        responseType: 'text' 
     }
  ).subscribe(  ....   );

This appears to work in Chrome and Edge.

@TheParad0X

This comment has been minimized.

Show comment
Hide comment
@TheParad0X

TheParad0X Sep 20, 2017

Same problem here, it used to work with 4.3.6, but with 4.4.3 reading empty responses causes the parsing error.

Using responseType: 'text' as a workaround really not ideal because the response needs to be parsed as JSON everywhere then...

IMO a better workaround is to send an empty JSON response that won't fail when parsed: "{}"

TheParad0X commented Sep 20, 2017

Same problem here, it used to work with 4.3.6, but with 4.4.3 reading empty responses causes the parsing error.

Using responseType: 'text' as a workaround really not ideal because the response needs to be parsed as JSON everywhere then...

IMO a better workaround is to send an empty JSON response that won't fail when parsed: "{}"

@thetric

This comment has been minimized.

Show comment
Hide comment
@thetric

thetric Sep 20, 2017

This issue is indeed caused by Angular 4.4: #18466

thetric commented Sep 20, 2017

This issue is indeed caused by Angular 4.4: #18466

@snakenstein

This comment has been minimized.

Show comment
Hide comment
@snakenstein

snakenstein Sep 27, 2017

Yeah, I have this problem too (after upgrading to 4.4.3). "Unexpected end of JSON input" on empty 200 response.
Chrome v.61.0.3163.100 (Official Build) (64-bit).
Any plans to fix this?

snakenstein commented Sep 27, 2017

Yeah, I have this problem too (after upgrading to 4.4.3). "Unexpected end of JSON input" on empty 200 response.
Chrome v.61.0.3163.100 (Official Build) (64-bit).
Any plans to fix this?

@thetric

This comment has been minimized.

Show comment
Hide comment
@thetric

thetric Sep 27, 2017

@snakenstein See brentos99 comment. Just add responseType:

    return this.http.post('api/groups', group, {
      responseType: 'text',
    });

thetric commented Sep 27, 2017

@snakenstein See brentos99 comment. Just add responseType:

    return this.http.post('api/groups', group, {
      responseType: 'text',
    });
@snakenstein

This comment has been minimized.

Show comment
Hide comment
@snakenstein

snakenstein Sep 27, 2017

@thetric I saw this workaround, but It is not suitable for me.
I have a .net api with a lot of methods which returns an empty 200 response. And I use swagger and NSwagStudio which generates typescript client for this api.
So, I can't change the way NSwagStudio generates code for calling http.post(...). Of course, I can manually change particular method calls. But this is not a practical solution.
Thats why I wondering if there any plans on solving this issue (not a workaround).

snakenstein commented Sep 27, 2017

@thetric I saw this workaround, but It is not suitable for me.
I have a .net api with a lot of methods which returns an empty 200 response. And I use swagger and NSwagStudio which generates typescript client for this api.
So, I can't change the way NSwagStudio generates code for calling http.post(...). Of course, I can manually change particular method calls. But this is not a practical solution.
Thats why I wondering if there any plans on solving this issue (not a workaround).

@jonathanp

This comment has been minimized.

Show comment
Hide comment
@jonathanp

jonathanp Sep 27, 2017

A workaround that did it for me was to replace 200 OK responses to POST/PUT/DELETE requests with 204 No Content.

It seems strange that this breaking change was introduced in a minor version though, so I guess it was not intentional by the Angular team.

jonathanp commented Sep 27, 2017

A workaround that did it for me was to replace 200 OK responses to POST/PUT/DELETE requests with 204 No Content.

It seems strange that this breaking change was introduced in a minor version though, so I guess it was not intentional by the Angular team.

@snakenstein

This comment has been minimized.

Show comment
Hide comment
@snakenstein

snakenstein Sep 27, 2017

I came to the same workaround as jonathanp (return 204 No Content instead of empty 200 Ok from backend side) and it works for me

snakenstein commented Sep 27, 2017

I came to the same workaround as jonathanp (return 204 No Content instead of empty 200 Ok from backend side) and it works for me

@lazarljubenovic

This comment has been minimized.

Show comment
Hide comment
@lazarljubenovic

lazarljubenovic Oct 2, 2017

You should change the title of the issue to drop the "IE11" part, since it apparently affects all browsers.

This looks like the oldest issue and it's most upvoted, so here's a list of dupes:

#19502
#19413
#19090

Also, could be related to #19195, which has a PR #19303.

lazarljubenovic commented Oct 2, 2017

You should change the title of the issue to drop the "IE11" part, since it apparently affects all browsers.

This looks like the oldest issue and it's most upvoted, so here's a list of dupes:

#19502
#19413
#19090

Also, could be related to #19195, which has a PR #19303.

@Yura13

This comment has been minimized.

Show comment
Hide comment
@Yura13

Yura13 Oct 5, 2017

I have the same issue with an empty response for post/delete.
After downgrade to 4.3.x this problem still present only in IE11(Http failure during parsing...).

Yura13 commented Oct 5, 2017

I have the same issue with an empty response for post/delete.
After downgrade to 4.3.x this problem still present only in IE11(Http failure during parsing...).

@Toub

This comment has been minimized.

Show comment
Hide comment
@Toub

Toub Oct 5, 2017

This can be fixed by setting default encoding to JSON on server-side.

@Yura13 be sure to fix the minor version, eg "4.3.6" or "~4.3.6", not "^4.3.6"

Toub commented Oct 5, 2017

This can be fixed by setting default encoding to JSON on server-side.

@Yura13 be sure to fix the minor version, eg "4.3.6" or "~4.3.6", not "^4.3.6"

@Yura13

This comment has been minimized.

Show comment
Hide comment
@Yura13

Yura13 Oct 5, 2017

@Toub I am using exactly match "4.3.6"

Yura13 commented Oct 5, 2017

@Toub I am using exactly match "4.3.6"

@hspier

This comment has been minimized.

Show comment
Hide comment
@hspier

hspier Oct 6, 2017

Small clarification here:
In 4.3.X, there is an error in IE11 only when parsing empty content (which can be the case in POST, PUT or DELETE). Setting responseType to text is a workaround but since the old Http code does work correctly, HttpClient should handle this too.
In 4.4.X, the error is in every browser and setting responseType to text is a workaround as mentioned in this (and all other related) defect. Again, HttpClient should handle this.

As @snakenstein mentioned, when using automatically generated code makes the workaround less trivial to implement, not counting the fact that we are consuming JSON payloads from various vendors and making them change their code server side just for a simple case of handling a response that is empty or "ok" is hard to justify.

hspier commented Oct 6, 2017

Small clarification here:
In 4.3.X, there is an error in IE11 only when parsing empty content (which can be the case in POST, PUT or DELETE). Setting responseType to text is a workaround but since the old Http code does work correctly, HttpClient should handle this too.
In 4.4.X, the error is in every browser and setting responseType to text is a workaround as mentioned in this (and all other related) defect. Again, HttpClient should handle this.

As @snakenstein mentioned, when using automatically generated code makes the workaround less trivial to implement, not counting the fact that we are consuming JSON payloads from various vendors and making them change their code server side just for a simple case of handling a response that is empty or "ok" is hard to justify.

@SharpKing

This comment has been minimized.

Show comment
Hide comment
@SharpKing

SharpKing Oct 12, 2017

Agree with everything @hspier said. Because of this issue I can not use HttpClient. I will continue to use the older Http and consider this "replacement" to be broken and unusable until this issue is resolved.

SharpKing commented Oct 12, 2017

Agree with everything @hspier said. Because of this issue I can not use HttpClient. I will continue to use the older Http and consider this "replacement" to be broken and unusable until this issue is resolved.

@manuel-di-iorio

This comment has been minimized.

Show comment
Hide comment
@manuel-di-iorio

manuel-di-iorio Oct 19, 2017

Same in Chrome v61

manuel-di-iorio commented Oct 19, 2017

Same in Chrome v61

@ehsaanwelcome

This comment has been minimized.

Show comment
Hide comment
@ehsaanwelcome

ehsaanwelcome Oct 21, 2017

same issue as i upgraded 4.4

ehsaanwelcome commented Oct 21, 2017

same issue as i upgraded 4.4

@duykhuongkid

This comment has been minimized.

Show comment
Hide comment
@duykhuongkid

duykhuongkid Oct 23, 2017

I have same issue when upgraded to 4.4.5

duykhuongkid commented Oct 23, 2017

I have same issue when upgraded to 4.4.5

@ikramcheb

This comment has been minimized.

Show comment
Hide comment
@ikramcheb

ikramcheb Oct 24, 2017

same issue when I upgraded to 4.4.6

ikramcheb commented Oct 24, 2017

same issue when I upgraded to 4.4.6

@duykhuongkid

This comment has been minimized.

Show comment
Hide comment
@duykhuongkid

duykhuongkid Oct 24, 2017

@ikramcheb Could you please check the content of body when get the response from Server? Does it empty?

duykhuongkid commented Oct 24, 2017

@ikramcheb Could you please check the content of body when get the response from Server? Does it empty?

@compeek

This comment has been minimized.

Show comment
Hide comment
@compeek

compeek Oct 24, 2017

I'm am also still noticing this problem in 4.4.6 (Chrome, probably the rest too). The response from the server is empty, and Angular throws an error because it can't parse the response as JSON. Setting responseType: text fixes it, but than I have to manually parse the JSON in any error responses, which is annoying.

compeek commented Oct 24, 2017

I'm am also still noticing this problem in 4.4.6 (Chrome, probably the rest too). The response from the server is empty, and Angular throws an error because it can't parse the response as JSON. Setting responseType: text fixes it, but than I have to manually parse the JSON in any error responses, which is annoying.

alxhub added a commit to alxhub/angular that referenced this issue Oct 25, 2017

fix(common): treat an empty body as null when parsing JSON in HttpClient
Previously, XhrBackend would call JSON.parse('') if the response body was
empty (a 200 status code with content-length 0). This changes the XhrBackend
to attempt the JSON parse only if the response body is non-empty. Otherwise,
the body is left as null.

Fixes #18680.
Fixes #19413.
Fixes #19502.
Fixes #19555.
@compeek

This comment has been minimized.

Show comment
Hide comment
@compeek

compeek Oct 26, 2017

For what I'm working on, it's not an option to change the response codes the server sends, so I have to tolerate 200 and 201 responses with empty bodies. I don't want to set responseType: text on the request because then I have to manually parse the JSON in any error responses, and regardless I don't want to have to make a code change for each request where this is now a problem.

For now my workaround is an HttpInterceptor that catches errors when the status code is in the 200 range and returns an HttpResponse with a null body instead (just like HttpClient does with 204 Not Content responses):

@Injectable()
export class EmptyResponseBodyErrorInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req)
      .catch((err: HttpErrorResponse) => {
        if (err.status >= 200 && err.status < 300) {
          const res = new HttpResponse({
            body: null,
            headers: err.headers,
            status: err.status,
            statusText: err.statusText,
            url: err.url
          });

          return Observable.of(res);
        } else {
          return Observable.throw(err);
        }
      });
  }
}

compeek commented Oct 26, 2017

For what I'm working on, it's not an option to change the response codes the server sends, so I have to tolerate 200 and 201 responses with empty bodies. I don't want to set responseType: text on the request because then I have to manually parse the JSON in any error responses, and regardless I don't want to have to make a code change for each request where this is now a problem.

For now my workaround is an HttpInterceptor that catches errors when the status code is in the 200 range and returns an HttpResponse with a null body instead (just like HttpClient does with 204 Not Content responses):

@Injectable()
export class EmptyResponseBodyErrorInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req)
      .catch((err: HttpErrorResponse) => {
        if (err.status >= 200 && err.status < 300) {
          const res = new HttpResponse({
            body: null,
            headers: err.headers,
            status: err.status,
            statusText: err.statusText,
            url: err.url
          });

          return Observable.of(res);
        } else {
          return Observable.throw(err);
        }
      });
  }
}

alxhub added a commit to alxhub/angular that referenced this issue Oct 26, 2017

fix(common): treat an empty body as null when parsing JSON in HttpClient
Previously, XhrBackend would call JSON.parse('') if the response body was
empty (a 200 status code with content-length 0). This changes the XhrBackend
to attempt the JSON parse only if the response body is non-empty. Otherwise,
the body is left as null.

Fixes #18680.
Fixes #19413.
Fixes #19502.
Fixes #19555.
@arpan2501

This comment has been minimized.

Show comment
Hide comment
@arpan2501

arpan2501 Nov 30, 2017

Sir what is the final response as i am also facing this error -
Shall i update my angular version OR set the responseType as "text"

I am very new to this and working out - kindly guide

arpan2501 commented Nov 30, 2017

Sir what is the final response as i am also facing this error -
Shall i update my angular version OR set the responseType as "text"

I am very new to this and working out - kindly guide

wKoza added a commit to wKoza/angular that referenced this issue Dec 2, 2017

fix(common): treat an empty body as null when parsing JSON in HttpCli…
…ent (#19958)

Previously, XhrBackend would call JSON.parse('') if the response body was
empty (a 200 status code with content-length 0). This changes the XhrBackend
to attempt the JSON parse only if the response body is non-empty. Otherwise,
the body is left as null.

Fixes #18680.
Fixes #19413.
Fixes #19502.
Fixes #19555.

PR Close #19958
@claudiuconstantin

This comment has been minimized.

Show comment
Hide comment
@claudiuconstantin

claudiuconstantin Dec 5, 2017

@arpan2501 I believe the bug-fix will be included in the next ng version. Until then you can use the responseType as "text" workaround

claudiuconstantin commented Dec 5, 2017

@arpan2501 I believe the bug-fix will be included in the next ng version. Until then you can use the responseType as "text" workaround

@dgsqrs

This comment has been minimized.

Show comment
Hide comment
@dgsqrs

dgsqrs Dec 5, 2017

It works as expected using ng 5.0.5

dgsqrs commented Dec 5, 2017

It works as expected using ng 5.0.5

@fertandil87

This comment has been minimized.

Show comment
Hide comment
@fertandil87

fertandil87 Jan 2, 2018

it's not fixed, I updated to 5.1.2

fertandil87 commented Jan 2, 2018

it's not fixed, I updated to 5.1.2

@alexandredavi

This comment has been minimized.

Show comment
Hide comment
@alexandredavi

alexandredavi Jan 10, 2018

same issue when I upgraded to 5.2.0

alexandredavi commented Jan 10, 2018

same issue when I upgraded to 5.2.0

@atheros

This comment has been minimized.

Show comment
Hide comment
@atheros

atheros Jan 15, 2018

@mrahhal's solution doesn't work for HttpErrorReponse. Here is my improvement:

import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse, HttpResponseBase } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

/**
 * Handle empty JSON responses.
 */
@Injectable()
export class EmptyResponseBodyInterceptor implements HttpInterceptor {
    constructor() {
    }

    /**
     * Intercept request and convert it to text response.
     *
     * BUG: https://github.com/angular/angular/issues/18680
     * As of 2018-01-11 it is closed and reported fixed in Angular 5, however people are still reporting it not working.
     *
     * @param {HttpRequest<any>} req
     * @param {HttpHandler} next
     * @return {Observable<HttpEvent<any>>}
     */
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        /* istanbul ignore next */
        if (req.responseType === 'json') {
            req = req.clone({responseType: 'text'});

            return next.handle(req).map(response => {
                if (response instanceof HttpResponse) {
                    response = response.clone<any>({body: JSON.parse(response.body)});
                }

                return response;
            }).catch((error: any) => {
                if (error instanceof HttpErrorResponse) {
                    if (error.error && error.error.length && error.headers.get('content-type').startsWith('application/json')) {
                        error = new HttpErrorResponse({
                            error: JSON.parse(error.error),
                            headers: error.headers,
                            status: error.status,
                            statusText: error.statusText,
                            url: error.url
                        });
                    }
                }

                return Observable.throw(error);
            });
        }

        return next.handle(req);
    }
}

atheros commented Jan 15, 2018

@mrahhal's solution doesn't work for HttpErrorReponse. Here is my improvement:

import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse, HttpResponseBase } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

/**
 * Handle empty JSON responses.
 */
@Injectable()
export class EmptyResponseBodyInterceptor implements HttpInterceptor {
    constructor() {
    }

    /**
     * Intercept request and convert it to text response.
     *
     * BUG: https://github.com/angular/angular/issues/18680
     * As of 2018-01-11 it is closed and reported fixed in Angular 5, however people are still reporting it not working.
     *
     * @param {HttpRequest<any>} req
     * @param {HttpHandler} next
     * @return {Observable<HttpEvent<any>>}
     */
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        /* istanbul ignore next */
        if (req.responseType === 'json') {
            req = req.clone({responseType: 'text'});

            return next.handle(req).map(response => {
                if (response instanceof HttpResponse) {
                    response = response.clone<any>({body: JSON.parse(response.body)});
                }

                return response;
            }).catch((error: any) => {
                if (error instanceof HttpErrorResponse) {
                    if (error.error && error.error.length && error.headers.get('content-type').startsWith('application/json')) {
                        error = new HttpErrorResponse({
                            error: JSON.parse(error.error),
                            headers: error.headers,
                            status: error.status,
                            statusText: error.statusText,
                            url: error.url
                        });
                    }
                }

                return Observable.throw(error);
            });
        }

        return next.handle(req);
    }
}
@heuristicAL

This comment has been minimized.

Show comment
Hide comment
@heuristicAL

heuristicAL Feb 8, 2018

For those who are still monitoring this: updating to 5.2.4 seems to fix the problem.
😄

heuristicAL commented Feb 8, 2018

For those who are still monitoring this: updating to 5.2.4 seems to fix the problem.
😄

@FelixFriedman

This comment has been minimized.

Show comment
Hide comment
@FelixFriedman

FelixFriedman Feb 20, 2018

I installed 5.2.5 but the issue seem to be still there. I have a http.get that returns text. but seem to be getting an error still

error: SyntaxError: Unexpected token h in JSON at position 0
at JSON.parse ()
at XMLHttp…

FelixFriedman commented Feb 20, 2018

I installed 5.2.5 but the issue seem to be still there. I have a http.get that returns text. but seem to be getting an error still

error: SyntaxError: Unexpected token h in JSON at position 0
at JSON.parse ()
at XMLHttp…

@tenderloin420

This comment has been minimized.

Show comment
Hide comment
@tenderloin420

tenderloin420 Mar 9, 2018

I was previously using the empty response body interceptor to workaround this issue. After upgrading to 5.2.6 and removing the interceptor everything works as expected.

tenderloin420 commented Mar 9, 2018

I was previously using the empty response body interceptor to workaround this issue. After upgrading to 5.2.6 and removing the interceptor everything works as expected.

@leojkwan

This comment has been minimized.

Show comment
Hide comment
@leojkwan

leojkwan Mar 18, 2018

Ran into same problem making a put request to my endpoint. I realized I was sending 200 without a response body. Sending status code 204 made angular http 5.2.8 stop complaining. This problem should not be happening even with a 200, but 204 is the correct code for empty responses.

leojkwan commented Mar 18, 2018

Ran into same problem making a put request to my endpoint. I realized I was sending 200 without a response body. Sending status code 204 made angular http 5.2.8 stop complaining. This problem should not be happening even with a 200, but 204 is the correct code for empty responses.

@lazarljubenovic

This comment has been minimized.

Show comment
Hide comment
@lazarljubenovic

lazarljubenovic Mar 18, 2018

@leojkwan Empty string is not a valid JSON. Angular by default assumes that the response is JSON and tries to parse it. If you know your endpoint returns a different type, tell that to Angular with resopnseType. In your case, text should work.

It's not an issue with Angular, it's your API: if you say it's application/json and then return an empty body, you're not tagging the payload properly.

lazarljubenovic commented Mar 18, 2018

@leojkwan Empty string is not a valid JSON. Angular by default assumes that the response is JSON and tries to parse it. If you know your endpoint returns a different type, tell that to Angular with resopnseType. In your case, text should work.

It's not an issue with Angular, it's your API: if you say it's application/json and then return an empty body, you're not tagging the payload properly.

@leojkwan

This comment has been minimized.

Show comment
Hide comment
@leojkwan

leojkwan Mar 19, 2018

@lazarljubenovic thanks for clarifying, I did try the aforementioned suggestions, that is, to tell Angular that my responseType is text, but I still ran into with http 5.2.8, I will try again and try to provide more information for the context of my issue.

To be clear, if I do not want an endpoint to send any payload back, just a success 200, I would need to set responseType to text?

leojkwan commented Mar 19, 2018

@lazarljubenovic thanks for clarifying, I did try the aforementioned suggestions, that is, to tell Angular that my responseType is text, but I still ran into with http 5.2.8, I will try again and try to provide more information for the context of my issue.

To be clear, if I do not want an endpoint to send any payload back, just a success 200, I would need to set responseType to text?

@lazarljubenovic

This comment has been minimized.

Show comment
Hide comment
@lazarljubenovic

lazarljubenovic Mar 19, 2018

[I]f I do not want an endpoint to send any payload back, just a success 200, I would need to set responseType to text?

Yes.

lazarljubenovic commented Mar 19, 2018

[I]f I do not want an endpoint to send any payload back, just a success 200, I would need to set responseType to text?

Yes.

@funkizer

This comment has been minimized.

Show comment
Hide comment
@funkizer

funkizer Mar 24, 2018

This seems to be still affecting .head() and the responseType-trick doesn't help here. Tried also sending status 204 from the server, no change. Express doesn't seem to allow a body in a head response so can't try sending back a non-empty one.

moveFile(file: NgfmFile, from: NgfmFolder, to: NgfmFolder): Observable<{ file: NgfmFile; from: NgfmFolder; to: NgfmFolder; }> {
    return this.http.head(this.getFullPath(file), 
        { observe: 'response', responseType: 'text' }
    ).pipe(
        switchMap((res: HttpResponse<any>) => {
            console.log(res.headers.keys()); // empty array
            console.log(res.headers.get('x-ngfm-hash')); // null

Chrome response headers:

Access-Control-Allow-Origin: *
Connection: keep-alive
Date: Sat, 24 Mar 2018 17:30:57 GMT
x-ngfm-hash: 35621b5c8c22f5fbab91dfd24ddbb521265223c58e5aaab33b6c5b4e7bdc33e8
X-Powered-By: Express
Angular: 5.2.9
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Edit: hmm.. I can get it to read content-type if I do res.set('x-ngfm-hash', hash).set('Content-Type', 'text/plain').send('foo') on the server, but not my custom header. What am I missing?

funkizer commented Mar 24, 2018

This seems to be still affecting .head() and the responseType-trick doesn't help here. Tried also sending status 204 from the server, no change. Express doesn't seem to allow a body in a head response so can't try sending back a non-empty one.

moveFile(file: NgfmFile, from: NgfmFolder, to: NgfmFolder): Observable<{ file: NgfmFile; from: NgfmFolder; to: NgfmFolder; }> {
    return this.http.head(this.getFullPath(file), 
        { observe: 'response', responseType: 'text' }
    ).pipe(
        switchMap((res: HttpResponse<any>) => {
            console.log(res.headers.keys()); // empty array
            console.log(res.headers.get('x-ngfm-hash')); // null

Chrome response headers:

Access-Control-Allow-Origin: *
Connection: keep-alive
Date: Sat, 24 Mar 2018 17:30:57 GMT
x-ngfm-hash: 35621b5c8c22f5fbab91dfd24ddbb521265223c58e5aaab33b6c5b4e7bdc33e8
X-Powered-By: Express
Angular: 5.2.9
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router

Edit: hmm.. I can get it to read content-type if I do res.set('x-ngfm-hash', hash).set('Content-Type', 'text/plain').send('foo') on the server, but not my custom header. What am I missing?

@funkizer

This comment has been minimized.

Show comment
Hide comment
@funkizer

funkizer Mar 24, 2018

Ok so no custom headers can be read currently, doesn't matter if it's GET or HEAD. A gloriously ugly workaround is to pass the string I need in content-type. :D

// Express:
const hash = connector.store.getHash(req.path);
res.set('content-type', 'application/' + hash).send();
// client:
const hash = res.headers.get('content-type').split(/\/|\;/)[1];

funkizer commented Mar 24, 2018

Ok so no custom headers can be read currently, doesn't matter if it's GET or HEAD. A gloriously ugly workaround is to pass the string I need in content-type. :D

// Express:
const hash = connector.store.getHash(req.path);
res.set('content-type', 'application/' + hash).send();
// client:
const hash = res.headers.get('content-type').split(/\/|\;/)[1];
@petercunha

This comment has been minimized.

Show comment
Hide comment
@petercunha

petercunha Jun 21, 2018

Latest version of Firefox. Latest version of Angular6.
Server sends text/plain. Angular interprets it as application/json. NO WAY TO FIX THIS.

WHY DO YOU DO THIS, ANGULAR?

petercunha commented Jun 21, 2018

Latest version of Firefox. Latest version of Angular6.
Server sends text/plain. Angular interprets it as application/json. NO WAY TO FIX THIS.

WHY DO YOU DO THIS, ANGULAR?

@funkizer

This comment has been minimized.

Show comment
Hide comment
@funkizer

funkizer Jun 21, 2018

@petercunha just pass responseType: 'text' in the options dude! :D

funkizer commented Jun 21, 2018

@petercunha just pass responseType: 'text' in the options dude! :D

@petercunha

This comment has been minimized.

Show comment
Hide comment
@petercunha

petercunha Jun 21, 2018

@funkizer I already tried that, it didn't fix anything.

petercunha commented Jun 21, 2018

@funkizer I already tried that, it didn't fix anything.

@gatschet

This comment has been minimized.

Show comment
Hide comment
@gatschet

gatschet Jul 16, 2018

Still an issue in Angular 6.0.6. Workaround {responseType: 'text'} can fix it.

gatschet commented Jul 16, 2018

Still an issue in Angular 6.0.6. Workaround {responseType: 'text'} can fix it.

@lazarljubenovic

This comment has been minimized.

Show comment
Hide comment
@lazarljubenovic

lazarljubenovic Jul 16, 2018

@gatschet There's nothing wrong with how Angular handles this. If your server returns 200 with an empty body, your server is not complying with the Http spec.

The spec states the following.

All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body. All other responses do include a message-body, although it MAY be of zero length.

There's a difference here. 1xx, 204 and 304 do not contain the body at all. This is handled by Angular after the fix: if the response type is 204, the HttpClient doesn't attempt to transform its body to JSON.

However, when your server is returning a 200 with an empty body (that is, an empty string -- as the spec says, of zero length), and specifies Content-Type as application/json, then your server is lying: '' is not a valid JSON.

You can try it yourself.

image

lazarljubenovic commented Jul 16, 2018

@gatschet There's nothing wrong with how Angular handles this. If your server returns 200 with an empty body, your server is not complying with the Http spec.

The spec states the following.

All 1xx (informational), 204 (no content), and 304 (not modified) responses MUST NOT include a message-body. All other responses do include a message-body, although it MAY be of zero length.

There's a difference here. 1xx, 204 and 304 do not contain the body at all. This is handled by Angular after the fix: if the response type is 204, the HttpClient doesn't attempt to transform its body to JSON.

However, when your server is returning a 200 with an empty body (that is, an empty string -- as the spec says, of zero length), and specifies Content-Type as application/json, then your server is lying: '' is not a valid JSON.

You can try it yourself.

image

@gatschet

This comment has been minimized.

Show comment
Hide comment
@gatschet

gatschet Jul 16, 2018

@lazarljubenovic I don't think my server is lying because it's defined as TEXT_PLAIN on server side

    	@POST
	@Path("/testmail/")
	@Consumes(MediaType.WILDCARD)
	@Produces(MediaType.TEXT_PLAIN)
	public Response sendTestMail() {
            return Response.ok("testmessage").build();
        }

As you can see the body is not emty ("testmessage")! The problem is, that angular is thinking this is an json response and fails to parse.

gatschet commented Jul 16, 2018

@lazarljubenovic I don't think my server is lying because it's defined as TEXT_PLAIN on server side

    	@POST
	@Path("/testmail/")
	@Consumes(MediaType.WILDCARD)
	@Produces(MediaType.TEXT_PLAIN)
	public Response sendTestMail() {
            return Response.ok("testmessage").build();
        }

As you can see the body is not emty ("testmessage")! The problem is, that angular is thinking this is an json response and fails to parse.

@lazarljubenovic

This comment has been minimized.

Show comment
Hide comment
@lazarljubenovic

lazarljubenovic Jul 16, 2018

That's not a problem, that's the feature. The default responseType is 'json'. You have to tell Angular if you want to parse body as a different type.

So, your code {responseType: 'text'} is not a "workaround" or a "fix" for a "bug", it's simply the correct way to declare the type of the body.

lazarljubenovic commented Jul 16, 2018

That's not a problem, that's the feature. The default responseType is 'json'. You have to tell Angular if you want to parse body as a different type.

So, your code {responseType: 'text'} is not a "workaround" or a "fix" for a "bug", it's simply the correct way to declare the type of the body.

@gatschet

This comment has been minimized.

Show comment
Hide comment
@gatschet

gatschet Jul 17, 2018

@lazarljubenovic In Response Header is content-type: text/plain;charset=UTF-8 as you see
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * connection: close content-type: text/plain;charset=UTF-8 date: Tue, 17 Jul 2018 05:28:09 GMT Transfer-Encoding: chunked

Angular just ignores that and you tell me this is a feature??? For real?

gatschet commented Jul 17, 2018

@lazarljubenovic In Response Header is content-type: text/plain;charset=UTF-8 as you see
HTTP/1.1 200 OK X-Powered-By: Express Access-Control-Allow-Origin: * connection: close content-type: text/plain;charset=UTF-8 date: Tue, 17 Jul 2018 05:28:09 GMT Transfer-Encoding: chunked

Angular just ignores that and you tell me this is a feature??? For real?

@mlc-mlapis

This comment has been minimized.

Show comment
Hide comment
@mlc-mlapis

mlc-mlapis Jul 17, 2018

@gatschet ... the situation is that responseType plays a key role and affects the rest of the behavior.

mlc-mlapis commented Jul 17, 2018

@gatschet ... the situation is that responseType plays a key role and affects the rest of the behavior.

@lazarljubenovic

This comment has been minimized.

Show comment
Hide comment
@lazarljubenovic

lazarljubenovic Jul 17, 2018

@gatschet If Angular used the runtime check for headers, it would not be possible to correctly type your code.

If you do not know the response type or want to use the header to differently parse the body, you can create a util function where all responses are interpreted as { responseType: 'text' }, and then your custom logic kicks in, along the lines of if (headerSaysTextPlain) { return body } else if (headerSaysJson) { return JSON.parse(body) }.

You'll, however, lose any typing information which makes this kind of useless since you'll have to include some runtime check for the correct type, and you're back to square one -- you could've just used the original responseType setting.

lazarljubenovic commented Jul 17, 2018

@gatschet If Angular used the runtime check for headers, it would not be possible to correctly type your code.

If you do not know the response type or want to use the header to differently parse the body, you can create a util function where all responses are interpreted as { responseType: 'text' }, and then your custom logic kicks in, along the lines of if (headerSaysTextPlain) { return body } else if (headerSaysJson) { return JSON.parse(body) }.

You'll, however, lose any typing information which makes this kind of useless since you'll have to include some runtime check for the correct type, and you're back to square one -- you could've just used the original responseType setting.

@trotyl

This comment has been minimized.

Show comment
Hide comment
@trotyl

trotyl Jul 17, 2018

Contributor

@lazarljubenovic I guess you were @ing the wrong person?

Contributor

trotyl commented Jul 17, 2018

@lazarljubenovic I guess you were @ing the wrong person?

@lazarljubenovic

This comment has been minimized.

Show comment
Hide comment
@lazarljubenovic

lazarljubenovic Jul 17, 2018

@trotyl Sorry, GitHub's autocomplete ain't the prettiest. 😅

lazarljubenovic commented Jul 17, 2018

@trotyl Sorry, GitHub's autocomplete ain't the prettiest. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment