The new custom API script allows a lot of customization through any type of connection.
I found that this website Custom API in Azure Mobile Services – Client SDKs describes the custom API.
var client = new WindowsAzure.MobileServiceClient('http://myservice.azure-mobile.net/', 'mykey');
client.invokeApi('query', {
method: 'POST'
});
But I couldn't run this code. It is supposed to show a message "Hello post World!".
I put the code inside tags in an HTML file and ran it but nothing happened.
Any help?
The call you have is making a call to your service, but it's ignoring its response. Assuming you have a custom API called 'query' (since it's what you're passing to invokeApi) with the following body:
exports.post = function(request, response) {
response.send(200, { message: 'Hello world' });
};
Your client code is calling it and (if everything goes fine) receiving the response, but it's not doing anything with it. There are a couple of ways to find out whether the call is being made. For example, you can add a log entry in the API and check the logs in your service:
exports.post = function(request, response) {
console.log('The API was called');
response.send(200, { message: 'Hello world' });
};
Or you can use a networking tool (the browser developer tools or Fiddler, for example) to see if the request is being made. Or you can actually do something with the result in the client side:
var client = new WindowsAzure.MobileServiceClient('http://myservice.azure-mobile.net/', 'mykey');
client.invokeApi('query', {
method: 'POST'
}).done(
function(result) { alert('Result: ' + JSON.stringify(result)); },
function(error) { alert('Error: ' + error); }
);
One thing which you need to look at if you're calling the API from a browser is whether the domain from where the page is being loaded is in the 'allow requests from host names' list, under the 'configure' tab, 'cross-origin resource sharing (cors)' section. If it's not, then you may get an error instead of the response you want.
Related
This is a doPost function inside a Google App that returns a Hello World message.
function doPost(e){
return ContentService.createTextOutput('Hello World');
}
Now suppose I want to only accept valid JSON to be posted to this Google App endpoint and I want to send a respones with Bad Request status. How can I do that. Here's the pseudo code:
function doPost(e){
try{
const data = JSON.parse(e.postData.contents);
return ContentService.createTextOutput('Hello World');
}catch(err){
// Send Bad Request
}
}
Issue and workaround:
Unfortunately, in the current stage, ContentService cannot modify the status code. When I saw the official document of Class ContentService, such method cannot be found. Ref It seems that this is the current specification.
So in your situation, as the current workaround, how about returning the value as JSON data? By this, you can check the value using the key of JSON data. For example, how about the following sample script?
When the correct value without the error is returned,
return ContentService.createTextOutput(JSON.stringify({value: 'value'}));
When the value with the error is returned,
return ContentService.createTextOutput(JSON.stringify({error: 'Error message'}));
When you need .setMimeType(ContentService.MimeType.JSON), please add this.
Note:
When I searched about this at the Google issue tracker, I couldn't find it. So how about reporting this as the future request? Ref
Reference:
Class ContentService
Here's another workaround that allows raising errors on the client side for errors on the web app side. For example, a client might need to catch errors such as bad url args sent to the web app (i.e. the OP's question), or catch errors thrown by a method that is called from doGet() or doPost().
As far as I know, when an error is thrown downstream of doGet() or doPost(), a text error message is returned in the response, but the web app request itself succeeds, so there is no error thrown on the client side. As #Tanaike said, there still seems no way for a Google web app dev to throw an HTTP error from the app (like 400 Bad Request or 500 Internal Server Error).
The idea involves returning a function body from the web app, which the client can use to create and run a dynamic function via the Function() constructor (this assumes Javascript is available on the client).
So the web app can be written to:
return a function body that will throw an error for bad args, server method errors, etc.
return a function body that will return intended JSON when there is no error
This is a bit of a hack, but it unifies error handling on the client side. The client makes the http request, constructs a function using the function body returned in the response, and then runs this function, all in one try{} block. Then both Google-raised http errors and web app downstream errors can be caught in the catch{} block.
Example setup for a Google Apps Script client making a request to a Google web app:
(1) In the web app doGet() or doPost() function:
// this string will be returned by the webapp
var fnBody;
// for bad url args, return a fnBody that will throw an error with an indicative message
if(!urlArgsOk()) {
fnBody = "'use strict'; throw new Error('POST args error');";
}
// if url args are ok, call server method
else {
try {
// if the method call succeeds, return a fnBody that will return the intended JSON
var returnObj = myServerMethod(methodArgs);
fnBody = "'use strict'; return JSON.stringify(" + JSON.stringify(returnObj) + ");";
}
catch(serverErr) {
// if the method call fails, return a fnBody that will throw an error ...
// ... simple example shown here, but info from serverErr can be included in fnBody
fnBody = "'use strict'; throw new Error('server error');";
}
}
// return fnBody, which can be run via Function() on the client
return ContentService.createTextOutput(fnBody).setMimeType(ContentService.MimeType.TEXT);
(2) On the client side (Google apps script client making a POST request)
// Set the url, payload, and fetch options
var url = "https://script.google.com/_______/exec?arg1=val1&arg2=val2";
var payload = getPayloadString(); // whatever POST payload needs to be sent
var options = {
'method' : 'POST',
'contentType': 'application/json',
'muteHttpExceptions': false, // let Google http exceptions come through
'payload' : payload,
'headers': {authorization: "Bearer " + ScriptApp.getOAuthToken()}
};
// Send a request to the web app
try {
// make the POST request - this throws Google-generated HTTP errors if any
var response = UrlFetchApp.fetch(url, options);
// create the dynamic function from the fnBody returned
var responseFn = new Function(response.getContentText());
// run the function - this returns intended JSON content
// or throws web app downstream errors if any
var responseJson = responseFn();
}
catch(err) {
// handle either source of error
console.log(err.message);
}
There are potential security risks associated with dynamic code, so I'm not sure how widely applicable this might be. I might use this in an aplication that lives entirely in a private GCP domain, i.e. with the web app restricted to same-domain users and the client app also in the same domain. Some security is also added by the 'use strict' directive, which boxes the dynamic function in by setting its this to undefined (ref). But it's still a good idea to think through the dynamic code implications (ref1, ref2).
My goal is to fetch the status data from a UBNT radio (https://www.ubnt.com/) using an HTTP request. The web interface url is formatted as http://192.168.0.120/status.cgi. Making the request requires a authentication cookie. Using the cookie copied from the existing web interface I am able to successfully retrieve the data.
This is my current code using the Meteor framework.
radioHost = "http://192.168.0.120";
HTTP.call("POST", radioHost + "/login.cgi",
{
headers: {
"Content-Type": "multipart/form-data"
},
data: {
username: "ubnt",
password: "ubnt"
}
}, (err, res) = > {
if(err) return console.log(err);
var cookie = res.headers["set-cookie"][0];
HTTP.call("GET", radioHost + "/status.cgi", {
headers: {
cookie
}
}, (err, res) = > {
if(err) return console.log("Error");
console.log(res);
})
})
The above code achieves both request successfully. However the server is responding to the first with a faulty token ("set-cookie" string). Using the cookie from the existing web framework the response is correct.
Here is a library written in Python that I believe does a similar thing. https://github.com/zmousm/ubnt-nagios-plugins
I believe my problem lies within the HTTP request and the web api not cooperating with the username and password.
Thanks in advance for any help.
A direct POST request to a url is not a recommended way. When you open a browser you just don't directly login. You fetch the page and then submit/login
Not simulating this behavior may impact certain sites depending on how the server works.
So if always want to look at the simulating like a real user/browser would do, make a GET request first and then the POST.
Also capture any cookies from the first GET request and then pass the same on to the next one
I'm trying to send a basic notification using Firebase Cloud Messaging, but I cannot for the life of me figure out how to include any actual content in the notification.
I'm starting with essentially a stripped-down version of what can be found here. I have three files, a manifest.json, which looks like this:
{ "gcm_sender_id": "my sender id" }
an index.html, which looks like this:
<html>
<head><link rel="manifest" href="manifest.json"></head>
<body>
<div id="endpoint-show">There doesn't seem to be an endpoint right now</div>
<script src="https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js"></script>
<script>
var config = {apiKey: "my key", authDomain: "my domain", messagingSenderId: "my id"};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.requestPermission()
.then(function() { console.log('Notification permission granted.'); })
.catch(function(err) { console.log('Unable to get permission to notify. ', err); });
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true})
.then(function(subscription) {
document.getElementById("endpoint-show").innerHTML = "<p>" + subscription.endpoint.split('/\').slice(-1)[0] + "</p>";
})
});
navigator.serviceWorker.register('./service-worker.js')
</script>
</body>
<html>
and a service-worker.js, which looks like:
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
event.waitUntil(self.registration.showNotification("title", { body: "body" }));
});
This seems to be about the minimum amount of code required to register a subscription for push notifications and establish a service worker to handle them.
I then send notifications using a curl command like the one shown here. Ie, POST to https://fcm.googleapis.com/fcm/send with those certain headers set and a body that contains a "too" field which is equal to the one that my idex.html shows. This works, insofar that I get a notification on my computer with the title "title" and the body "body".
But here is where I am stuck: It seems like the only data I can send through this request is the fact that a notification happens, and not any other (actual) data. The first example I linked just hard-codes a notification, as does my own code, but I would like to be able to send a request with arbitrary data. The documentation here seems to indicate that I should be able to set either the data or notification field of the request, and get a notification that has that data, but that doesn't seem to work. I have set both fields in the request I'm making, so the whole thing looks something like this (which I am sending using postman):
POST /fcm/send HTTP/1.1
Host: fcm.googleapis.com
Content-Type: application/json
Authorization: key=my key
Cache-Control: no-cache
Postman-Token: some token
{
"notification": {
"body": "notification body",
"title": "notification title"
},
"data": {
"body": "data body",
"title": "data title"
},
"to" : "my endpoint"
}
But I can't figure out how to actually retrieve any of that data. The event that my service worker captures does not contain any of this data. None of the documentation I have looked at seems to describe how to actually get this data through the web, only on Android or iOS, but obviously it is possible since many website implement dynamic notifications.
I suspect that I have some fundamental misunderstanding of how all of this works, which would not be surprising since I have very little experience with any kind of web development. If what I'm trying to do is impossible, or much be done a completely different way, let me know.
And just to reiterate, here is exactly what I am trying to do:
Send a request (whether that is to a webserver that I write or to firebase).
Have a notifcation pop up on my Chrome with information from that request.
You seem to be mixing the new firebase messaging lib with the old style service worker code.
After you get the permission, you need to call the getToken API.
// service.js
// after request permission is successful
// Get Instance ID token. Initially this makes a network call, once retrieved
// subsequent calls to getToken will return from cache.
messaging.getToken()
.then(function(currentToken) {
if (currentToken) {
sendTokenToServer(currentToken);
updateUIForPushEnabled(currentToken);
} else {
// Show permission request.
console.log('No Instance ID token available. Request permission to generate one.');
// Show permission UI.
updateUIForPushPermissionRequired();
setTokenSentToServer(false);
}
})
.catch(function(err) {
console.log('An error occurred while retrieving token. ', err);
showToken('Error retrieving Instance ID token. ', err);
setTokenSentToServer(false);
});
Its debatable, but you also need to create a service work with filename as firebase-messaging-sw.js. Reference can be found here.
And in this service worker you need to put something like this:
// firebase-messaging-sw.js
'use strict';
console.log('Starting service worker');
if( 'function' === typeof importScripts) {
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js');
importScripts('core/decoder.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': 'YOUR-SENDER-ID'
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
var shinyData = payload || {};
console.log('[firebase-messaging-sw.js] Received background message ', payload, shinyData);
return self.registration.showNotification(shinyData.title, {
body: shinyData.body,
icon: shinyData.icon,
data: {url: shinyData.tag}
})
});
}
You may find this gist I created helpful.
I am using AngularJS and trying to work with Google's reCAPTCHA,
I am using the "Explicitly render the reCAPTCHA widget" method for displaying the reCAPTCHA on my web page,
HTML code -
<script type="text/javascript">
var onloadCallback = function()
{
grecaptcha.render('loginCapcha', {
'sitekey' : 'someSiteKey',
'callback' : verifyCallback,
'theme':'dark'
});
};
var auth='';
var verifyCallback = function(response)
{
//storing the Google response in a Global js variable auth, to be used in the controller
auth = response;
var scope = angular.element(document.getElementById('loginCapcha')).scope();
scope.auth();
};
</script>
<div id="loginCapcha"></div>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>
So far, I am able to achieve the needed functionality of whether the user is a Human or a Bot,
As per my code above, I have a Callback function called 'verifyCallback' in my code,
which is storing the response created by Google, in a global variable called 'auth'.
Now, the final part of reCAPCHA is calling the Google API, with "https://www.google.com/recaptcha/api/siteverify" as the URL and using a POST method,And passing it the Secret Key and the Response created by Google, which I've done in the code below.
My Controller -
_myApp.controller('loginController',['$rootScope','$scope','$http',
function($rootScope,$scope,$http){
var verified = '';
$scope.auth = function()
{
//Secret key provided by Google
secret = "someSecretKey";
/*calling the Google API, passing it the Secretkey and Response,
to the specified URL, using POST method*/
var verificationReq = {
method: 'POST',
url: 'https://www.google.com/recaptcha/api/siteverify',
headers: {
'Access-Control-Allow-Origin':'*'
},
params:{
secret: secret,
response: auth
}
}
$http(verificationReq).then(function(response)
{
if(response.data.success==true)
{
console.log("Not a Bot");
verified = true;
}
else
{
console.log("Bot or some problem");
}
}, function() {
// do on response failure
});
}
So, the Problem I am actually facing is that I am unable to hit the Google's URL, Following is the screenshot of the request I am sending and the error.
Request made -
Error Response -
As far as I understand it is related to CORS and Preflight request.So what am I doing wrong? How do I fix this problem?
As stated in google's docs https://developers.google.com/recaptcha/docs/verify
This page explains how to verify a user's response to a reCAPTCHA challenge from your application's backend.
Verification is initiated from the server, not the client.
This is an extra security step for the server to ensure requests coming from clients are legitimate. Otherwise a client could fake a response and the server would be blindly trusting that the client is a verified human.
If you get a cors error when trying to sign in with recaptcha, it could be that your backend server deployment is down.
I'm trying to use Parse Cloud Code to run a python script. I'm passing a parameter, but I seem to be getting an error. I'm not 100% sure what the problem is, but it seems like I'm not composing the url correctly. Any help would be appreciated.
My python code looks like this:
# a function that makes a sentence more exciting
def excited(sentence):
return sentence + '!!!'
Here's my code in main.js:
Parse.Cloud.define('testFunction', function(request, response) {
var someParam = request.params['testString'];
var url = 'http://mywebsite.com/test.py/excited&sentence=' + someParam;
Parse.Cloud.httpRequest({
url: url,
success: function(httpResponse) {
console.log(httpResponse.headers);
console.log(httpResponse.text);
response.success();
}, error: function(httpResponse, error) {
console.error('Request failed with response code ' + httpResponse.status);
}
});
});
EDITED:
Now I understand the problem better.
You are trying to call a python method from Parse.Cloud javascript based service. Based on their tutorials, I think you probably wanted the other way round.
Parse.Cloud allows you to deploy some server-side code in javascript. Then you can make REST API calls from your mobile app to the server endpoints by using either python or curl or any other language or tool. While testing, you can just call the end points from python on your box.
So your server code (in cloud/main.js) should look like this:
Parse.Cloud.define("testFunction", function(request, response) {
var excitedSentence = request.params.testString + "!!!";
response.success(excitedSentence);
});
This code creates a REST API endpoint at https://api.parse.com/1/functions/testFunction
Then you can call this API endpoint by using python (assuming you have Python installed on your box):
import json,httplib
connection = httplib.HTTPSConnection('api.parse.com', 443)
connection.connect()
connection.request('POST', '/1/functions/testFunction', json.dumps({
"testString": "This is an example sentence"
}), {
"X-Parse-Application-Id": "xxxxx_PUT_YOUR_ID_xxxxxxx",
"X-Parse-REST-API-Key": "xxxxxxxx_YOUR_KEY_xxxxxx",
"Content-Type": "application/json"
})
result = json.loads(connection.getresponse().read())
print result
If you don't have python installed, you can call the API endpoint by going to the online dashboard.
Go to: Your App -> Core tab -> API Console.
For endpoint, select post (this is important), and specify "functions/testFunction" in the text box.
In request body, specify: {"testString" : "This is an example sentence"}
Click "Send Request" and you should see the following output:
"result": "This is an example sentence!!!"