JavaScript alternative to Python requests.Session() - javascript

I have recently developed a Python API for a chatbot and made a demo using a quick python script:
s = request.Session()
user_response = None
while True:
chat_response = s.get(url='http://localhost:5000/chat', json={'user_input': user_response}).json()
if chat_response['is_end']:
break
if 'text' in response_json:
print '\t' + response_json['text']
user_response = raw_input()
When the chat route is called I set a unique session key
if 'session_id' not in session:
session['session_id'] = chat_utils.id_generator()
This works great on the command line, the session id is used to keep track of the conversation on the server. However I am trying to implement this in a JavaScript front end. Is there an equivalent to the line
s = request.Session()
Currently I am unable to store session information so the initial question is repeated with a different generated key (see below).
This is generated using BotUI
The call to the API is made using the following code:
function chat(){
botui.action.text({
delay: 1000,
action: {
placeholder: 'User response here'
}
}).then(function (res) {
sendxhr(res, textResponse)
});
}
function sendxhr(user_input, formatter){
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://localhost:5000/chat');
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("user_input", user_input);
xhr.onload = function () {
var res = JSON.parse(xhr.responseText)
console.log(res)
formatter(res.text)
}
xhr.send();
}
The API call works but as there is no session each time the chat route starts a new session.

The solution to this is to serve the html. Rather than opening the html locally I added a route to serve static html.
#app.route("/")
def index():
return render_template('index.html')
The javascript is loaded in the html and everything works nicely.

Related

Download Zip file PushStreamContent Javascript

i'am asking the about the right way to downlaod a PushStreamContent present in Post Request ,
i already preapred the backend request , something like this
private static HttpClient Client { get; } = new HttpClient();
public HttpResponseMessage Get()
{
var filenamesAndUrls = new Dictionary<string, string>
{
{ 'README.md', 'https://raw.githubusercontent.com/StephenClearyExamples/AsyncDynamicZip/master/README.md' },
{ '.gitignore', 'https://raw.githubusercontent.com/StephenClearyExamples/AsyncDynamicZip/master/.gitignore'},
};
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new PushStreamContent(async (outputStream, httpContext, transportContext) =>
{
using (var zipStream = new ZipOutputStream(outputStream))
{
foreach (var kvp in filenamesAndUrls)
{
zipStream.PutNextEntry(kvp.Key);
using (var stream = await Client.GetStreamAsync(kvp.Value))
await stream.CopyToAsync(zipStream);
}
}
}),
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "MyZipfile.zip" };
return result;
}
and in the front part , i used axios to send Post request and with the result i create blob to download it (i modified the backend to support Post)
but the download take much time and i think this a wrong way to use PushStreamContent and i should use EventSource or something like this.
Thank you.
After few days of search , there are two solution :
Change the download request to Get Request instead of Post.
Use Fetch instead of axios http request and with the response send it to streamsaver package , it's really amazing and instantly start the download on the fly.
I agree with houssem about changing it to a get request.
I'm the creator of StreamSaver and occasionally i search for ppl talking about it and help someone in need. I often have to tell ppl that it's better to use the server to save files rather than using StreamSaver. StreamSaver is meant for client generated content (good for stuff like WebTorrent or webcam recording)
Download can only happen when you navigate the resource. That means you can't use ajax (xhr, fetch, axios, etc) to trigger a download
a <a href>, <iframe>, location.href = all works fine, but if you really need it to be a post request, then you can also submit a <form> but you will have to do it with application/multipart or URLEncoded and not with a json request or anything else. the con of this is that you can't use custom request headers like a authentication header (unless you use service worker to add them)
in such cases it is better with cookies that gets sent along every request

Recieving web requests from playfab to aws server

I have a C# dotnetcore console app running on an AWS instance, and I would like to add in communication between this and my playfab cloudscript.
I can communicate from the C# console to playfab, that was simple just using the playfab nuget package. However I'm having trouble going the other way.
I only ever want to send a few different simple messages, so im not looking for anything too complex. What I have done so far, is I added the following to the start of my console application:
var listener = new HttpListener();
listener.Prefixes.Add("http://+:80/");
listener.Start();
Writer.WriteBuffer("Listening...");
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
Writer.WriteBuffer("Context: " + context.ToString());
Writer.WriteBuffer("request: " + request.ToString());
Writer.WriteBuffer("response: " + response.ToString());
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
listener.Stop();
Writer.WriteBuffer is a wrapper for writing Console.Write essentially, just formats stuff in a much better way for me. I see the "listening..." come up, so great its listening.
Now, I copied an example from playfab and just adapted it very slightly. cloudscript is in js, so here is what I am running from playfab:
var headers = {
"X-MyCustomHeader": "Some Value"
};
var body = {
input: args,
userId: currentPlayerId,
mode: "foobar"
};
var url = "http://11.111.111.1/";
var content = JSON.stringify(body);
var httpMethod = "post";
var contentType = "application/json";
// The pre-defined http object makes synchronous HTTP requests
var response = http.request(url, httpMethod, content, contentType, headers);
return { responseContent: response };
11.111.111.1 is where I put the IP address of the AWS instance (I've changed it for obvious reasons).
When running this, I get "httpRequestError": "Timeout".
When I check on AWS, I have nothing else printed out other than "Listening...", so it hasn't handled anything yet.
I'm not too sure where the problem lies to be honest.

Sending data from JavaScript to Python function locally with AJAX

I am trying to build a website where a user can enter text, which will be picked up via javascript, and sent to a python function where it will be posted to twitter. For the time being, the python function is being stored locally, along with the rest of the site. However, my AJAX isn't too great and I'm having a few issues.
I have written AJAX code which sends a POST request to the python function with the tweet, and the response is the entire python script. No connection is made to the socket my script is listening to. Below is the AJAX function and the python script. Any ideas what's going on?
Thanks in advance for any help!
$(function(){
$('#PostTweet').on('click', function(e) {
var tweet = document.getElementById("theTweet").value;
var len = tweet.length;
if(len > 140){
window.alert("Tweet too long. Please remove some characters");
}else{
callPython(tweet);
}
});
});
function callPython(tweet){
window.alert("sending");
$.ajax({
type: "POST",
url: "tweet.py",
data: tweet,
success: function(response){
window.alert(response);
}
})
}
And the Python Script:
from OAuthSettings import settings
import twitter
from socket import *
consumer_key = settings['consumer_key']
consumer_secret = settings['consumer_secret']
access_token_key = settings['access_token_key']
access_token_secret = settings['access_token_secret']
s = socket()
s.bind(('', 9999))
s.listen(4)
(ns, na) = s.accept()
def PostToTwits(data):
try:
api = twitter.Api(
consumer_key = consumer_key,
consumer_secret = consumer_secret,
access_token_key = access_token_key,
access_token_secret = access_token_secret)
api.PostUpdate(data)
makeConnection(s)
except twitter.TwitterError:
print 'Post Unsuccessful. Error Occurred'
def makeConnection(s):
while True:
print "connected with: " + str(na)
try:
data = ns.recv(4096)
print data
PostToTwits(data)
except:
ns.close()
s.close()
break
makeConnection(s)
Your problem is that you are working with pure sockets which know nothing about HTTP protocol. Take a look at Flask or Bottle web micro frameworks to see how to turn python script or function into web endpoint.
you need a webserver so that your can make request via web browser.
you can web framework like flask or django or you can use webpy.
A simple example using webpy from their website
import web
urls = (
'/(.*)', 'hello'
)
app = web.application(urls, globals())
class hello:
def GET(self, name):
if not name:
name = 'World'
return 'Hello, ' + name + '!'
if __name__ == "__main__":
app.run()
then you call url(your python function) from javascript.
You can totally write a simple web server using sockets, and indeed you've done so. But this approach will quickly get tedious for anything beyond a simple exercise.
For example, your code is restricted to handling a single request handler, which goes to the heart of your problem.
The url on the post request is wrong. In your setup there is no notion of a url "tweet.py". That url would actually work if you were also serving the web page where the jquery lives from the same server (but you can't be).
You have to post to "http://localhost:9999" and you can have any path you want after:"http://localhost:9999/foo", "http://localhost:9999/boo". Just make sure you run the python script from the command line first, so the server is listening.
Also the difference between a get and a post request is part of the HTTP protocol which your simple server doesn't know anything about. This mainly means that it doesn't matter what verb you use on the ajax request. Your server listens for all HTTP verb types.
Lastly, I'm not seeing any data being returned to the client. You need to do something like ns.sendall("Some response"). Tutorials for building a simple http server abound and show different ways of sending responses.

Accessing Google Drive from a Firefox extension

I'm trying to access (CRUD) Google Drive from a Firefox extension. Extensions are coded in Javascript, but neither of the two existing javascript SDKs seem to fit; the client-side SDK expects "window" to be available, which isn't the case in extensions, and the server-side SDK seems to rely on Node-specific facilities, as a script that works in node no longer does when I load it in chrome after running it through browserify. Am I stuck using raw REST calls? The Node script that works looks like this:
var google = require('googleapis');
var readlineSync = require('readline-sync');
var CLIENT_ID = '....',
CLIENT_SECRET = '....',
REDIRECT_URL = 'urn:ietf:wg:oauth:2.0:oob',
SCOPE = 'https://www.googleapis.com/auth/drive.file';
var oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
scope: SCOPE // If you only need one scope you can pass it as string
});
var code = readlineSync.question('Auth code? :');
oauth2Client.getToken(code, function(err, tokens) {
console.log('authenticated?');
// Now tokens contains an access_token and an optional refresh_token. Save them.
if(!err) {
console.log('authenticated');
oauth2Client.setCredentials(tokens);
} else {
console.log('not authenticated');
}
});
I wrap the node GDrive SDK using browserify on this script:
var Google = new function(){
this.api = require('googleapis');
this.clientID = '....';
this.clientSecret = '....';
this.redirectURL = 'urn:ietf:wg:oauth:2.0:oob';
this.scope = 'https://www.googleapis.com/auth/drive.file';
this.client = new this.api.auth.OAuth2(this.clientID, this.clientSecret, this.redirectURL);
}
}
which is then called using after clicking a button (if the text field has no code it launches the browser to get one):
function authorize() {
var code = document.getElementById("code").value.trim();
if (code === '') {
var url = Google.client.generateAuthUrl({access_type: 'offline', scope: Google.scope});
var win = Components.classes['#mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('navigator:browser');
win.gBrowser.selectedTab = win.gBrowser.addTab(url);
} else {
Google.client.getToken(code, function(err, tokens) {
if(!err) {
Google.client.setCredentials(tokens);
// store token
alert('Succesfully authorized');
} else {
alert('Not authorized: ' + err); // always ends here
}
});
}
}
But this yields the error Not authorized: Invalid protocol: https:
It is possible though, depending on the use case, it might also of limited interest.
Firefox ships with a tiny http server, just the bare bones. It is included for test purposes but this is not a reason to overlook it.
Lets follow the quickstart guide for running a Drive app in Javascript
The tricky part is to set the Redirect URIs and the Javascript Origins. Obviously the right setting is http://localhost, but how can you be sure that every user has port 80 available?
You can't and, unless you have control over your users, no port is guaranteed to work for everyone. With this in mind lets choose port 49870 and pray.
So now Redirect URIs and the Javascript Origins are set to http://localhost:49870
Assuming you use Add-on SDK, save the quickstart.html (remember to add your Client ID) in the data directory of your extension. Now edit your main.js
const self = require("sdk/self");
const { Cc, Ci } = require("chrome");
const tabs = require("sdk/tabs");
const httpd = require("sdk/test/httpd");
var quickstart = self.data.load("quickstart.html");
var srv = new httpd.nsHttpServer();
srv.registerPathHandler("/gdrive", function handler(request, response){
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
let converter = Cc["#mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
response.write(converter.ConvertFromUnicode(quickstart));
})
srv.start(49870);
tabs.open("http://localhost:49870/gdrive");
exports.onUnload = function (reason) {
srv.stop(function(){});
};
Notice that quickstart.html is not opened as a local file, with a resource: URI. The Drive API wouldn't like that. It is served at the url http://localhost:49870/gdrive. Needless to say that instead of static html we can use a template or anything else. Also the http://localhost:49870/gdrive can be scripted with a regular PageMod.
I don't consider this a real solution. It's just better than nothing.
From here https://developer.mozilla.org/en/docs/Working_with_windows_in_chrome_code you could try window = window || content || {}
Use the JavaScript client API and not the node.js client. Although browserify will make it work. You will have to expose your client secret in the latter. The flow of client side authentication is very diff than server side. Refer to https://developers.google.com/accounts/docs/OAuth2
Having said all this. Its really not that difficult to implement an app with REST based calls. The methods in all client libraries mimic the corresponding REST URLs. You could set up some functions of your own to handle request and response and the rest would feel the same.

Basic Ajax send/receive with node.js

So I'm trying to make a very basic node.js server that with take in a request for a string, randomly select one from an array and return the selected string. Unfortunately I'm running into a few problems.
Here's the front end:
function newGame()
{
guessCnt=0;
guess="";
server();
displayHash();
displayGuessStr();
displayGuessCnt();
}
function server()
{
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","server.js", true);
xmlhttp.send();
string=xmlhttp.responseText;
}
This should send the request to server.js:
var http = require('http');
var choices=["hello world", "goodbye world"];
console.log("server initialized");
http.createServer(function(request, response)
{
console.log("request recieved");
var string = choices[Math.floor(Math.random()*choices.length)];
console.log("string '" + string + "' chosen");
response.on(string);
console.log("string sent");
}).listen(8001);
So clearly there are several things going wrong here:
I get the feeling the way I am "connecting" these two files isn't correct both in the xmlhttp.open method and in using response.on to send the string back to the front end.
I'm a little confused with how I call this page on localhost. The front end is named index.html and the sever posts to 8001. What address should I be go to on localhost in order to access the initial html page after I have initialized server.js? Should I change it to .listen(index.html) or something like that?
are there other obvious problems with how I am implementing this (using .responsetext etc.)
(sorry for the long multi-question post but the various tutorials and the node.js source all assume that the user already has an understanding of these things.)
Your request should be to the server, NOT the server.js file which instantiates it. So, the request should look something like this: xmlhttp.open("GET","http://localhost:8001/", true); Also, you are trying to serve the front-end (index.html) AND serve AJAX requests at the same URI. To accomplish this, you are going to have to introduce logic to your server.js that will differentiate between your AJAX requests and a normal http access request. To do this, you'll want to either introduce GET/POST data (i.e. call http://localhost:8001/?getstring=true) or use a different path for your AJAX requests (i.e. call http://localhost:8001/getstring). On the server end then, you'll need to examine the request object to determine what to write on the response. For the latter option, you need to use the 'url' module to parse the request.
You are correctly calling listen() but incorrectly writing the response. First of all, if you wish to serve index.html when navigating to http://localhost:8001/, you need to write the contents of the file to the response using response.write() or response.end(). First, you need to include fs=require('fs') to get access to the filesystem. Then, you need to actually serve the file.
XMLHttpRequest needs a callback function specified if you use it asynchronously (third parameter = true, as you have done) AND want to do something with the response. The way you have it now, string will be undefined (or perhaps null), because that line will execute before the AJAX request is complete (i.e. the responseText is still empty). If you use it synchronously (third parameter = false), you can write inline code as you have done. This is not recommended as it locks the browser during the request. Asynchronous operation is usually used with the onreadystatechange function, which can handle the response once it is complete. You need to learn the basics of XMLHttpRequest. Start here.
Here is a simple implementation that incorporates all of the above:
server.js:
var http = require('http'),
fs = require('fs'),
url = require('url'),
choices = ["hello world", "goodbye world"];
http.createServer(function(request, response){
var path = url.parse(request.url).pathname;
if(path=="/getstring"){
console.log("request recieved");
var string = choices[Math.floor(Math.random()*choices.length)];
console.log("string '" + string + "' chosen");
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(string);
console.log("string sent");
}else{
fs.readFile('./index.html', function(err, file) {
if(err) {
// write an error response or nothing here
return;
}
response.writeHead(200, { 'Content-Type': 'text/html' });
response.end(file, "utf-8");
});
}
}).listen(8001);
console.log("server initialized");
frontend (part of index.html):
function newGame()
{
guessCnt=0;
guess="";
server();
displayHash();
displayGuessStr();
displayGuessCnt();
}
function server()
{
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","http://localhost:8001/getstring", true);
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
string=xmlhttp.responseText;
}
}
xmlhttp.send();
}
You will need to be comfortable with AJAX. Use the mozilla learning center to learn about XMLHttpRequest. After you can use the basic XHR object, you will most likely want to use a good AJAX library instead of manually writing cross-browser AJAX requests (for example, in IE you'll need to use an ActiveXObject instead of XHR). The AJAX in jQuery is excellent, but if you don't need everything else jQuery offers, find a good AJAX library here: http://microjs.com/. You will also need to get comfy with the node.js docs, found here. Search http://google.com for some good node.js server and static file server tutorials. http://nodetuts.com is a good place to start.
UPDATE: I have changed response.sendHeader() to the new response.writeHead() in the code above !!!
Express makes this kind of stuff really intuitive. The syntax looks like below :
var app = require('express').createServer();
app.get("/string", function(req, res) {
var strings = ["rad", "bla", "ska"]
var n = Math.floor(Math.random() * strings.length)
res.send(strings[n])
})
app.listen(8001)
https://expressjs.com
If you're using jQuery on the client side you can do something like this:
$.get("/string", function(string) {
alert(string)
})
I was facing following error with code (nodejs 0.10.13), provided by ampersand:
origin is not allowed by access-control-allow-origin
Issue was resolved changing
response.writeHead(200, {"Content-Type": "text/plain"});
to
response.writeHead(200, {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin' : '*'});
Here is a fully functional example of what you are trying to accomplish. I created the example inside of hyperdev rather than jsFiddle so that you could see the server-side and client-side code.
View Code:
https://hyperdev.com/#!/project/destiny-authorization
View Working Application: https://destiny-authorization.hyperdev.space/
This code creates a handler for a get request that returns a random string:
app.get("/string", function(req, res) {
var strings = ["string1", "string2", "string3"]
var n = Math.floor(Math.random() * strings.length)
res.send(strings[n])
});
This jQuery code then makes the ajax request and receives the random string from the server.
$.get("/string", function(string) {
$('#txtString').val(string);
});
Note that this example is based on code from Jamund Ferguson's answer so if you find this useful be sure to upvote him as well. I just thought this example would help you to see how everything fits together.

Categories

Resources