Frank Metis
Frank Metis

Reputation: 1

Bug report ExtJS 7.4.0 Ext.data.reader.Json

first post on this account, how exciting. Sadly, the contents are less exciting. I have found a very simple bug in the reader.json function getResponseData.

I want to have proper error handling in my application should, unexpectedly, the server I'm querying not provide me with valid JSON. The reader has an exception event for exactly this problem. However, reader.json sets responseType on the XHR to "json".

And as detailed here, this results in a null value in response.response on the XHR:

When setting responseType to a particular value, the author should make sure that the server is actually sending a response compatible with that format. If the server returns data that is not compatible with the responseType that was set, the value of response will be null.

getResponseData wants to avoid work, and so will return responseJson, if this is an object:

if (typeof response.responseJson === 'object') {
    return response.responseJson;
}

However, in the version of Chrome I'm using typeof null evaluates to "object".

I fixed this in my application by overriding the function and replacing the first line above with if (Ext.isObject(response.responseJson)) {, but I think this should be fixed in the framework code.

I'm not really looking for an answer, but some kind of reaction for sencha would be nice.

This is posted here, because I skimmed over https://stackoverflow.blog/2019/10/30/why-sencha-is-moving-its-support-forums-to-stack-overflow/

Version information

EDIT: It appears my text was not explicit enough about my problem, so I'll add an example here. I have a store that looks similar to this:

Ext.define('myCoolStore', {
    extend: 'Ext.data.Store',
    
    constructor: function(cfg) {
        var me = this;
        cfg = cfg || {};
        me.callParent([Ext.Object.merge({
            proxy: {
                type: 'ajax',
                withCredentials: true,
                reader: {
                    type: 'json',
                    rootProperty: 'results'
                },
                listeners: {
                    exception: {
                        fn: me.onAjaxException,
                        scope: me
                    }
                }
            }
        }, cfg)]);
    },

    onAjaxException: function(proxy, response, operation, eOpts) {
        // ..
    }
});

Now, if I do myCoolStore.load(), I expect a server response like this:

{
    "success": true,
    "deprecated": false,
    "results": [{
        "id": "11730",
        "bericht_id": "167",
        "projekt_id": "1429",
        "lastedit": "2021-08-10 16:01:20",
        "lastedit_mitarbeiter_id": "182"
    }],
    "errors": [],
    "messages": [],
    "total": 1,
    "metaData": null
}

However, the server might be bugged and return something else, that is invalid JSON. For the sake of simplicity, let's say someone forgot to turn of display_errors in his PHP ini, and the answer looks like this

[..date..] [php7:notice] [pid ..pid..] [client ..ip..] PHP Notice:  Trying to get property 'myCoolProperty' of non-object in file.class.php on line 80, referer: ..myCoolSite..
{
    "success": true,
    "deprecated": false,
    "results": [{
        "id": "11730",
        "bericht_id": "167",
        "projekt_id": "1429",
        "lastedit": "2021-08-10 16:01:20",
        "lastedit_mitarbeiter_id": "182"
    }],
    "errors": [],
    "messages": [],
    "total": 1,
    "metaData": null
}

The default behaviour of the reader.json will not set an exception here, because the XHR will not be able to parse this, resulting in reponseJson being null, which will pass the typeof-check.

Upvotes: 0

Views: 399

Answers (1)

Peter Koltai
Peter Koltai

Reputation: 9734

According to RFC7159 specification, null is a valid JSON value:

A JSON value MUST be an object, array, number, or string, or one of the following three literal names:

false null true

So I think this is the intended behaviour and not a bug. If you want to customize how responses are handled you can try one of the following two methods.

1. Define a global request exception handler

Do something like this in your Application.js:

Ext.define('MyApp.Application', {
    extend: 'Ext.app.Application',
    name: 'MyApp',
    launch: function () {
        const me = this;
        Ext.Ajax.on('requestexception', me.onRequestException);
    },
    onRequestException: function (conn, response, options, eOpts) {
        // this will run on every error during request (timeout, abort, 4xx, 5xx etc.)
        // for example Ext.isEmpty(response.responseJson) will result true on null value
        // add a breakpoint here and look what is in the variables in your case
    }
});

2. Create a custom reader to intercept how response is converted

Define the reader something like this:

Ext.define('MyApp.data.reader.RestJson', {
    extend: 'Ext.data.reader.Json',
    alias: 'reader.myreader',
    getResponseData: function (response) {
        // evalutate here with debugger what you have in
        // response and do the conversion you'd like
        return something;
    }
});

And use this reader whenever you need:

Ext.create('Ext.data.Store', {
    model: 'MyModel',
    proxy: {
        type: 'rest',
        url : 'myurl',
        reader: {
            type: 'myreader'
        }
    }
});

Upvotes: 1

Related Questions