As the title suggests, I am attempting to write an AJAX call using Angular's $http object. I would like to send a post request that submits JSON in the body of the request, but also has a query string appended to the URL. After reading the documentation for $http all the way through, surfing for answers on SO, and attempting several different configurations, I am still not clear on how to pass the parameters correctly. Currently, my API responds with either empty JSON or Bad Request (400).
Code
function inviteStaff (json) {
authToken = service.getAuthToken();
return $http({
method: 'POST',
url: baseUrl + '/invitations' + '?authToken=' + authToken,
data: JSON.stringify(json),
headers: {
'Content-type': 'application/json'
}
});
}
Consuming Function
self.invite = function ($form) {
self.showLoader = true;
InvitesService.inviteStaff($scope.inviteModel).then(function () {
$form.$setPristine();
$scope.inviteModel.timestamp = new Date();
$scope.invites.unshift($scope.inviteModel);
$scope.inviteModel = self.createNewInvite();
self.showLoader = false;
},
function () {
self.showLoader = false;
});
};
Request log
data: "authToken=********&t=*****" // this is weird because it adds another "t" parameter to this query string here
headers: Object
Accept: "application/json"
Accept-Language: "en"
Content-type: "application/json"
authToken: "*********" // just the token here
__proto__: Object
method: "POST"
params: Object
timeout: Object
transformRequest: Array[1]
transformResponse: Array[1]
url: "http://****.****.com:****/doctors/invitations?authToken=*****"
__proto__: Object
Param JSON object
{
accountType: "LEAD_DOCTOR",
firstName: "kraken",
lastName: "lastName",
email: "kraken#mail.com"
}
Can anyone shed a little light on how this should work? Do I pass the json object to data or params? Do I need to use JSON.stringify on the data field? In some cases, I also saw people passing an empty string to data.
I also have been running it through Charles Proxy. I can get very close to what I want, but for some reason the JSON is showing under the JSON Text filter as a query string, and not the JSON filter.
Any ideas would be helpful. I will be refactoring and upgrading it all to use the $resource tool, but we are on a tight deadline and I just want to get this working for now. Let me know if you need more info.
UPDATE
We're using swagger to document our API, and you can actually hit the endpoint in their UI. It produces the following CURL command. I thought it might help spot the issue.
curl -X POST --header "Content-Type: application/json" --header "Accept: application/json" -d "{
\"firstName\": \"string\",
\"lastName\": \"string\",
\"email\": \"blah#mail.com\",
\"accountType\": \"CLIENT\"
}" "http://*****.****.com:****/doctors/invitations?authToken=***obfuscatedAuthToken***"
I would recommend you to take advantage of the header field:
function inviteStaff (json) {
authToken = service.getAuthToken();
return $http({
method: 'POST',
url: baseUrl + '/invitations',
data: JSON.stringify(json),
headers: {
'Content-type': 'application/json',
'authToken': authToken
}
});
}
Then on the server side, ask the current request for the header value of authToken.
Passing Tokens in the URL has some security concerns, for your reading: https URL with token parameter : how secure is it?
It turns out we had an HTTP Interceptor function elsewhere in the codebase that was changing the content-type! Wrapped that in a conditional, and it is good to go now. Thanks to everyone who helped me ensure my code was written correctly!
Related
My goal is to export responses from a survey I made on Qualtrics using Google Apps Script. I am trying to get my code to work the a POST API and I got the code to ping, but whatever it is pinging, it is coming back with an 'httpStatus: 400-Bad request' error.
I am new to both Google Apps Script and using API but understand the gist of it. I used Postman to acquired a javaScript Jquery ajax code and made it work with GAS. What is confusing me is when I use the same code with GET APIs and manually typing in IDs (given to me using POSTMAN), it pings perfectly. When running it through Postman, it shows that everything is going through, so not sure what I am doing wrong with the POST call.
var option = {
async: true,
crossDomain: true,
//url:"https://ousurvey.ca1.qualtrics.com/API/v3/surveys/SV_8dK8AKUFyAH8qyN//export-responses/",
method: "POST",
"headers": {
"X-API-TOKEN": "**************************",
"Content-Type": "application/json",
"cache-control": "no-cache",
"Postman-Token": "7a148b75-fa03-4f45-9782-08791c2f1c35"
},
processData: false,
data : '{"format": "csv}',
muteHttpExceptions: true //muted to check Logger
};
var qUrl='https://ousurvey.ca1.qualtrics.com/API/v3/surveys/SV_8dK8AKUFyAH8qyN/export-responses/'
var getSurvey = UrlFetchApp.fetch(qUrl, option);
I need to get the POST to work to obtain the JSON to get the surveys ID so I can use that ID with the GET API to download the information to google drive and convert the information into GoogleDocs.
Here is the current error from the log:
{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Error decoding json body:
com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input\n at
[Source: akka.util.ByteIterator$ByteArrayIterator$$anon$1#71f9c2bb; line: 1, column: 0]"}}}
After changing "Content-Type" to "contentType" I get this error:
""meta":{"requestId":"62b3a313-b1ba-4939-83b7-ee73e65b4e3e","httpStatus":"400
- Bad Request","error":{"errorCode":"QVAL_1","errorMessage":"Json type request body is expected.""
You want to use "Create Response Export" of the API.
From your question and replying comment, I could understand like above. When I saw the document you provided, I found the sample curl command as follows.
curl -X POST \
-H 'X-API-TOKEN: yourapitokenhere' \
-H 'Content-Type: application/json' \
-d '{"format": "csv"}' \
'https://yourdatacenterid.qualtrics.com/API/v3/surveys/SV_012345678912345/export-responses'
I converted this sample to the script of Google Apps Script. The sample script is as follows.
Sample script:
var qUrl = "https://ousurvey.ca1.qualtrics.com/API/v3/surveys/SV_8dK8AKUFyAH8qyN/export-responses/";
var option = {
method: "post",
headers: {"X-API-TOKEN": "###"},
contentType: "application/json",
payload: JSON.stringify({"format": "csv"}),
muteHttpExceptions: true,
};
var getSurvey = UrlFetchApp.fetch(qUrl, option);
Logger.log(getSurvey)
Note:
Above sample script is the same request with the sample curl command. But if an error occurs when you ran the script, please confirm the value of X-API-TOKEN, URL and other parameters which need to your situation.
References:
fetch(url, params)
Create Response Export
I'm trying to send a POST request to my Django view using plain javascript (I don't want any unnecessary libraries involved). The data is not sent through a form, but by using fetch. For now I just want to be able to manipulate the request.POSTin my views.py, nothing more.
Here's my code:
Javascript
let article = document.querySelector('article')
articleId = article.getAttribute('data-product-id')
# some other stuff
fetch("{% url 'shop:shoplist' 1 %}", {
method: 'POST',
dataType: "application/json",
data: {'article_id': articleId},
headers: {'X-CSRFToken': csrf_token}
})
Python
if request.method == 'POST':
testing = request.POST
return JsonResponse({'test': testing})
The request is sent, the csrftoken is received correctly, but the request.POST returns just <QueryDict: {}>, instead of what I'm expecting (headers, data...).
I've searched and found a lot of similar questions, the most similar one being this one, but still I can't seem to find a solution.
Any idea?
Try to add 'content-type' to headers in fetch call (instead of dataType parameter) and change data parameter to body with stringified object:
fetch("{% url 'shop:shoplist' 1 %}", {
method: 'POST',
body: JSON.stringify({'article_id': articleId}),
headers: {
'X-CSRFToken': csrf_token,
'Content-Type': 'application/json'
}})
The request.POST contains only the parameters that are form encoded. Since your data type is application/json they are empty. The request.body contains the actual json data.
I'm trying to build a connection to dropbo using angular.js. For this i have the following piece of code to do a request to https://api.dropboxapi.com/1/oauth2/token .
The code is obtained in the url after the user clicks-through to the verification url of dropbox and back. $scope.accessUrl = 'https://www.dropbox.com/1/oauth2/authorize?client_id=szj63bffo9rp5v7&response_type=' + $scope.response_type + '&redirect_uri=' + $scope.redirect_uri;
This seems to work, since a code is returned via get and it is available in function below.
$http({
method: 'POST',
grant_type: 'authorization_code',
code: <<<<token>>>>,
client_id: '<<<<mydropbox.key>>>>',
client_secret: '<<<<mydropbox.secret>>>>',
redirect_uri: "http://localhost:8080",
contentType: "application/json",
url: "https://api.dropboxapi.com/1/oauth2/token"
})
However, I've tried for a while to get this working and it I'm stuck. I'm not sure what is causing this error but it is always the following response:
error:"invalid_request"
error_description:"No auth function available for given request"
And the HTTP status code is always 400 (bad request).
I'm new to angular. A similar $http request has worked for a different endpoint. For this endpoint I put Bearer: <<<< authorization key that should be returned from broken function above >>>> in the url and a path in data{} of the body.
If anyone knows what I'm doing wrong, of which I'm guessing it's likely a silly mistake, I'd love some help.
You should pass required data in data property as dictionary.
https://docs.angularjs.org/api/ng/service/$http#usage
If you need to pass some data in headers, use headers property.
https://docs.angularjs.org/api/ng/service/$http#setting-http-headers
$http({
method: 'GET',
data: {
grant_type: 'authorization_code',
code: <<<<token>>>>,
client_id: '<<<<mydropbox.key>>>>',
client_secret: '<<<<mydropbox.secret>>>>',
redirect_uri: "http://localhost:8080"
},
contentType: "application/json",
url: "https://api.dropboxapi.com/1/oauth2/token"
})
I finally found the answer. The API documentation of dropbox didn't notice me of the fact I had to send contentType: "application/x-www-form-urlencoded".
They noticed that in general you should send json so I assumed this was true unless mentioned otherwise.
I'm using Angular's http function to access my remote API service.
At the moment, my API service is offline, and if users try to access it, it still throws a 200 status which is 'ok' with Angular. Therefore, my success callback is always called instead of my error callback. Weird thing is, if I use a REST client, such as POSTman, it has a status of 404?
Here is my function to call my API (using jQuerys $.param() function for URL friendly post parameters):
$http({
method: 'POST',
url: "http://mydomain/api/login",
data: $.param({
username: "test",
password: "test"
}),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).then(successCallback(response), errorCallback(response));
Here is the response it gives when logged:
{data: "
↵<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN…r at [my ip address] Port 80</address>↵</body></html>↵", status: 200, config: Object, statusText: "OK"}
The response when the API is working is usually something along the lines of:
{
data: {
id: 123
},
status: 200,
config: object,
statusText: 'ok'
}
Sure, theres a few dodgy things that I could do here such as writing a http interceptor to check if data is of type object, yet that's not really the answer I'm looking for here since some of my API calls just return a boolean value.
I also tried changing the 'Content-Type' to 'application/json', yet no luck.
I need my error callback to be used if my API is down. Is there an elegant solution to this?
It all sounds a bit hacky, the most appropriate approach I can think of is to send a 404 / 500 from the API endpoints until it comes online.
Otherwise, try to set an Accept header:
{
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
}
I have a very simple .NET Web API hosted in Azure, with two very simple methods:
[EnableCors(origins: "http://simpleapiearl.azurewebsites.net", headers: "*", methods: "*")]
public class EnvelopesController : ApiController {
// GET: api/Envelopes
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
// POST: api/Envelopes
public string Post([FromBody]Envelope env) {
return "rval: " + env + " (and my addition to env)";
}
}
I have created a simple plunk to call these methods. In my AngularJS code, I'm making two very simple $http calls. The GET works fine. The POST, however, always returns a "400 (Bad Request)", followed shortly in my WebStorm console by "XMLHttpRequestion cannot load ... Invalid HTTP status code 400".
Any and all suggestions appreciated!
EDIT!!
Hello, and thanks for the very fast responses. I just realized I forgot to add some very relevant detail.
The parameter to my POST method is what I call an Envelope object, which contains two properties: listingId (int), and Description (string). When I add the urlencoded Content-Type header, and pass '{"listingId":1234, "Description":"some description"}' as my POST data, the env parameter in my POST method on the server is NULL (that is, it seems unable to parse the JSON I'm providing). Sorry this was omitted from original post.
I think this post would be helpful.
How do I POST urlencoded form data with $http in AngularJS?
By default, the $http service will transform the outgoing request by
serializing the data as JSON and then posting it with the content-
type, "application/json". When we want to post the value as a FORM
post, we need to change the serialization algorithm and post the data
with the content-type, "application/x-www-form-urlencoded".
This is the modified plnkr from yours. Your code is missing conversion of JSON to encoded url.
http://plnkr.co/edit/4aeEDz73qgHSfOv1sBgn?p=preview
$http({
method: 'POST',
url: 'http://simpleApiEarl.azurewebsites.net/api/envelopes',
data: env,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
}
}).
I found a blog post that corrected my issue:
http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/
Thanks to everyone for helping me out!
With AngularJS you can do in an easier way.
You can inject $httpParamSerializer and then prepare your data through $httpParamSerializer(data).
So your call should look something like:
$http({
method: 'POST',
url: 'http://simpleApiEarl.azurewebsites.net/api/envelopes',
data: $httpParamSerializer(env),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})