【问题标题】:Why is data variable undefined in resource query transform为什么资源查询转换中未定义数据变量
【发布时间】:2015-10-04 07:46:05
【问题描述】:

我是 Angular 的新手......所以我确定我做错了什么。花了几个小时试图寻找解决方案,但 angularjs 文档几乎没用……而且几乎每个示例都试图将其设置为全局级别。

我正在尝试将复杂的 javascript 对象作为查询参数传递。该对象具有一个数组属性,因此我需要展平(专有名词??)该对象,以便 MVC 绑定可以正确实例化模型。

我试图传递的对象类似于

newRequest = {
   SearchTerms: 'error',
   PageSize: 25,
   ...
   Facets: [
       { Field: 'createdBy', Value: 'Joe' },
       { Field: 'createdBy', Value: 'Mary' }
   ]
}

我声明我的资源如下

(function () {
    "use strict";

    angular
        .module('common.services')
        .factory('searchResource', ['$resource', 'appSettings', searchResource]);

    function searchResource($resource, appSettings) {
        return $resource(appSettings.searchPath, null, {
            query: {
                method: 'GET',
                transformRequest: function (data, headersGetter) {
                    if (data === undefined) {   // this is always true
                        return data;
                    }

                    return $.param(data);
                }
            }
        });
    }
}());

我正在使用它

vm.executeSearch = function () {
    searchResource.query(
        newRequest,
        function (data) {
            vm.response = data;
            vm.request = data.Request;
        }
    );
}

transformRequest 函数正在被调用...并且headersGetter 有一个值。

附加信息

按照建议,我改变了方向,而不是使用资源,而是通过工厂提供服务。结果相同...data 参数未定义。这是新代码。

(function () {
    "use strict";

    angular
        .module('common.services')
        .factory('searchProvider', ['$http', 'appSettings', searchProvider]);

    function searchProvider($http, appSettings) {
        return {
            query: function (request, callback) {
                $http({
                    url: appSettings.searchPath,
                    method: 'GET',
                    params: request,
                    transformRequest: function (data, headersGetter) {
                        if (data == undefined) {
                            return data;
                        }

                        return $.param(data);
                    }
                })
                .success(function (data) {
                    callback(data);
                });
            }
        }
    }


}());

searchProvider.query(
    newRequest,
    function (data) {
        console.log(data);

        vm.response = data;
        vm.request = data.Request;
    }
);

但问题是dataundefined!我知道newRequest 对象是有效的,因为呼叫结束了。只是有一个格式不正确的网址。我哪里做错了?

【问题讨论】:

标签: angularjs angular-resource


【解决方案1】:

问题是它是一个 GET 请求,而 ngResource 不会接受 GET 请求中的data

https://github.com/angular/angular.js/blob/master/src/ngResource/resource.js#L526

var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);

它使用hasBody来判断是否设置data参数。

因此,此时您最好的选择是转而使用 $http,因为 ngResource 用于更多 RESTful api,或者将您的 API 切换为 POST 而不是 GET。

编辑

还有一件事:你不需要使用if (data === undefined) 什么时候可以做:if(!data),意思是一样的。

Javascript thruthiness : https://developer.mozilla.org/en-US/docs/Glossary/Truthy

回复附加信息

您仍然在数据中未定义,因为您没有将数据设置为任何内容,而是在设置参数。但是一旦你到了这里,你就不需要转换请求了。

你可以这样做

$http({
  ...
  params: $.param(request),
  ...
})

【讨论】:

  • 谢谢@sirrocco。我将在周末对其进行测试,并在周一回复/标记答案。
  • 是的。我知道……那段特定的代码是从另一个标记为正确答案的代码中复制/粘贴的。在我确认我最初的目标有效之前不想进行更改。话虽这么说......我通过工厂将我的资源转换为服务(提供者??),我得到了同样的东西——数据是未定义的。将使用修改后的代码更新我的原始问题以供审核。
  • 是的,仍然为空,因为您发送的是 params 而不是 data。但是此时您不需要转换,只需在发出请求之前转换参数即可。
【解决方案2】:

有一些额外的信息可以帮助我们找到问题的根源。我认为没有它们我们也能过得去,但万一我们遇到困难,我想知道:

  • 浏览器控制台是否有错误?
  • 搜索端点 URL appSettings.searchPath 是什么样的?
    • 是否包含以冒号为前缀的参数?
  • 您的端点期待什么样的请求?
  • 除了 Angular 之外,您还包括 jQuery 还是 jQuery Lite?

为了让事情顺利进行,我假设您实际上没有遇到任何控制台错误,并且您已经包含了 Angular 和 jQuery 的必要库。

如果是这样的话,这里有一些花絮可能会有所帮助。


当您使用 $request 工厂时,您会生成一种抽象 HTTP 请求的接口。由于您将其用于“搜索”,因此我们将其称为您的搜索界面。

如果您通过搜索界面的 query 方法执行 GET 请求,您可以传入任何对象或数组,它会根据该参数构建一个带有查询的 URL。

例如,假设您按原样传递了 newRequest 变量,没有进行任何处理。这最终会产生一个如下所示的 URL 查询(为了可读性而解码):

?Facets={"Field":"createdBy","Value":"Joe"}&Facets={"Field":"createdBy","Value":"Mary"}&PageSize=25&SearchTerms=error

注意Facets 参数如何被指定两次?对 Facets 数组中的每个元素执行一次。如果您的端点支持该格式的请求,这实际上可能有效。

另一方面,使用 jQuery 的 $.param 实用程序,正如您在第一个示例中尝试在这个 sn-p 中所做的那样......

{
    query: {
        method: 'GET',
        transformRequest: function (data, headersGetter) {
            if (data === undefined) {
                return data;
            }

            return $.param(data);
        }
}

...理论上,您最终会得到这样的 URL 查询字符串(为便于阅读而解码):

SearchTerms=error&PageSize=25&Facets[0][Field]=createdBy&Facets[0][Value]=Joe&Facets[1][Field]=createdBy&Facets[1][Value]=Mary

为什么是“理论上”?好吧,事实证明,您不能以这种方式使用$request,原因有几个。

'query' 使用 HTTP GET

首先,您为 query 指定的 HTTP 方法将决定您传递给该 query 方法的参数对象会发生什么。

假设 query 使用 HTTP GET(如您的示例中所示),参数对象将被序列化并附加到端点 URL。您最终会得到一个附有类似内容的 URL:

?Facets={"Field":"createdBy","Value":"Joe"}&Facets={"Field":"createdBy","Value":"Mary"}&PageSize=25&SearchTerms=error

通过使用 HTTP GET,您基本上是在说您想要随请求发送的数据嵌入在 URL 中。

无论您在transformRequest 中指定的任何处理,都会发生这种情况。

事实上,由于这个query 操作不是 HTTP POST(它是 GET),因此没有要“发布”的数据,因为 GET 的工作方式与 POST 不同(请阅读 HTTP 方法,如果这是新领域为您服务)。这就是为什么您的 transformRequest 函数会得到一个 data 参数,即 undefined

如果我们使用 HTTP POST 会怎样?

假设您将query 设置为使用HTTP POST。在这种情况下,您在调用 query 时传入的参数对象将被放入您的 HTTP 请求的发布数据(也称为请求负载)中。它不会像 GET 那样附加到 URL。

通过使用 HTTP POST,您现在声明可以在请求正文(而不是 URL)中找到您要发送的数据。

由于要发布“数据”,因此您传递给query 的参数对象将在您的transformRequest 函数中作为data 参数提供。这很好,因为您不能只发送像newRequest 这样的原始对象。您需要将其序列化为字符串。否则,您最终会得到类似以下的发布数据:

[object Object]

但由于现在这是一个 POST,您不必再像 URL 查询那样格式化它(因为它不会进入 URL)。您可以使用端点想要接收的任何数据格式。如果你的端点接受 JSON,你可以发送你的 newRequest 对象的 JSON 编码版本,通过在你的 transformRequest 函数中运行这样的东西:

return JSON.stringify(data)

但如果我们必须使用 GET 怎么办?

有时您无法控制终点。如果它只接受您要使用的搜索 API 的 GET 请求怎么办?如果它想要一个非常具体的 URL 查询格式怎么办?

没问题...在一定程度上。

当您通过 $request 工厂创建搜索界面时,您可以指定 参数化 的资源 URL。

假设您的搜索端点 URL 是:

/search.do

它希望看到带有如下 URL 的 GET 请求:

/search.do?PageSize=25&SearchTerms=error

然后,您将使用 参数化 URL 创建搜索界面,如下所示:

$resource("/search.do?PageSize=:PageSize&SearchTerms=:SearchTerms")

这样你就可以传入如下请求:

searchResource.query({PageSize: 25, SearchTerms: "error"}, successCallback);

这将使用 URL 发出 GET 请求:

/search.do?PageSize=25&SearchTerms=error

不幸的是,这开始因无限的参数列表而崩溃,例如您的 Facets 数组。上述模式要求您有一个固定的参数列表。

我们不要忘记,如果您的端点不是太挑剔,那么您总是可以发送一个带有序列化 URL 的 GET 请求,如下所示:

?Facets={"Field":"createdBy","Value":"Joe"}&Facets={"Field":"createdBy","Value":"Mary"}&PageSize=25&SearchTerms=error

这是$resource 对您的newRequest 对象免费执行的操作。

让我们变得复杂

如果你的真的真的想要使用$request,你必须发送看起来像输出的GET请求$.param?

好的。让我们变得花哨。 JS花式。

如果您只打算在搜索界面上使用query 方法,您可以只包装$resource 生成的对象。

.factory("searchResource", ["$resource", searchResource]);

function searchResource($resource) {
    var searchDelegate = $resource("/search.do?:queryString");
    var searchInterface = {
        query: function (params) {
            var encodedQuery = $.param(params);
            return searchDelegate.query({queryString: encodedQuery});
        }
    };
    return searchInterface;
}

现在您可以按原样传递您的 newRequest 对象,并且您的包装搜索资源将对这个 URL 发出 GET 请求(为了可读性而解码):

search.do?SearchTerms=error&PageSize=25&Facets[0][Field]=createdBy&Facets[0][Value]=Joe&Facets[1][Field]=createdBy&Facets[1][Value]=Mary

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-05
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多