Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Ability to configure custom JSON parser in HttpClient #48167

Copy link
Copy link
@sbr61

Description

@sbr61
Issue body actions

Which @angular/* package(s) are relevant/related to the feature request?

common

Description

There is a common use case to customize JSON parsing in HttpClient, which was already discussed in issue #21079 (closed without solution). There was also a very simple pull request #25027 favoring a JSON_PARSER injection token for customizing the parser. This was rejected in #25027 (comment) as it was considered fairly straightforward to have the user implement a JsonHttpInterceptor as suggested in https://stackblitz.com/edit/angular-ivy-nrdj5q?file=src%2Fapp%2Fcustom-json-parser.ts,src%2Fapp%2Fjson-interceptor.ts.

However, if it were really straightforward, the suggested code would not change the behavior if customized with the original JSON.parse method - but it does in the following way:

  • It does not strip away the XSSI_PREFIX from the response body.
  • It does not return null for an empty response body.
  • It changes the error handling: In case of a parse error, a SyntaxError from the JSON.parse call is raised on the observable error channel instead of an HttpErrorResponse object with its error property set to a HttpJsonParseError object { error, text } where error is the SyntaxError and text is the original response body that could not be parsed.

This means, errors can no longer be handled as described in https://angular.io/guide/http#handling-request-errors. The user may end up in changing the error handling in unrelated HTTP interceptors and for httpClient calls throughout his code.

Proposed solution

Accepting the pull request #25027 would not cause such problems.

Alternatives considered

Modifying the JsonHttpInterceptor from https://stackblitz.com/edit/angular-ivy-nrdj5q?file=src%2Fapp%2Fapp.module.ts to a more adequate solution. The user would need to look up the implementation of HttpXhrBackend (https://github.com/angular/angular/blob/main/packages/common/http/src/xhr.ts#L150) and reimplement the following code in a different way in JsonHttpInterceptor (which is error-prone rather than straightforward):

        // ok determines whether the response will be transmitted on the event or
        // error channel. Unsuccessful status codes (not 2xx) will always be errors,
        // but a successful status code can still result in an error if the user
        // asked for JSON data and the body cannot be parsed as such.
        let ok = status >= 200 && status < 300;

        // Check whether the body needs to be parsed as JSON (in many cases the browser
        // will have done that already).
        if (req.responseType === 'json' && typeof body === 'string') {
          // Save the original body, before attempting XSSI prefix stripping.
          const originalBody = body;
          body = body.replace(XSSI_PREFIX, '');
          try {
            // Attempt the parse. If it fails, a parse error should be delivered to the user.
            body = body !== '' ? JSON.parse(body) : null;
          } catch (error) {
            // Since the JSON.parse failed, it's reasonable to assume this might not have been a
            // JSON response. Restore the original body (including any XSSI prefix) to deliver
            // a better error response.
            body = originalBody;

            // If this was an error request to begin with, leave it as a string, it probably
            // just isn't JSON. Otherwise, deliver the parsing error to the user.
            if (ok) {
              // Even though the response status was 2xx, this is still an error.
              ok = false;
              // The parse error contains the text of the body that failed to parse.
              body = {error, text: body} as HttpJsonParseError;
            }
          }
        }

        if (ok) {
          // A successful response is delivered on the event stream.
          observer.next(new HttpResponse({
            body,
            headers,
            status,
            statusText,
            url: url || undefined,
          }));
          // The full body has been received and delivered, no further events
          // are possible. This request is complete.
          observer.complete();
        } else {
          // An unsuccessful request is delivered on the error channel.
          observer.error(new HttpErrorResponse({
            // The error in this case is the response body (error from the server).
            error: body,
            headers,
            status,
            statusText,
            url: url || undefined,
          }));
        }
      };
Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Labels

    P4A relatively minor issue that is not relevant to core functionsA relatively minor issue that is not relevant to core functionsarea: common/httpIssues related to HTTP and HTTP ClientIssues related to HTTP and HTTP ClientfeatureLabel used to distinguish feature request from other issuesLabel used to distinguish feature request from other issuesgood first issueLabel noting a good first issue to be worked on by a community memberLabel noting a good first issue to be worked on by a community memberopen for contributionsAn issue that is suitable for a community contributor (based on its complexity/scope).An issue that is suitable for a community contributor (based on its complexity/scope).

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.