我是棱角分明的新人......所以我确信我做错了什么。花了好几个小时试图寻找解决方案,但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;
}
);
但问题是data
是undefined
!我知道newRequest
对象是有效的,因为呼叫结束了。只是有一个格式不正确的网址。我哪里做错了?
答案 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包含了必要的库。
如果是这种情况,这里有一些可能有帮助的花絮。
当您使用$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
。
首先,您为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
参数的原因。
假设您设置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)
有时你无法控制终点。如果它只需要您要使用的搜索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
$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