We are integrating Google App State into our web based game using Google's API discovery service. Our test code looks like this:
gapi.client.load('appstate','v1',function(response) {
var request = gapi.client.appstate.states.update({ stateKey: 0 });
request.execute(function(result) { console.log(result); });
});
I always get back an HTTP 400 with the message "The update request does not contain any data.". This makes sense since I'm not passing any data, the problem is that I have no idea how to pass the data (aka state) and I can't find any documentation on how to do it. I tried passing some data along with the stateKey
{ stateKey: 0 , data: <some-base64-encoded-data> }
But this still fails with the same message.
After inspecting the library's minified code I'm starting to suspect this feature is still unimplemented.
UPDATE (after doing some more experiments)
I also tried using gapi.client.request like this:
var request = gapi.client.request({
'path': '/appstate/v1/states',
'params': { 'stateKey': '0' },
'method': 'PUT',
'body': {
'kind': 'appstate#updateRequest',
'data': 'ZGF0YQ=='
}
});
But I get a 404 and I think it's because this method passes request parameters in the &stateKey=0 style. On the other hand if I use 'path': '/appstate/v1/states/0' I get a conflict response which is what I was expecting.
Correct me if I'm wrong but I'm starting to believe that the problem is due to inconsistencies between Google's different web APIs which they are now trying to coalesce into a single interface.
In your initial Discovery-based method, you need to pass the data in a parameter named 'resource.'
Here is the Discovery document for appstate/v1: https://www.googleapis.com/discovery/v1/apis/appstate/v1/rpc
Related
I recently restructured my API on AWS Gateway to make all my Lambda functions use proxy integration - before that, every single parameter was passed in as a path parameter (awful, I know.)
I never had any issues with CORS then, and I've tried several things over the past few hours to fix the issue discussed in the topic line.
First, I used a proxy resource and used an "ANY" method, but when that gave me CORS issues, I enabled CORS on the API method and tried again - still nothing. So, I tried changing it so that it was a "POST" request instead and enabling CORS - still nothing. And I made sure to deploy after every setting change. Then, I got rid of the proxy and instead just made a "POST" method with CORS enabled, and still nothing.
I'm using Angular's http post method.
Edit:
I'm using Angular 1.6.4, and this is the code I'm using to call the API:
this.checkRegistered = function(email){
var data = { Email : email};
var toSend = JSON.stringify(data);
return $http.post('link', toSend);
};
That's in my service for the angular module, and it's being called from this function in the controller:
function CheckIsRegistered(email)
{
return userService.checkRegistered(email).then(function(res){
if (res.data.statusCode === 200){
return res.data.body;
}});
}
I've configured all the parameters so that "Email" is what it should be expecting, and I did replace the word 'link' with the actual link.
When I enable CORS through the console, I assign the headers as follows:
Access-Control-Allow-Headers: 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
Access-Control-Allow-Origin: '*'
This is especially infuriating because I've actually worked with this exact issue before and solved it fairly easily, but now that I'm using Lambda's proxy integration I've run into this issue again and I can't quite seem to figure it out.
Any help is appreciated.
I figured out the problem - I had to add the headers for CORS to the Lambda response. Here's the code snippet for anyone else having similar problems:
connection.query('arbitrary query', [params], function (error, results, field) {
if (!error)
{
connection.end();
var responseBody = results;
response = { statusCode: 200,
headers: { "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", "Access-Control-Allow-Origin": "*"},
body: JSON.stringify(responseBody)};
callback(null, response);
}
I put the querying line in there just for context of what "response" is.
I'm having trouble getting Google App Script's UrlFetchApp class to correctly format a JSON payload in a HTTP request.
Here's how I'm formatting the payload:
var data = {
'RequestData': {
'ItemNumber': '1',
'StockItemId': '2',
'ImageUrl': 'www.example.com'
}
};
var payload = JSON.stringify(data);
Surely enough, Logger.log(payload) returns:
{"RequestData":{"ItemNumber":"1","StockItemId":"2","ImageUrl":"www.example.com"}}
However, when I integrate this with the actual request:
var url = 'https://example.com/api/method';
var options = {
'method': 'post',
'contentType':'application/json',
'payload': payload,
'muteHttpExceptions': true
};
and examine Logger.log(UrlFetchApp.getRequest(url, options).toSource()), I get this:
({method:"post",
payload:"{\"RequestData\":{\"ItemNumber\":\"1\",\"StockItemId\":\"2\",\"ImageUrl\":\"www.example.com\"}}",
followRedirects:true,
validateHttpsCertificates:true,
useIntranet:false, contentType:"application/json",
url:"https://example.com/api/method"})
i.e. all the quotes are escaped, as if JSON.stringify is being called twice on the payload.
I thought GAS might be calling an equivalent of JSON.stringify on the payload behind the scenes because contentType is specified as "application/json" but if I pass the data raw without stringifying it, I get:
({method:"post",
payload:"RequestData=%7BItemNumber%3D1,+StockItemId%3D2,+ImageUrl%3Dwww.example.com%7D",
followRedirects:true,
validateHttpsCertificates:true,
useIntranet:false, contentType:"application/json",
url:"https://example.com/api/method"})
which is weird and (I'm pretty sure) an invalid JSON string. Either way, the request fails (400 Bad Request) and I'm pretty sure it's because I can't get the JSON across properly.
All the docs and examples I can find seem to suggest that JSON.stringify(payload) is the way to go but I'm clearly having issues...
Have also had the thought that this might have something to do with the implementation of the .toSource() method and that my problem might lie elsewhere but I have no way of knowing / otherwise being able to check the request body I'm sending.
Any insight would be much appreciated!
I'm trying to send just the array of my data to the server. I found this post discussing how to send an array, but I can't get it to work.
AngularJS: ngResource and array of object as params to URL
The problem I am having is my resource gets sent back to me as JSON like this
{
Results: []
}
So when I ask for my resources,
var collaboratorResource = api.CollaboratorList.get({Id: Id });
but then, if I try something like
collaboratorResource.$save($scope.collaborators);
When I look at firebug, it shows that my data is being sent as
{
Results: []
}
when in reality, I don't want to send the data as an object with the array as a Results property. I want to send it just as an array []. I need to do that since the api is legacy and expects that.
I've been trying to see if transformRequest works, like if I did
collaboratorResource.$save({data: $scope.collaborators, transformRequest: function (data, headers) { return data.results; }});
collaboratorResource.$save({}, $scope.collaborators);
collaboratorResource.$save($scope.collaborators);
But that doesn't seem to work either. Is this even possible? As an aside, if I use $http like this, it works:
$http({ method: "POST", data: $scope.collaborators, url: collaboratorUrl });
I'm just not sure how to use the $resource service properly since I'd prefer to wrap everything in $resource if possible and not have a hybrid of both if possible. Thanks.
I'm working on a web application that is powered by a restful API written with Python's CherryPy framework. I started out writing the user interface with a combination of jQuery and server side templates, but eventually switched to Backbone.js because the jQuery was getting out of hand.
Unfortunately, I'm having some problems getting my models to sync with the server. Here's a quick example from my code:
$(function() {
var User = Backbone.Model.extend({
defaults: {
id: null,
username: null,
token: null,
token_expires: null,
created: null
},
url: function() {
return '/api/users';
},
parse: function(response, options) {
console.log(response.id);
console.log(response.username);
console.log(response.token);
console.log(response.created);
return response;
}
});
var u = new User();
u.save({'username':'asdf', 'token':'asdf'}, {
wait: true,
success: function(model, response) {
console.log(model.get('id'));
console.log(model.get('username'));
console.log(model.get('token'));
console.log(model.get('created'));
}
});
});
As you can probably tell, the idea here is to register a new user with the service. When I call u.save();, Backbone does indeed send a POST request to the server. Here are the relevant bits:
Request:
Request URL: http://localhost:8080/api/users
Request Method: POST
Request Body: {"username":"asdf","token":"asdf","id":null,"token_expires":null,"created":null}
Response:
Status Code: HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 109
Response Body: {"username": "asdf", "created": "2013-02-07T13:11:09.811507", "token": null, "id": 14, "token_expires": null}
As you can see, the server successfully processes the request and sends back an id and a value for created. But for some reason, when my code calls console.log(u.id);, I get null, and when my code calls console.log(u.created);, I get undefined.
tl;dr: Why isn't Backbone.js persisting changes to my objects after a call to save()?
Edit:
I've modified the above code so that the model properties are accessed using the get function in a success callback. This should solve any concurrency problems with the original code.
I've also added some console logging in the model's parse function. Oddly enough, each of these is undefined... Does that mean that Backbone.js is failing to parse my response JSON?
Edit 2:
A few days ago, I found out that issue was actually a custom header that I was adding to every request to enable HTTP Basic Authentication. See this answer for details.
This code:
u.save();
console.log(u.id);
console.log(u.username);
console.log(u.token);
console.log(u.created);
Runs immediately... after that there is nothing to run and the queued ajax request begins. The response then comes a bit later and only at that point have the values changed.
It also seems that those properties are not directly on the object, but the asynchronous processing of the save still holds in that you wouldn't get expected results even if you corrected that code to have console.log(u.get("id")) etc.
I figured out the issue, although I'm still at a loss to explain why it's an issue at all. The web app that I'm building has an authentication process that requires an HTTP basic Authentication header to be passed with all requests.
In order to make this work with Backbone, I overrode the Backbone.sync function and changed line 1398 to add the header.
Original Code:
var params = {type: type, dataType: 'json'};
Modified Code:
var params = {
type: type,
dataType: 'json',
headers: {
'Authorization': getAuthHash()
}
};
The function getAuthHash() just returns a Base64 string that represents the appropriate authentication information.
For some reason, the addition of this header makes the sync/save functions fail. If I take it out, everything works as you might expect it to.
The bounty is still open, and I'll happily reward it to anybody who can explain why this is, as well as provide a solution to the problem.
Edit:
It looks like the problem was the way that I was adding the header to the request. There's a nice little JavaScript library available on Github that solves this problem by correctly adding the HTTP Basic Auth header.
i have tested your code, its works fine for me.
See Demo here, jsfiddle.net/yxkUD/
Try to add a custom beforeSend method to the ajax request to add the custom header.
For example:
https://github.com/documentcloud/backbone/blob/master/backbone.js#L1424
The service API I am consuming has a given GET method that requires the data be sent in the body of the request.
The data required in the body is a list of id's separated by hypen and could potentially be very large and thus it must be sent in the body otherwise it will likely foobar somewhere in the browsers/proxies/webservers etc chain. Note I don't have control over the service or API so please don't make suggestions to change it.
I am using the following jQuery code however observing the request/response in fiddler I can see that the "data" I am sending is ALWAYS converted and appended to the query string despite me setting the "processData" option to false...
$.ajax({
url: "htttp://api.com/entity/list($body)",
type: "GET",
data: "id1-id2-id3",
contentType: "text/plain",
dataType: "json",
processData: false, // avoid the data being parsed to query string params
success: onSuccess,
error: onError
});
Anyone know how I can force the "data" value to be sent in the body of the request?
In general, that's not how systems use GET requests. So, it will be hard to get your libraries to play along. In fact, the spec says that "If the request method is a case-sensitive match for GET or HEAD act as if data is null." So, I think you are out of luck unless the browser you are using doesn't respect that part of the spec.
You can probably setup an endpoint on your own server for a POST ajax request, then redirect that in your server code to a GET request with a body.
If you aren't absolutely tied to GET requests with the body being the data, you have two options.
POST with data: This is probably what you want. If you are passing data along, that probably means you are modifying some model or performing some action on the server. These types of actions are typically done with POST requests.
GET with query string data: You can convert your data to query string parameters and pass them along to the server that way.
url: 'somesite.com/models/thing?ids=1,2,3'
we all know generally that for sending the data according to the http standards we generally use POST request.
But if you really want to use Get for sending the data in your scenario
I would suggest you to use the query-string or query-parameters.
1.GET use of Query string as.
{{url}}admin/recordings/some_id
here the some_id is mendatory parameter to send and can be used and req.params.some_id at server side.
2.GET use of query string as{{url}}admin/recordings?durationExact=34&isFavourite=true
here the durationExact ,isFavourite is optional strings to send and can be used and req.query.durationExact and req.query.isFavourite at server side.
3.GET Sending arrays
{{url}}admin/recordings/sessions/?os["Windows","Linux","Macintosh"]
and you can access those array values at server side like this
let osValues = JSON.parse(req.query.os);
if(osValues.length > 0)
{
for (let i=0; i<osValues.length; i++)
{
console.log(osValues[i])
//do whatever you want to do here
}
}
Just in case somebody ist still coming along this question:
There is a body query object in any request. You do not need to parse it yourself.
E.g. if you want to send an accessToken from a client with GET, you could do it like this:
const request = require('superagent');
request.get(`http://localhost:3000/download?accessToken=${accessToken}`).end((err, res) => {
if (err) throw new Error(err);
console.log(res);
});
The server request object then looks like {request: { ... query: { accessToken: abcfed } ... } }
You know, I have a not so standard way around this. I typically use nextjs. I like to make things restful if at all possible. If I need to make a get request I instead use post and in the body I add a submethod parameter which is GET. At which point my server side handles it. I know it's still a post method technically but this makes the intention clear and I don't need to add any query parameters. Then the get method handles a get request using the data provided in the post method. Hopefully this helps. It's a bit of a side step around proper protocol but it does mean there's no crazy work around and the code on the server side can handle it without any problems. The first thing present in the server side is if(subMethod === "GET"){|DO WHATEVER YOU NEED|}