使用Ext JS访问复杂的REST资源

时间:2013-07-25 09:43:20

标签: rest extjs extjs4 parent-child

我正在访问一个REST服务,它公开这两个资源,一个父资源和一个子资源:

/users
/users/{userId}/account

因此资源“account”不嵌套在资源“user”中,它必须由第二个请求访问。这些REST API有一些例子,例如here

我使用这些模型将用户及其帐户映射到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"}
    }
});

帐户代理没有网址(我希望这会根据父用户模型创建)。当我调用user.getAccount()时,我得到一个异常,因为代理缺少url。

问题是否有某种方法来设置模型,以便Ext Js将访问/ users / {userId} / account而无需使用每个父userId手动更新帐户代理URL?< / em>的

2 个答案:

答案 0 :(得分:26)

你不会从库存Ext类中获得你想要的东西,你将不得不变得有点脏......

根据我的理解,您需要用户的ID来加载其帐户,而不是帐户记录本身的ID。所以,我会配置关联来反映:

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

这里的一大优势是,当系统要求用户ID为请求构建网址时,用户ID可用。

现在,为了使用您需要的格式构建网址,我们必须替换代理的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's the part honoring your URL format
        if (me.isValidId(id)) {
            url = url.replace('{}', id);
        } else {
            throw new Error('A valid id is required');
        }

        // That's 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);
    }
}

此时,我们最终会在表单的网址上发出代理触发请求:

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中扩展你自己的代理类,特别是如果你需要配置许多这样的代理......但我希望我给你开始所需的所有成分!

答案 1 :(得分:5)

我遇到了同样的问题,我发现rixo的答案绝对令人惊讶。因此我自己采用了它,但后来我做了一些修改,所以这是我目前正在使用的代码。 它的优点是,它允许您根据需要绝对格式化服务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's 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);
                }

通过这种方式,您可以使用&#34; / service / {param1} / {param2} /?abc = {param3} &#34;给出了一个&#34; request.params&#34;像

这样的对象

{     &#34; param1&#34;:&#34; value1&#34;,     &#34; param2&#34;:&#34; value2&#34;,     &#34; param3&#34;:&#34; value3&#34; }

并且无需覆盖&#34; buildRequest&#34;方法,因为此处使用的任何参数都会从&#34; params&#34;中删除。对象,以后不会与查询字符串连接。

我希望这有帮助,欢迎任何评论!