为什么在资源查询转换

时间:2015-10-03 02:46:30

标签: angularjs angular-resource

我是棱角分明的新人......所以我确信我做错了什么。花了好几个小时试图寻找解决方案,但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对象是有效的,因为呼叫结束了。只是有一个格式不正确的网址。我哪里做错了?

2 个答案:

答案 0 :(得分:3)

问题是它是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 pututhiness:https://developer.mozilla.org/en-US/docs/Glossary/Truthy

回复其他信息

由于您没有将数据设置为任何内容,因此您仍然在数据中未定义,您正在设置参数。但是一旦你在这里,你就不需要改变请求了。

你可以做到

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

答案 1 :(得分:1)

有一些额外的信息可以帮助我们找到问题的根源。我想我们可以在没有它们的情况下顺利过关,但万一我们遇到困难,我想知道:

  • 浏览器控制台中是否有错误?
  • 搜索端点网址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实用程序,正如您在第一个示例中尝试在此代码段中所做的那样...

{
    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

'查询'使用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请求的post数据(也称为请求有效负载)中。它不会像GET一样附加到URL。

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

因为有"数据"要发布,您传递到query的参数对象将作为data函数中的transformRequest参数提供。这很好,因为你不能只发送像newRequest这样的原始对象。您需要将其序列化为字符串。否则,您最终会得到以下帖子数据:

[object Object]

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

return JSON.stringify(data)

但是如果我们必须使用GET呢?

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

不成问题......直到某一点。

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

我们说您的搜索终点网址为:

/search.do

它希望通过以下网址查看GET请求:

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

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

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

这样您就可以传递一个请求:

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

哪个会使用网址发出GET请求:

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

不幸的是,这开始与像Facets数组这样的无界参数列表分解。上述模式要求您拥有固定的参数列表。

让我们不要忘记,如果你的终点不是太挑剔,那么你总是可以发送一个GET请求,其中包含如下序列化的URL:

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

$resourcenewRequest对象免费提供的内容。

让我们变得复杂

如果您的真的 真的 想要使用$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