No response from API - javascript

I have created an API call in excel to get data from a Wix database.
The call:
Dim http As Object, JSON As Object
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "GET", "https://username.wixsite.com/mysite/_functions/Functionname", False
http.setRequestHeader "Authorization", "myauthkey"
http.Send
MsgBox (http.responseText)
The javascript http backend file on Wix:
import { ok, notFound, serverError } from 'wix-http-functions';
import wixData from 'wixdata';
export function get_Wixdata() {
let options = {
"headers": {
"content-type": "application/json"
}
};
return wixData.query("wix data collection name")
.find()
.then(results => {
if (results.items.length > 0) {
options.body ={
"items": results.items
}
return ok(options);
}
})
}
I tested the call (without authorisation) on JSON place holder and it worked fine.
Just trying to debug what's happening as I am getting "" as a response.
Even if I enter the wrong API key I still get "", even a wrong url it's still a "" response.
I take it I am clearly way off the mark with what I am trying to do..

Did you tried put both headers in your request, like the following:
let headers = new Headers({
'Content-Type': 'application/json',
'Authorization': '....'
});

The issue was with the VBA call, the header was not needed.
Dim https As Object, JSON As Object
Set https = CreateObject("MSXML2.XMLHTTP")
With CreateObject("Microsoft.XMLHTTP")
.Open "GET", "end point url", False
.send
response = .responseText
End With

Related

Google Apps Script GraphQL API Call

I've been working with the Canvas REST API and ran into some limitations and was directed to their experimental GraphQL API. Given its experimental nature, they have little to no documentation on it. Despite its advantages for what I'm building, I can't find a whole lot on the internet either. As of right now, I can't even get a basic query to work.
function testsendgraphql() {
const url = "https://hsccsd.beta.instructure.com:443/api/graphql";
const payload = `
query {
course(id: 1234) {
{
name
}
}
}`;
const options = {
"method": "POST",
"headers": { "Content-Type": "application/json", "Authorization": "Bearer "+getcanvasaccesstoken() },
"body": payload
};
Logger.log(query);
apiresponse = UrlFetchApp.fetch(url,options);
var head = apiresponse.getAllHeaders();
var data = JSON.parse(apiresponse.getContentText());
Logger.log(data);
Logger.log(head);
}
Running the above gets a response code of 200 but gives the following message:
{errors=[{message=No query string was present}]}
Something with my formatting of the payload seems to be off. Any help would be greatly appreciated.
I recently had success with the following GraphQL query using Google Apps Script:
function test(query) {
var url = "https://gql.waveapps.com/graphql/public";
var options = {"headers": {"Authorization": "Bearer " + getAccessToken(),
"Content-Type": "application/json"
},
"payload": JSON.stringify({query}),
"method": "POST"
}
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
return;
}
where the query that was passed into the function was formatted as follows:
var query = "query { user { id defaultEmail } }";
You must JSON.stringify your query and post it as payload.
I had a similar error to yours, that there was no query string present. It may be because GraphQL is looking in the URL arguments for the query, not in the HTTP request payload.
For example, get rid of the payload and change your URL to:
var url = encodeURI("https://gql.waveapps.com/graphql/public?query=query{course(id:1234){{name}}}");
Or maybe
var url = encodeURI("https://gql.waveapps.com/graphql/public?query={course(id:1234){{name}}}");
This was brought to my attention by this answer.

Trouble returning API response leveraging fetch

Been working on a script to execute in Google Apps Scripts to pull some data from an external API and post that information into a Google Sheet. I have a script that works client side (running from the console in chrome) that is able to leverage promises and return HTTP responses correctly to execute more code on.
However, in Apps Scripts I cannot figure out how to return a native JSON object representation from the API. In normal JS, I would return the .json() method of the response and would be good to go. Since Apps Script is essentially executing a .gs file they have different classes and methods that are not specific to JS. This help doc https://developers.google.com/apps-script/guides/services/external provides the below example for working with JSON
var json = response.getContentText();
var data = JSON.parse(json);
Logger.log(data.title);
If I try to leverage that getContextText() method by itself I get a TypeError: Cannot read property '0' of undefined error. If I combine it with JSON.parse like return JSON.parse(response.getContentText()); I get a SyntaxError: Unexpected token M in JSON at position 0. Am I missing something wildly obvious? Any help would be greatly appreciated! Additionally, happy to provide more specifics from my script as well.
EDIT
Below is a snippet of script that works client side.
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': "Basic" + ' ' + gongCreds,
'Accept': "*/*",
'Connection': "keep-alive",
'Content-Type': "application/json"
},
body: gongRequestBody,
});
return response.json();
}
Here is the returned promise object data that I want to leverage for future execution
[[PromiseResult]]: Object
records: {totalRecords: 1, currentPageSize: 1, currentPageNumber: 0}
requestId: "6w83fpcbo5ka2evast7"
usersDetailedActivities: Array(1)
0:
userDailyActivityStats: Array(1)
0:
callsAsHost: []
callsAttended: (6) ["432806286570218902", "1825323793748204011", "3193437184015561879", "4172384470445855263", "5128172192322468435", "5808052479141116583"]
callsCommentsGiven: []
callsCommentsReceived: []
callsGaveFeedback: []
callsMarkedAsFeedbackGiven: []
callsMarkedAsFeedbackReceived: []
callsReceivedFeedback: []
callsRequestedFeedback: []
callsScorecardsFilled: []
callsScorecardsReceived: []
callsSharedExternally: []
callsSharedInternally: []
fromDate: "2021-01-20T05:00:00Z"
othersCallsListenedTo: (2) ["3401282086024720458", "8098199458721511977"]
When your following Javascript is converted to Google Apps Script,
async function postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': "Basic" + ' ' + gongCreds,
'Accept': "*/*",
'Connection': "keep-alive",
'Content-Type': "application/json"
},
body: gongRequestBody,
});
return response.json();
}
it becomes as follows.
const response = UrlFetchApp.fetch(url, {
method: 'POST',
headers: {
'Authorization': "Basic" + ' ' + gongCreds,
'Accept': "*/*",
'Connection': "keep-alive",
},
muteHttpExceptions: true,
contentType: "application/json",
payload: gongRequestBody,
});
console.log(response.getContentText())
// I think that if above request works and the returned value is the correct value you expected, you can use console.log(JSON.parse(response.getContentText()).title)
But, there are several important points.
From your script, I cannot see the value of gongRequestBody. When 'Content-Type': "application/json" is used, JSON.stringify() is required to be used for the JSON object. So if gongRequestBody is the JSON object, please convert it to the string using JSON.stringify().
From your question, I cannot see your script for requesting to the URL. So I cannot point out the modification points of your script even when your script has the modification points.
I asked to show the sample value of response.getContentText() in your Google Apps Script. But unfortunately, I cannot find the sample value of it. So when you use console.log(JSON.parse(response.getContentText()).title) to the above sample script of Google Apps Script and an error occurs, can you provide the sample value of console.log(response.getContentText())? By this, I would like to confirm it.
Note:
In this sample script, it supposes that your Javascript works and the variables of gongCreds, gongCreds and gongRequestBody are the valid values for requesting to your URL. Please be careful this. So when above sample script didn't work as you expected, can you provide the sample value of console.log(response.getContentText())? By this, I would like to confirm your situation.
Reference:
fetch(url, params)

400 Bad Request error when sending POST request to WCF service using JavaScript

I have a very simple self-hosted WCF application initialized this way:
Host = new WebServiceHost(this, new Uri(serviceAddress));
var binding = new WebHttpBinding();
binding.ReceiveTimeout = TimeSpan.MaxValue;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.CrossDomainScriptAccessEnabled = true; // TODO: Remove this?
var endpoint = Host.AddServiceEndpoint(typeof(HttpService), binding, "");
Host.Open();
My HttpService class has this method:
[OperationContract, WebInvoke(Method = "*")]
public string Evaluate(string query)
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST, PUT, DELETE");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Max-Age", "1728000");
return "";
}
else
{
try
{
return "12345";
}
catch (ProcessNotFoundException ex)
{
throw new WebFaultException<string>(ex.Message, System.Net.HttpStatusCode.NotFound);
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message, System.Net.HttpStatusCode.InternalServerError);
}
}
}
I added the CORS stuff above so that anyone can use this API from any website using JavaScript. And I'm trying to run some POST requests using fetch because once it works, I'll pass large amounts of data so a GET method won't work. Doing the following from JavaScript works OK:
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '1232'});
When I do that, my Evaluate method is called first with OPTIONS and then with POST, and an XML string is returned with no errors. The following also works (note the quotes inside the body):
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '"hello world"'});
Since both are valid JSON, I though any JSON string would work, so I did this but I got a 400 Bad Request error:
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '[10]'});
I thought maybe it wants a JSON that it can convert to my method arguments, but this also doesn't work (gives me a 400 error):
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: JSON.stringify({ query: "hey" })})
So it can take numbers and strings but nothing else, and I have no idea why. I tried using all these other attribute combinations and got the exact same results:
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Wrapped)]
I just want to be able to send any string, without something looking into it and deciding whether it's good or bad, while also being able to do the CORS thing above. Is there a way to do this?
I found a workaround but I'm still not sure why it was letting me pass numbers and strings but not arrays, so if someone comes up with an actual explanation I'll be happy to accept that as an answer. My solution was to take a Stream instead of a string in my Evaluate method, and then read the stream like:
using (var memoryStream = new MemoryStream())
{
query.CopyTo(memoryStream);
var array = memoryStream.ToArray();
var lole = Encoding.UTF8.GetString(array);
}
I think the idea is that, since I'm doing the CORS thing and the browser sends an empty body when sending an OPTIONS request, WCF can't handle that as JSON so I have to handle everything myself and the way to do that is by taking a war Stream. Still no idea why it was letting me send numbers and strings (and booleans too BTW) but no arrays (or objects).
At first, the following definition only supports to accept the parameters by using request body, instead of the URL Params.
[OperationContract, WebInvoke(Method = "*")]
public string Evaluate(string query)
Moreover, the body style of the parameters is bare by default.
BodyStyle =WebMessageBodyStyle.Bare
It means that the server accepts the string without wrapping the name of the parameter.
body: '1232'
body: '"hello world"'
When we manually switch the value to BodyStyle=WebMessageBodyStyle.Wrapped, the server will accept the below form. Basically, your attempts are correct. There is just a small problem here.
{"value":"abcd"}
Here is my example, wish it is helpful to you.
Server contract.
[OperationContract]
[WebInvoke(Method ="*",BodyStyle =WebMessageBodyStyle.Wrapped,ResponseFormat =WebMessageFormat.Json)]
string GetData(string value);
JS
<script>
var para1 = { "value": "abcdefg" };
$.ajax({
type: "POST",
url: "http://localhost:8864/Service1.svc/getdata",
data: JSON.stringify(para1),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: OnSuccessCall,
error: OnErrorCall
});
function OnSuccessCall(response) {
console.log(response);
}
function OnErrorCall(response) {
console.log(response.status + " " + response.statusText);
}
</script>
About handing the CORS issue, I add an extra global.asax file.
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.AddHeader("Cache-Control", "no-cache");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With,Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
}
Here is a related document.
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.web.webmessagebodystyle?view=netframework-4.8
Feel free to let me know if there is anything I can help with.

Safebrowsing API returns 'Invalid JSON payload received'

I'm using Safe browsing API to check some URLs from my database, but the request gives me this result:
data {
"error": {
"code": 400,
"message": "Invalid JSON payload received. Unknown name \"threatInfo[threatTypes][0]\": Cannot bind query parameter. Field 'threatInfo[threatTypes][0]' could not be found in request message.\nInvalid JSON payload received. Unknown name \"threatInfo[threatTypes][1]\": Cannot bind query parameter. Field 'threatInfo[threatTypes][1]' could not be found in request message.\nInvalid JSON payload received. Unknown name \"threatInfo[platformTypes][0]\": Cannot bind query parameter. Field 'threatInfo[platformTypes][0]' could not be found in request message.\nInvalid JSON payload received. Unknown name \"threatInfo[threatEntryTypes][0]\": Cannot bind query parameter. Field 'threatInfo[threatEntryTypes][0]' could not be found in request message.\nInvalid JSON payload received. Unknown name \"threatInfo[threatEntries][0][url]\": Cannot bind query parameter. Field 'threatInfo[threatEntries][0][url]' could not be found in request message."
}
}
I'm trying the following code:
const request = require('request');
const body = {
threatInfo: {
threatTypes: ["SOCIAL_ENGINEERING", "MALWARE"],
platformTypes: ["ANY_PLATFORM"],
threatEntryTypes: ["URL"],
threatEntries: [{url: "http://www.urltocheck2.org/"}]
}
}
const options = {
headers: {
"Content-Type": "application/json"
},
method: "POST",
url: "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${API_KEY}",
form: body
}
console.log(options);
request(options,
function(err, res, data) {
console.log('data', data)
if (!err && res.statusCode == 200) {
console.log(data);
}
}
)
I expected the output of the request be {} with a 200 status code on this sample.
If you look in the request() doc for the form property, you will see this:
form - when passed an object or a querystring, this sets body to a querystring representation of value, and adds Content-type: application/x-www-form-urlencoded header. When passed no options, a FormData instance is returned (and is piped to request). See "Forms" section above.
When you look at the Google safe browsing API, you will see this:
POST https://safebrowsing.googleapis.com/v4/threatMatches:find?key=API_KEY HTTP/1.1
Content-Type: application/json
You are sending Content-type: application/x-www-form-urlencoded, but the API wants Content-Type: application/json. You need to send JSON, not form encoded data.
You can probably just change the form property to the json property by changing from this:
const options = {
headers: {
"Content-Type": "application/json"
},
method: "POST",
url: "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${API_KEY}",
form: body
}
to this:
const options = {
method: "POST",
url: "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${API_KEY}",
json: body // <=== change here
}
The content-type is automatically set either way to match the format of the generated body so you don't need to set it.

When sending POST request to backend API via fetch(), the body has only key and no value

When I'm sending a POST request to my backend express server, the req.body contains only the key part where the entire body is the key and the value part is empty
This is the frontend fetch request
let data = {
videoUrl: "dummy text"
}
fetch("/api/getinfo", {
method:"POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
},
body: JSON.stringify(data)
})
This is how I handle it in backend (NOTE: I'm using body-parser)
app.post("/api/getinfo", (req,res) => {
console.log(req.body);
}
I expect the output to be
'{ "videoUrl":"dummy text" }'
But what I get is
{ '{"videoUrl":"dummy text"}': '' }
The entire requests body is the key, and the value is empty.
What am I doing wrong?
You are using the wrong Content-Type to send json
Try
"Content-Type": "application/json;charset=UTF-8"
I noticed an issue in your header;
fetch("/api/getinfo", {
method:"POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" //this very line
},
I guess what you meant is
fetch("/api/getinfo", {
method:"POST",
headers: {
'Content-type': 'application/json; charset=utf-8'
},
Note: Your header denotes what the content is encoded in. It is not necessarily possible to deduce the type of the content from the content itself, i.e. you can't necessarily just look at the content and know what to do with it. So you need to be sure of what you're writing in your header else you will end up with an error.
I will suggest you get to read more about What does “Content-type: application/json; charset=utf-8” really mean?
The problem is that you are stringifying the data:
body: JSON.stringify(data)
should be
body: data
That should fix it

Categories

Resources