I try to create a little website using web.py and webpysocketio, and I have a problem: Web.py doesn't seem to find any file besides the index.html.
Here's my webpy app:
import web
from socketio import SocketIOServer
from gevent import monkey
monkey.patch_all()
from webpy_socketio import *
urls = (
'/', 'index',
)
urls += socketio_urls
app = web.application(urls, globals())
SOCKETIO_HOST = ""
SOCKETIO_PORT = 8080
application = app.wsgifunc()
if __name__ == "__main__":
SocketIOServer((SOCKETIO_HOST, SOCKETIO_PORT), application, resource="socket.io").serve_forever()
class index:
def GET(self):
render = web.template.render('templates/')
return render.index()
#on_message(channel="my channel")
def message(request, socket, context, message):
socket.send_and_broadcast_channel(message)
In my template folder I have the index.html (and the socketio_scripts.html):
<html>
<head>
<meta charset="utf-8">
<title>webpysocketio TEST</title>
<object type="text/html" data="socketio_scripts.html">
<script>
var socket = new io.Socket();
socket.connect();
socket.on('connect', function() {
socket.subscribe('my channel');
socket.send('asdf');
});
</script>
</object>
</head>
<body>
<div>
</div>
</body>
</html>
Now when I run the webiste, after I visit it in a browser, I get the following on my terminal:
IP - - [DATE] "GET /socketio_scripts.html HTTP/1.1" 404 135 0.004273
Why does it not find the other html file?
I suspect that /socketio_scripts.html is not the correct url for that file - it seems odd that that file would be at the root of your document tree.
According to several pages, it should be in .../templates/. I also doubt very much it would ever be accessed by GET
Have a look at the web.py tutorial. One big difference with your code is that all the pages have to be listed in the urls dispatch table:
urls = (
'/', 'hello',
'/bye', 'bye')
The static files which you dont want to be served from GET function have to be placed in a static file , in your example the "socketio_scripts.html"
and accessed by the full url "http://localhost/static/socketio_scripts.html". http://webpy.org/cookbook/staticfiles
Related
I have a web flask application I have deployed using Windows IIS.
I am able to access the website and it shows up but when I click on the "start button" I have built within the website, which causes a series of API calls using the requests library. I am seeing that the data is not being "GET" or "PUT".
When I run the code using Visual Studio Code I am seeing the data being populated but once it is deployed using IIS the data is not being populated which leads me to believe it could possibly be some type of security measure within the IIS.
I have thought that the reason may be because it is just a http website currently and the browser is not allowing API requests but I have also tried to make the website https using this article (https://www.educba.com/flask-https/) explaining the adhoc ssl context method
I am able to access the deployment URL and my application shows up but the functionality of the API is still not working so I am thinking that the HTTP and HTTPS is not the root cause.
Here is my app.py currently
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def index():
return render_template('index.html')
#app.route("/active", methods=['GET', 'POST'])
def active():
try:
import json
import requests
import pandas as pd
EXUser = 'API'
EXPw = 'test'
EXI = 'Example'
url = requests.get(
f'https://{EXI}.api.com/api/values/0153ecf7-9e90-4aef-ad0b-ed8d0aa7092f', auth=(EXUser,EXPw ))
myToken = 'exampletoken'
myUrl = 'https://api2.com/api/v1/devices.json'
head = {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
'Authorization': 'Token Example',
'Cookie': 'XSRF-TOKEN=Example'
}
sfresp = requests.get(myUrl, headers=head)
r = json.loads(url.content)
sr = json.loads(sfresp.content)
df = pd.json_normalize(sr, 'devices')
df1 = pd.json_normalize(r, record_path='userDefinedValues')
df1.set_index('userDefinedKeyId', inplace=True)
owner = df1['value']['66ea21fb-036f-4081-ac58-6b4c9e3caa54']
lon = df['device.location.lng'][0].astype(str)
bat = df['device.battery_status'][0].astype(str)
lat = df['device.location.lat'][0].astype(str)
did = df['device.id'][0].astype(str)
payload = 'properties=[ { "name": "Owner", "value": "' + owner + '" } ]'
myUrl2 = f'https://api2.com/api/v1/devices/{did}/custom_properties.json'
test = requests.put(myUrl2, headers=head, data=payload)
payload2 = {'userDefinedValues': [{'userDefinedKeyId': 'ec94503d-efb4-4b04-82ac-1717beff85b9',
'userDefinedKeyName': 'Latitude',
'value': lat},
{'userDefinedKeyId': 'a64d94c5-e156-4f2d-b218-82597bfa95ce',
'userDefinedKeyName': 'Longitude',
'value': lon},
{'userDefinedKeyId': '537b843a-044e-45c0-9625-93c4f52c539d',
'userDefinedKeyName': 'Battery Level',
'value': bat}]}
test1 = requests.patch(f'https://{EXI}.api.com/api/assets/0153ecf7-9e90-4aef-ad0b-ed8d0aa7092f', auth=(
EXUser, AVIPw), data=json.dumps(payload2))
return render_template('index.html')
except:
return render_template('index.html')
if __name__ == '__main__':
app.run(ssl_context='adhoc')
index.html code of the single start button that renders on the page
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.3.1/dist/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Data Integration Demo</title>
</head>
<body>
<form action="/active" method="POST">
<input type="submit" value="Start">
</form>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.14.7/dist/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#4.3.1/dist/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
Just to reiterate the app is deployed using IIS, I have followed the steps here (https://medium.com/#dpralay07/deploy-a-python-flask-application-in-iis-server-and-run-on-machine-ip-address-ddb81df8edf3). I am able to browse to the application using localhost but the part of the script is not running that PUTs and GETs data. The code works when I run it using Visual Studio Code but once it has been deployed it no longer does the API requests process within the code. I tried to replace values into example values so I may have missed some sections in the code, I can assure that it does work.
It appears that Flask assumes that the server is returning html to the client (browser).
Here's a simple example;
import json
from flask import Flask
app = Flask(__name__)
#app.route("/")
def home():
msg = ['Hello, world!']
return json.dumps(msg) + '\n'
This code works as expected and returns the desired json;
$ curl -s http://localhost:5000/
["Hello, world!"]
But if I introduce an error;
import json
from flask import Flask
app = Flask(__name__)
#app.route("/")
def home():
msg = ['Hello, world!']
return json.dumps(XXmsg) + '\n'
Then Flask emits the error wrapped in several pages worth of html, starting like;
$ curl -s http://localhost:5000/
<!DOCTYPE html>
<html>
<head>
<title>NameError: name 'XXmsg' is not defined
// Werkzeug Debugger</title>
<link rel="stylesheet" href="?__debugger__=yes&cmd=resource&f=style.css">
<link rel="shortcut icon"
href="?__debugger__=yes&cmd=resource&f=console.png">
<script src="?__debugger__=yes&cmd=resource&f=debugger.js"></script>
<script>
var CONSOLE_MODE = false,
EVALEX = true,
EVALEX_TRUSTED = false,
SECRET = "Mq5TSy6QE4OuOHUfvk8b";
</script>
</head>
<body style="background-color: #fff">
<div class="debugger">
Emitting html makes sense if you're creating a page load app. But I'm creating an api that only returns json.
Is there anyway to prevent Flask from emitting html at all?
Thanks
Mike
Have a look at the section Returning API Errors as JSON of the Flask docs.
Basically, you have to replace the default error handler with a function that returns the error as json. A very basic example:
#app.errorhandler(HTTPException)
def handle_exception(exception):
response = exception.get_response()
response.content_type = "application/json"
response.data = json.dumps({"code": exception.code})
return response
The accepted response gives a good hint for handling HTTPException but it won't work for all exceptions unless you create a handler for the mother of all exceptions:Exception. And you might not want to do this for security reasons, if you have some custom defined exceptions with sensible data it'll get handled by this handler.
I suspect the true reason you have those lengthy html responses is because you started your flask app with the --debug option.
I'm building a little server that sends a page with a script in javascript, it works when I try to open it in my browser, but if I request the page from the server, the page is recived, but not the script, so I get these errors:
Script.js is missing between the files:
Looks strange because from the network session i can see a request for the script with the status 200:
The js file i'm tryng to add is Chart.js, so I can't add it internally, it would become impossible to work with it, but for now the server is just a few lines of code in python that use the SimpleHTTPRequestHandler, and I'm probably going to replace it, may it be because the SimpleHTTPRequestHandler can't handle multiple file requests?
Here's the code, tried to make a snippet but it does't work there too (probably that's just me never wrote a snippet before):
HTML:
<!doctype html>
<html>
<head>
<script src="script.js"></script>
</head>
<body>
<p id = "paragraph"></p>
<script>
document.getElementById('paragraph').innerHTML = sayHello();
</script>
</body>
</html>
JS:
function sayHello(){
return "HelloWorld!"
}
Here is the python server script:
from http.server import HTTPServer, BaseHTTPRequestHandler
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
with open("index.html", "r") as page:
self.wfile.write(page.read())
httpd = HTTPServer(("192.168.1.100", 8000), SimpleHTTPRequestHandler)
httpd.serve_forever()
I think you get an element with id, tag name or class Name and add file
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function() {
callFunctionFromScript();
}
script.src = 'path/to/your-script.js';
head.appendChild(script);
also check this link
Program description: I already have a functioning program that runs on console window, but I'd like to present its output on a locally hosted web page. The program consists on getting lyrics for currently playing songs by making requests to Spotify's API. I store the current lyrics in a "lyrics.txt" file.
What I want:
Change the web page from the running lyrics program when it detects the song has changed.
[EDIT:]
Is there a way to make the flask page display a variable, that is updated by a python request.post of the lyrics app to the flask url with the updated variable as the data?
What I have:
I'm using Flask as the framework since its a one local web page.
import os, io
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def test():
'''reads the current lyrics file and passes it to the web page
manually reload the page to update lyrics'''
with io.open('lyrics.txt', 'r') as f:
HEAD = f.readline().strip("\n")
BODY = f.read().split('\n')
lyrics = {"HEAD": HEAD, "BODY": BODY}
return render_template("home.html", lyrics=lyrics)
if __name__ == "__main__":
app.run(debug=1)
link to lyrics app github
You would need JavaScript/AJAX on page which periodically sends request for new content and Flask should send current content from file.
In this example I use jQuery.get() to send request to server, and setTimeout() to repeat it periodically.
Flask sends current time to show different content.
import datetime
from flask import Flask, render_template_string
app = Flask(__name__)
#app.route("/")
def index():
return render_template_string("""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.0.min.js"></script>
<script type="text/javascript">
function updater() {
$.get('/data', function(data) {
$('#time').html(data); // update page with new data
});
};
setInterval(updater, 1000); // run `updater()` every 1000ms (1s)
</script>
</head>
<body>
Date & Time: <span id="time"><span>
</body>
</html>""")
#app.route('/data')
def data():
"""send current content"""
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if __name__ == "__main__":
app.run(debug=True)
EDIT:
The same using standard fetch() without external libraries.
Code has to be after <span>
import datetime
from flask import Flask, render_template_string
app = Flask(__name__)
#app.route("/")
def index():
return render_template_string("""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
</head>
<body>
Date & Time: <span id="time"><span>
<script type="text/javascript">
var time_span = document.getElementById("time");
function updater() {
fetch('/data')
.then(response => response.text())
.then(text => (time_span.innerHTML = text)); // update page with new data
}
setInterval(updater, 1000); // run `updater()` every 1000ms (1s)
</script>
</body>
</html>""")
#app.route('/data')
def data():
"""send current content"""
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if __name__ == "__main__":
app.run(debug=True)
I am trying to access an external url https://data.dublinked.ie/cgi-bin/rtpi/realtimebusinformation?stopid=184&format=json in a Flask view
I get the error,
Not Found
The requested URL was not found on the server. If you entered the URL
manually please check your spelling and try again.
Is that my local server that flask is looking for this url on. And if so why? I am running flask locally.
The view, services.py
from flask import Flask, Response
import json
import urllib2
app = Flask(__name__)
#app.route('/')
def test():
return 'Everything is running!'
#app.route('/stopid')
def stopid():
dublin_bus_url = "https://data.dublinked.ie/cgi-bin/rtpi/realtimebusinformation?stopid=184&format=json"
response = urllib2.urlopen(dublin_bus_url)
json_response = json.load(response)
routes = set()
for result in json_response["results"]:
routes.add(result["route"])
return json.dumps(list(routes))
if __name__ == '__main__':
app.run()
The index.html and script is,
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
d3.json("/stopid", function(error, routes) {
routes.forEach(function(route) {
console.log(route)
});
});
</script>
</body>
</html>
I am new to Flask but this must not be the way to deal with an external link in a view.
The code above is adopted from this excellent tutorial for the Donorschoose api.
https://youtu.be/bzl4hCH2CdY
https://github.com/VidyaSource/starting-with-data
Thanks,
If we assume that the HTML file is not being served by flask:
You need to enable Cross origin resource sharing. You can do this by creating a response and setting it's header Access-Control-Allow-Origin to *: that is everyone. Or you can set it to your own domain when deploying.
resp.headers['Access-Control-Allow-Origin'] = '*'
Also, you're calling d3.json("/stopid" ... you need to change this to:
d3.json("http://localhost:5000/stopid" ...
Complete code:
from flask import Flask, Response, jsonify
import json
import urllib2
app = Flask(__name__)
#app.route('/')
def test():
return 'Everything is running!'
#app.route('/stopid')
def stopid():
dublin_bus_url = "https://data.dublinked.ie/cgi-bin/rtpi/realtimebusinformation?stopid=184&format=json"
my_response = urllib2.urlopen(dublin_bus_url)
json_response = json.load(my_response)
routes = set()
for result in json_response["results"]:
routes.add(result["route"])
resp = jsonify(list(routes))
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
if __name__ == '__main__':
app.run()
If the HTML is being served by flask, there is no need to enable cross origin sharing.
#app.route('/d3')
def d3():
return render_template('d3.html')
Call the link to this url using:
d3.json("{{ url_for('stopid') }}", ...
But this isn't exactly reliable, because you don't want to use the api using javascript when you can do it in flask itself.