Доступ к сложным ресурсам REST с помощью Ext JS

Я обращаюсь к службе REST, которая предоставляет эти два ресурса, родительский ресурс и дочерний ресурс:

/users
/users/{userId}/account

Таким образом, ресурс "учетная запись" не вложен в ресурс "пользователь", к нему должен быть обращен второй запрос. Существуют примеры для таких API REST, например. здесь

Я использую эти модели для сопоставления пользователей и их учетной записи с моделью данных Ext Js 4:

Пользователь

Ext.define("MyApp.model.User", {
    extend: "Ext.data.Model",
    fields: [ { name: "id", type: "string" }],
    associations: [{
            model: "MyApp.model.Account",
            name: "account",
            type: "hasOne",
            reader: "json",
            getterName: "getAccount",
            setterName: "setAccount", 
            foreignKey: "accountId"
        }
    ],
    proxy: {
        type: "rest",
        url: "/rest/users",
        reader: {
            type: "json",
            totalProperty: "total",
            root: "users"
        }
    }
});

Счет

Ext.define("MyApp.model.Account", {
    extend: "Ext.data.Model",
    fields: [ { name: "id", type: "string" }],
    belongsTo: "MyApp.model.User",
    proxy: {
        type: "rest",
        reader: { type: "json"}
    }
});

Прокси-сервер учетной записи не имеет URL-адреса (я надеялся, что это будет создано на основе модели родительского пользователя). Когда я вызываю user.getAccount(), я получаю исключение, потому что прокси отсутствует URL-адрес.

Вопрос. Есть ли способ настроить модели таким образом, чтобы Ext Js получал доступ /users/ {userId}/account БЕЗ обновления URL-адреса прокси-сервера вручную с каждым родительским пользователем?

Ответы

Ответ 1

Вы не получите то, что хотите от запасных классов Ext, вам придется немного поработать...

Из того, что я понимаю, вам нужен идентификатор пользователя для загрузки его учетной записи, а не идентификатор самой записи учетной записи. Итак, я бы сконфигурировал ассоциацию, чтобы отразить это:

associations: [{
    model: "MyApp.model.Account",
    name: "account",
    type: "hasOne",
    reader: "json",
    getterName: "getAccount",
    setterName: "setAccount",
    // foreignKey: "accountId"
    foreignKey: 'id'
}],

Большим преимуществом здесь является то, что идентификатор пользователя будет доступен прокси, когда ему будет предложено создать URL-адрес для запроса.

Теперь, чтобы построить url с нужным вам форматом, мы должны заменить метод proxy buildUrl. И, как вы уже обнаружили, вам нужен URL-адрес, чтобы получить этот метод в первую очередь.

Итак, вот как мне настроить прокси-сервер учетной записи:

proxy: {
    type: "rest",
    reader: {type: "json"},

    // Give it an URL to avoid the error
    url: '/rest/users/{}/account',

    // Replace the buildUrl method
    buildUrl: function(request) {
        var me        = this,
            operation = request.operation,
            records   = operation.records || [],
            record    = records[0],
            url       = me.getUrl(request),
            id        = record ? record.getId() : operation.id;

        // Here the part honoring your URL format
        if (me.isValidId(id)) {
            url = url.replace('{}', id);
        } else {
            throw new Error('A valid id is required');
        }

        // That enough, but we lose the cache buster param (see bellow)
        return url;

        // If we want the cache buster param (_dc=...) to be added,
        // we must call the superclass, which will read the url from
        // the request.
        request.url = url;
        return Ext.data.proxy.Rest.superclass.buildUrl.apply(this, arguments);
    }
}

В этот момент мы получаем запрос об активации прокси на URL-адресах формы:

rest/users/45/account?id=45

Это только косметический, но этот параметр запроса id меня раздражает, поэтому я также заменил бы метод buildRequest прокси-сервера следующим:

buildRequest: function(operation, callback, scope) {
    var me = this,
        params = operation.params = Ext.apply({}, operation.params, me.extraParams),
        request;

    Ext.applyIf(params, me.getParams(operation));

    // if (operation.id !== undefined && params[me.idParam] === undefined) {
    //     params[me.idParam] = operation.id;
    // }

    request = new Ext.data.Request({
        params   : params,
        action   : operation.action,
        records  : operation.records,
        operation: operation,
        url      : operation.url,
        proxy: me
    });

    request.url = me.buildUrl(request);

    operation.request = request;

    return request;
}

И здесь вы... Хотя это сработает, я не рекомендую переопределять методы таким образом, в конфигурации прокси. В реальной жизни вы должны расширить свой собственный прокси-класс от Rest, особенно если вам нужно настроить многие такие прокси-серверы... Но я надеюсь, что дал вам все ингредиенты, которые вам нужно для начала!

Ответ 2

У меня была такая же проблема, и я нашел ответ на риксо, безусловно, потрясающий. Поэтому я принял его для себя, но потом сделал некоторые изменения, так что это код, который я использую в данный момент. Преимущество заключается в том, что он позволяет вам форматировать URL-адрес службы абсолютно так, как вы предпочитаете, конкатенируя еще больше один параметр.

// Replace the buildUrl method
                buildUrl: function (request) {
                    var me = this,
                        url = me.getUrl(request);
                    var added = [];
                    for(var p in request.params)
                    {
                        if (url.indexOf('{' + p + '}') >= 0) {
                            url = url.replace('{' + p + '}', request.params[p]);
                            added.push(p);
                        }
                    }
                    for(var a in added)
                    {
                        delete request.params[added[a]];
                    }

                    // That enough, but we lose the cache buster param (see bellow)
                    return url;

                    // If we want the cache buster param (_dc=...) to be added,
                    // we must call the superclass, which will read the url from
                    // the request.
                    request.url = url;
                    return Ext.data.proxy.Rest.superclass.buildUrl.apply(this, arguments);
                }

Таким образом, вы можете использовать URL-адрес, например "/service/{param1}/{param2}/? abc = {param3}", для объекта "request.params", например

{    "param1": "value1",    "param2": "value2",    "param3": "value3" }

а также там не нужно переопределять метод "buildRequest" , поскольку любой параметр, использованный здесь, удаляется из объекта "params" и позже не конкатенируется с строкой запроса.

Я надеюсь, что это поможет, любой комментарий приветствуется!