Ability to configure custom JSON parser in HttpClient

This issue has been created since 2022-11-22.

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,
          }));
        }
      };
angular-robot[bot] wrote this answer on 2022-11-23

This feature request is now candidate for our backlog! In the next phase, the community has 60 days to upvote. If the request receives more than 20 upvotes, we'll move it to our consideration list.

You can find more details about the feature request process in our documentation.

More Details About Repo
Owner Name angular
Repo Name angular
Full Name angular/angular
Language TypeScript
Created Date 2014-09-18
Updated Date 2022-12-01
Star Count 85220
Watcher Count 3042
Fork Count 22607
Issue Count 1316

YOU MAY BE INTERESTED

Issue Title Created Date Comment Count Updated Date
feat: delete item from list 0 2022-10-06 2022-11-28
some bug about reparameterize flag in PrefixModel 1 2022-05-03 2022-11-06
How to change the size of the dataset? 0 2022-07-24 2022-11-06
Need a way in the admin page to delete a repo! 3 2021-11-15 2022-11-01
show the usage with Contentful 1 2022-03-15 2022-11-28
Does `software::scaling::context::Context` not implement `Send` for a reason? 1 2022-05-04 2022-11-05
Reduce the need to press "Enter" twice to run loaders 7 2022-06-07 2022-07-07
Multiple Order params 0 2022-07-06 2022-11-22
Incorrect picture displayed for Carracosta 1 2022-07-20 2022-11-22
The search does not work correctly 2 2021-08-13 2022-04-20
Using configuration on remote share 1 2021-09-05 2022-04-20
mi billetera oasis no abre 0 2022-02-21 2022-03-16
Access denied when single org setting is enabled 1 2022-11-04 2022-11-22
[BUG] Fix out of memory error in `sits_label_classification()` and `sits_smooth()` 0 2022-08-28 2022-09-14
attribute order 1 2021-09-10 2022-01-11
How do you restore a database that's backed up? 2 2022-05-10 2022-06-22
Please add the setting of turning off printout to the function LpProblem.solve() 6 2021-05-17 2022-10-05
registration notifications are not displayed 1 2022-04-12 2022-10-09
Setting Title Does Not Center Title 1 2022-10-24 2022-11-19
Mask not showing on Edge with Windows scalability 17 2021-09-27 2022-10-08
Why car_test has larger resolutions ? 0 2022-07-30 2022-11-17
PyTorch3D Camera Convention for Shapenet Chairs / Cars 0 2022-02-10 2022-11-17
Details of 3D Dataset 0 2021-07-16 2022-11-17
Feature request: Add the battery property to the friend view 0 2021-06-12 2022-10-25
Use a different baseURL? 2 2021-02-28 2022-11-05
Synchronous resume 😬 0 2021-02-19 2022-11-27
is there a way to limit the width of completion? 1 2022-04-21 2022-09-18
CVE-2021-28831 still present in openjdk14 images 2 2021-04-10 2022-11-22
file explorer not changing to dark mode in windows 11 dev version 42 2022-02-19 2022-10-28
decompressor can not fetch attched file width and height properties 3 2022-04-20 2022-11-29
config.php step missing in documentation? 3 2022-01-25 2022-11-18
run on windows, no connection though. 1 2021-03-09 2022-11-01
[jxl export] exported jxl image is different from the image in darktable 6 2022-10-29 2022-11-05
Wrapping long lines 0 2021-07-16 2022-01-04
Allow setting the OkHttpClient in OkHttpWebSocketTransport after initial construction 11 2021-06-18 2022-10-20
scribe-file support for Scala.js using Node.js fs 0 2022-08-03 2022-11-16
is this project still being maintained? 6 2021-10-05 2022-11-20
"Add content" should be visible at all times 4 2021-10-22 2022-08-17
Vulkan mobile: MSAA 3D causes 3D viewport to render black on phone 0 2022-10-10 2022-10-11
[v3] Configure online editor 1 2021-01-29 2022-11-22
REPL/--interactive rationalisation 2 2022-06-26 2022-11-23
An error was reported getting the regions list (Invalid enum value SERVICE_ATTACHMENTS) 7 2021-08-30 2022-10-30
[combo-box] Incorrect index announced in NVDA after scroll 0 2022-01-04 2022-09-30
ESP32 compilation problem----OSError: [Errno 8] Exec format error: 1 2022-05-28 2022-10-05
Unstable code coverage 0 2022-05-13 2022-11-17
Modifier/special keys in terminal 2 2022-11-14 2022-11-16
Test case definition generation in markdown format 0 2021-11-16 2022-11-17
Build helm test to test RBAC deployment 0 2021-11-15 2021-12-22
[Question] How to solve typescript type error when using dynamic tag? 0 2021-07-21 2022-11-28
Running multiple transformation/jobs on single spark operator simultaneously 1 2021-08-19 2022-10-30