I am attempting to have the url input be (some kind of url)/page/(whatever page number of the template we want). I am having trouble with this, and am not sure what the issue is. The first part of my code goes as follows:
from wsgiref.simple_server import make_server
from wsgiref.util import setup_testing_defaults
routing_table = {}
def route(url, func):
routing_table[url] = func
def find_path(url):
if url in routing_table:
return routing_table[url]
else:
return None
def app(environ, start_response):
setup_testing_defaults(environ)
handler = find_path(environ['PATH_INFO'])
if handler is None:
status = '404 Not Found'
body = "<html><body><h1>Page Not Found</h1></body></html>"
else:
status = '200 OK'
body = handler()
headers = [('Content-type', 'text/html: charset=utf-8')]
start_response(status, headers)
return [body.encode("utf-8")]
def run(ip, port):
myserver = make_server(ip, port, app)
print("Serving testings of wsgi at http://%s:%s" % (ip, port))
myserver.serve_forever()
The next part of the code is where I believe the main issue is occurring at page(page_id):
import test
import re
def index():
return "This is the main page"
def hello():
return "Hi, how are you?"
def page(page_id):
return "This is page number: %d" % page_id
if __name__ == '__main__':
test.route("/", index)
test.route("/Hello", hello)
test.route('/page/<page_id>', page)
test.run("127.0.0.1", 8000)
My thinking is that we need to import the template, and have the logic defined within the templates, themselves. However, when I attempt to do this, I am unable to "from python import Template" and utilize the template(myTemplates.tpl). I believe my syntax may be incorrect, but python.org has shown no suggestions, thus far.
In find_path you are simply comparing the given string to one of the URLs on the routing table
if url in routing_table:
So the only page you could actually reach for the '/page/<page_id>' route is the literal '/page/<page_id>'.
What you would need to do is parse the URL to see if it matches the format you are passing in, not compare the static string. Makes sense right?
In that case you might wanna look into regular expressions: https://docs.python.org/2/library/re.html
Related
So this is my code.
from flask import Flask, request
from flask_restful import Resource, Api
app = Flask(__name__)
app.config['DEBUG'] = True
api = Api(app)
# Make the WSGI interface available at the top level so wfastcgi can get it.
wsgi_app = app.wsgi_app
class Default(Resource):
def get(self, name):
"""Renders a sample page."""
return "Hello " + name
class LiveStats(Resource):
def get(self, url):
return "Trying to get " + url
# data = request.get(url)
# return data
api.add_resource(Default, '/default/<string:name>') # Route_1
api.add_resource(LiveStats, '/liveStats/<path:url>') # Route_2
if __name__ == '__main__':
import os
HOST = os.environ.get('SERVER_HOST', 'localhost')
try:
PORT = int(os.environ.get('SERVER_PORT', '5555'))
except ValueError:
PORT = 5555
app.run(HOST, PORT)
Now firstly this post helped a lot. how-to-pass-urls-as-parameters-in-a-get-request-within-python-flask-restplus
Changing what I originally had.
api.add_resource(LiveStats, '/liveStats/<string:url>') # Route_2
to this
api.add_resource(LiveStats, '/liveStats/<path:url>') # Route_2
got rid of 404 errors that I had but now I am noticing that it's not passing all of the url.
Example if I try this
localhost:60933/liveStats/http://address/Statistics?NoLogo=1%26KSLive=1
I get this
Trying to get http://address/Statistics
so it has taken off ?NoLogo=1%26KSLive=1
How do you prevent this?
All characters after the ? are considered parameters. From the docs:
To access parameters submitted in the URL (?key=value) you can use the args attribute:
searchword = request.args.get('key', '')
We recommend accessing URL
parameters with get or by catching the KeyError because users might
change the URL and presenting them a 400 bad request page in that case
is not user friendly.
For a full list of methods and attributes of the request object, head
over to the Request documentation.
Maybe you could encode the query string in a way that you can retrieve it as a single parameter on the back end, but not sure it would be useful.
If you don't want to access the args individually, you can access the full query string:
request.query_string
Putting this all together I think this will work for you:
class LiveStats(Resource):
def get(self, url):
return "Trying to get " + url + request.query_string
I am using wsgi-request-logger https://github.com/pklaus/wsgi-request-logger in a flask application and need it to also log the request parameters (ie. the arguments that would be sent with the request).
Using request.form or request.args doesn't work and returns -
RuntimeError: Working outside of request context.
val['params'] = url_decode(environ.get('QUERY_STRING', ''))
print val['params']
This does not work and returns MultiDict([]) (tried it in middleware and the views.py file, it returns the same thing for both cases).
if environ['REQUEST_METHOD'] == 'POST':
print parse_form_data(environ)[1]
This returns MultiDict[] too.
I don't get what I am missing here. Help would be great.
Code which calls the middleware. I edited the middleware a bit and changed the files name to request_logger_wsgi as Im testing it with a local clone right now.
#!flask/bin/python
from app import app
from request_logger_wsgi import WSGILogger, ApacheFormatters
from logging.handlers import TimedRotatingFileHandler
def application(environ, start_response):
response_body = 'The request method was %s' % environ['REQUEST_METHOD']
response_body = response_body.encode('utf-8')
response_headers = [('Content-Type', 'text/plain'),
('Content-Length', str(len(response_body)))]
start_response('200 OK', response_headers)
print response_body, "231321"
return [response_body]
handlers = [ TimedRotatingFileHandler('access.log', 'd', 7) , ]
app.wsgi_app = WSGILogger(app.wsgi_app, handlers, ApacheFormatters.format_log)
app.run(debug=True)
You should post more code of your application, otherwise it's very difficult to help.
You can't use Flask's request object in the WSGI layer. The wsgi-request-logger runs before Flask, that's why there is no request context yet.
You other code was probably run in a module and you used os.environ, which is different from the WSGI environment.
What you actually have to do is to create a custom formatter and tell
def query_formatter(status_code, environ, content_length):
return "{0} {1} {2}".format(dt.now().isoformat(), status_code,
environ.get('QUERY_STRING', ''))
And then set the formatter:
app = WSGILogger(application, handlers, query_formatter)
It would however be better to reuse one of the Apache formatters instead:
import requestlogger
def apache_query_formatter(status_code, environ, content_length):
return requestlogger.ApacheFormatters.format_NCSA_log(
status_code, environ, content_length) + environ.get('QUERY_STRING', '')
This formatter will use the NCSA format and append the query string. There are probably better formats for the log message, but this should get you started.
I have a very simple server. I use Python 2.7 with web.py.
Basically, my code looks like this:
urls = ("/endpoint", "Endpoint")
class Endpoint(object):
def GET(self):
return "End point"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] Endpoint POST with payload: " + json.dumps(result))
return "Endpoint POST"
I tested this server by making POST requests like this:
echo '{"field": "test"}' | curl -d #- http://my.ip.number:port/endpoint
I tried server other methods of making POST requests. I also tried making get requests, both from the terminal and the browser.
In all cases, I get this very strange error.
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 239, in process
return self.handle()
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 229, in handle
fn, args = self._match(self.mapping, web.ctx.path)
File "/usr/local/lib/python2.7/dist-packages/web.py-0.37-py2.7.egg/web/application.py", line 427, in _match
for pat, what in mapping:
ValueError: need more than 1 value to unpack
Why is this error occurring and what can I do to prevent it?
Thanks!
EDIT 1:
After the traceback is displayed, I also get this:
192.168.46.1:51390 - - [16/Mar/2016 12:54:08] "HTTP/1.1 GET /favicon.ico" - 500 Internal Server Error
Neither the IP nor the port are the ones that I am using.
EDIT 2
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Unicode
from __future__ import unicode_literals
import sys
reload(sys)
# -----
# Logging
import logging
logging.basicConfig(level=logging.INFO)
# -----
# Libs
import web
import json
# -----
urls = ("/", "Index",
"/endpoint1", "EP1"
"/endpoint2", "EP2")
class Index(object):
# In the browser, this displays "Index", but also causes the error on the server side.
def GET(self):
return "Index"
# Doesn't do anything, but causes the error
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] Index " + json.dumps(result))
return "Index POST"
class EP1(object):
def GET(self):
return "EP1"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] EP1 " + json.dumps(result))
return "EP1 POST"
class EP2(object):
def GET(self):
return "EP2"
def POST(self):
data = web.data()
web.header('Content-Type', 'application/json')
result = json.loads(data)
logging.info("[Server] EP2 " + json.dumps(result))
return "EP2 POST"
if __name__ == "__main__":
logging.info("[Server] Starting server.")
app = web.application(urls, globals())
app.run()
This is how my server looks like.
I start the server like this:
python server.py 0.0.0.0:7331
If I access the server's root endpoint from the browser, I get "Index" and the error still occurs. The other two endpoints don't return anything and cause the error.
You're missing a comma at second line here:
urls = ("/", "Index",
"/endpoint1", "EP1"
"/endpoint2", "EP2")
It should be like this:
urls = ("/", "Index",
"/endpoint1", "EP1",
"/endpoint2", "EP2")
What happens without the comma is that Python concatenates the two strings without a comma in between.
So with your code, urls was actually
("/", "Index", "/endpoint1", "EP1/endpoint2", "EP2")
I need to create an application that ends after receiving a specific request. I use wsgiref.simple_server and run handling request in separate thread.
There is my code:
from wsgiref.simple_server import make_server
import re
import threading
import urllib2
def webapp(environ, start_response):
path = environ.get('PATH_INFO', '').lstrip('/')
for regex, callback in urls:
match = re.search(regex, path)
if match is not None:
environ['app.url_args'] = match.groups()
return callback(environ, start_response)
return not_found(environ, start_response)
KEEP_RUNNING = True
srv = make_server('localhost', 8081, webapp)
def t_serve():
while KEEP_RUNNING:
srv.handle_request()
def main():
t = threading.Thread(target=t_serve)
t.start()
print 'Service is running.'
t.join()
def index(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return ['Service works']
def t_stop_service():
print 'Service shutdown'
urllib2.urlopen('http://localhost:8081/')
def stop_service(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
global KEEP_RUNNING
KEEP_RUNNING = False
threading.Thread(target=t_stop_service).start()
return ['Service is stopping']
def not_found(environ, start_response):
"""Called if no URL matches."""
start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
return ['<h1>Not Found</h1>']
urls = [
(r'^$', index),
(r'^stop/?$', stop_service)
]
if __name__ == '__main__':
main()
After I make request "stop" request processing ends, but the process of the program is not ending. How to fix it?
Depends on the WSGI server whether it will work, with good quality ones it usually doesn't for good reasons, but have you tried using sys.exit() from the request handler?
If you must, you could also use os._exit(1). This will cause Python to exit immediately, although it will not allow any cleanup code to run.
In my Flask application, I want to expose a URI like this:
http://<base_uri>/some_string
and I wish to handle requests to it differently depending on whether some_string is an integer or not.
With Sinatra I can achieve that via "passing" as shown below:
get '/:some_string' do
if is_integer(:some_string)
'Your URI contains an integer'
else
pass # This will pass the request on the the method below which can handle it
end
get '/*' do
'Your URI contains some string'
end
Here the call pass in the first route lets the second route process the request if :some_string is not an integer.
I couldn't find any equivalent functionality in Flask. Can someone please suggest a solution in Flask?
Type conversion in url routes can do this for you:
from flask import Flask
import unittest
app = Flask(__name__)
app.debug = True
#app.route('/<int:thing>')
def num(thing):
return 'INT'
#app.route('/<thing>')
def string(thing):
return 'STR'
class TestDispatch(unittest.TestCase):
def setUp(self):
self.client = app.test_client()
def test_int(self):
resp = self.client.get('/10')
self.assertEqual("INT", resp.data)
def test_str(self):
resp = self.client.get('/hello')
self.assertEqual("STR", resp.data)
if __name__ == '__main__':
unittest.main()