How to hide "cgi-bin", ".py", etc from my URLs? - python

Brand new to web design, using python. Got Apache up and running, test python script working in cgi-bin directory. Get valid results when I type in the URL explicitly: ".../cgi-bin/showenv.py"
But I don't want the URL to look that way. Here at stackoverflow, for example, the URLs that display in my address bar never have the messy details showing the script that was used to run them. They're clean of cgi-bin, .py, etc. extensions. How do I do that?
EDIT: Thanks for responses, every single one helpful, lots to learn. I'm going with URL Rewriting for now; example in the docs looks extremely close to what I actually want to do. But I'm committed to python, so will have to look at WSGI down the road.

The python way of writing web applications is not cgi-bin. It is by using WSGI.
WSGI is a standard interface between web servers and Python web applications or frameworks. The PEP 0333 defines it.
There are no disadvantages in using it instead of CGI. And you'll gain a lot. Beautiful URLs is just one of the neat things you can do easily.
Also, writing a WSGI application means you can deploy on any web server that supports the WSGI interface. Apache does so by using mod_wsgi.
You can configure it in apache like that:
WSGIScriptAlias /myapp /usr/local/www/wsgi-scripts/myapp.py
Then all requests on http://myserver.domain/myapp will go to myapp.py's application callable, including http://myserver.domain/myapp/something/here.
example myapp.py:
def application(environ, start_response):
start_response('200 OK', [('Content-type', 'text/plain')])
return ['Hello World!']

I think you can do this by rewriting URL through Apache configuration. You can see the Apache documentation for rewriting here.

You have to use URL Rewriting.
It is not a noob question, it can be quite tricky :)
http://httpd.apache.org/docs/2.0/misc/rewriteguide.html
Hope you find it helpful

this is an excerpt from a .htaccess that I use to achieve such a thing, this for example redirects all requests that were not to index.php to that file, of course you then have to check the server-variables within the file you redirect to to see, what was requested.
Or you simply make a rewrite rule, where you use a RegExp like ^.*\/cgi-bin\/.*\.py$ to determine when and what to rewrite. Such a RegExp must be crafted very carefully, so that rewriting only takes place when desired.
<IfModule mod_rewrite.c>
RewriteEngine On #activate rewriting
RewriteBase / #url base for rewriting
RewriteCond %{REQUEST_FILENAME} !index.php #requested file is not index.php
RewriteCond %{REQUEST_FILENAME} !^.*\.gif$ #requested file is no .gif
RewriteCond %{REQUEST_FILENAME} !^.*\.jpg$ #requested file is no .jpg
RewriteCond %{REQUEST_FILENAME} !-d #is not a directory
RewriteRule . /index.php [L] #send it all to index.php
</IfModule>
The above Example uses RewriteConditions to determine when to rewrite ( .gif's, .jpeg's and index.php are excluded ).
Hmm, so thats a long text already. Hope it was a bit helpful, but you won't be able to avoid learning the syntax of the Apache RewriteEngine.

You'll find the ScriptAlias directive helpful. Using
ScriptAlias /urlpath /your/cgi-bin/script.py
you can access your script via http://yourserver/urlpath.
You also might want to look into mod_passenger, though the last time I used it, WSGI was kind of a "second-class citizen" within the library—it could detect WSGI scripts if it were used to serve the whole domain, but otherwise there are no directives to get it to run a WSGI app.

Just use some good web framework e.g. django and you can have such URLs
more than URLs you will have a better infrastructure, templates, db orm etc

Related

How do I use python as a server-side language?

I am trying to use Python instead of PHP (just for educational purposes). I am testing my webpages on XAMPP and I already added python and cgi to configs.
I managed to make a very basic webpage with this code
#!/Python/python
print("Content-Type: text/plain;charset=utf-8")
print()
print("Hello World!")
Though, this is it. I can't find any info on how exactly do I serve webpages with Python 3+. Most of the info is outdated or controversial. Is there an up-to-date guide on how to use Python as a server-side language?
What Nicarus said was a valid sugestion but I also recommend some other things.
First, for your development environment you won't need to use xampp or wamp or such things, python has it's own HTTP server, although not the simplest thing to use, the libraries and frameworks I will explain next use that.
So, most python web developers don't use raw python to use python as their programming language for the web. Most developers use a framework or library of some kind. These frameworks range from being rather heavy and opinionated, like Django, to smaller ones like Flask. Most, if not all, of these frameworks provide some kind of easy and quick way to set up a development HTTP server for testing.
I would recommend looking up Django first since it has the most comprehensive tutorials and guides to get you started. Then, ones you're more comfortable in the Python language you can fairly easily use something else with less hand holding.
With django you can start here
Python can be a great side server language, but not in the way PHP is. It is highly recommended to use a framework, like flask.
In PHP you have different .php files (like index.php) that do a basic routing, but in python (with Flask and also some others frameworks) you must define the URI path within the function.
You can see here an example, from its webpage Flask
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
And if you look for a API REST framework, take a look to Falcon
For the purposes you mention, this current setup should work (at least for a while):
Apache Setup
Changes in httpd.conf
Change denied to granted
<Directory />
AllowOverride none
Require all granted
</Directory>
Look for Options +Indexes +FollowSynsLinks........ and add +ExecCGI to that line.
Options +Indexes +FollowSymLinks +Multiviews +ExecCGI
Look for AddHandler cgi-script .cgi and add .py to it
AddHandler cgi-script .cgi .py
Changes in httpd-vhosts.conf
<Directory "{Your directory}"/>
Options +Indexes +Includes +FollowSymLinks +MultiViews +ExecCGI
AllowOverride All
Require all granted
</Directory>
example.py
First line of Python code #!C:\{somepath}\python
You need to find the right path and bar convention ( /, \, //...) for the first and commented line of code. In your example is: #!/Python/python
Run this script.py to find it
import sys
print(sys.executable)
Paste the output next to #! and you should be able to test your test-code.py into your **www\ folder
If happens to be the case that it does not work, try different combinations of python, python.exe with different bar conventions like "/" "//" "\" next to the #! line.
To display an HTML file add print ('Content-type: text/html\n\n') on to your .py code.**
Full code
Note f on html_content = f'''<html>...
#!C:\Program Files (x86)\Python37-32\python
# -*- coding: utf-8 -*-
import cgi
stringvar = 'Hello variable'
intvar = 30
html_content = f'''<html>
<head><title>My first Python CGI app</title></head>
<body>
<p>Hello, 'world'!</p>
<p>This is a stringvar = {stringvar} </p>
<p>This is intvar = {intvar} </p>
</body>
</html>'''
print ('Content-type: text/html\n\n')
print (html_content)
"Most of the info is outdated or controversial"
Totally agree. I hope this works!

HTTP_AUTHORIZATION header in Django

I wrote a simple check in django which requires an access token to be passed along in some requests (I'm using a decorator).
authorization = request.META.get('HTTP_AUTHORIZATION', None)
# Forbid access when no access token, or an invalid one, is provided
form = AccessTokenForm({ 'access_token': authorization })
if not form.is_valid():
raise exceptions.HttpDeniedException()
This has been working fine with django's development server, but recently I deployed using Apache and now it doesn't work anymore, as the authorisation variable is None. Has anyone faced something similiar before? The client is passing the header as Authorization; again, this is working fine in localhost, but not on the remote server.
Edit: I found this, and it seems that it should work, but it doesn't. Actually, I didn't even have an .htaccess file, and adding one doesn't seem to have taken any effect at all. Here are the contents:
RewriteEngine On
RewriteRule ^(media/.*)$ - [L]
RewriteRule ^(static/.*)$ - [L]
RewriteCond %{REQUEST_URL} !(dispatch.fcgi)
RewriteRule ^(.*)$ dispatch.fcgi/$1 [QSA,L,E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
The thing is, I'm not even sure Apache is using this at all..
It seems there's a ticket for this issue here. Just add the following to apache2.conf:
WSGIPassAuthorization on

Get current, requested URL in Python without a framework

I'm trying to get the URL that has been requested in Python without using a web framework.
For example, on a page (let's say /main/index.html), the user clicks on a URL to go to /main/foo/bar (/foo/bar doesn't exist). Apache (with mod_wsgi) then redirects the user to a PHP script at /main/, which then gets the url and searches MySQL for any matching fields. Then the rest of the field is returned. This helped in PHP:
$_SERVER["REQUEST_URI"];
I'd rather not use PHP since it's becoming increasingly difficult to maintain the PHP code whilst the database keeps changing in structure.
I'm pretty sure there's a better way altogether and any mention would be greatly appreciated. For the sake of relevancy, is this even possible (to get the requested URL in Python)? Should I just use a framework, although it seems quite simple?
Thanks in advance,
Jamie
Note: I don't want to use GET for security purposes.
Well, if you run your program as a CGI script, you can get the same information in os.environ. However, if I recall correctly, REQUEST_URI as such is not part of the CGI standard and you need to use os.environ['SCRIPT_NAME'], os.environ['PATH_INFO'] and os.environ['QUERY_STRING'] to get the equivalent data.
However, I seriously urge you to see some lightweight framework, such as Pyramid. Plain CGI with Python is slow and generally just pain in the ass.
Unlike PHP, Python is a general purpose language and doesn't have this built-in.
The way you can gather this information depends on the deployment solution:
CGI (mostly Apache with mod_python, deprecated): see #Antti Haapala solution
WSGI (most other deployment solutions): see #gurney alex solution
But you will encouter much more problems: session hanling, url management, cookies, and even juste simple POST/GET parsing. All of this need to be done manually if you don't use a framework.
Now, if you feel like a framework is overkill (but really, incredible tools like Django are worth it), you can use a micro framework like bottle.
Microframeworks will typically make this heavy lifting for you, but without the complicated setup or the additional advanced features. Bottle has actually zero setup an is a one file lib.
Hello word with bottle:
from bottle import route, run, request
#route('/hello/:name')
def index(name='World'):
return '<b>Hello %s! You are at %s</b>' % (name, request.path)
run(host='localhost', port=8080)
request.path contains what you want, and if you visit http://127.0.0.1:8080/hello/you, you will get:
Hello you! You are at /hello/you
When I want to get a URL outside of any framework using Apache2 and Mod_WSGI I use
environ.get('PATH_INFO')
inside of my application() function.
When using mod_python, if I recall correctly you can use something like:
from mod_python import util
def handler(request):
parameters = util.FieldStorage(request)
url = parameters.get("url", "/")
See http://www.modpython.org/live/current/doc-html/pyapi-util.html for more info on the mod_python.util module and the FieldStorage class (including examples)

Using CherryPy/Cherryd to launch multiple Flask instances

Per suggestions on SO/SF and other sites, I am using CherryPy as the WSGI server to launch multiple instances of a Python web server I built with Flask. Each instance runs on its own port and sits behind Nginx. I should note that the below does work for me, but I'm troubled that I have gone about things the wrong way and it works "by accident".
Here is my current cherrypy.conf file:
[global]
server.socket_host = '0.0.0.0'
server.socket_port = 8891
request.dispatch: cherrypy.dispatch.MethodDispatcher()
tree.mount = {'/':my_flask_server.app}
Without diving too far into my Flask server, here's how it starts:
import flask
app = flask.Flask(__name__)
#app.route('/')
def hello_world():
return "hello"
And here is the command I issue on the command line to launch with Cherryd:
cherryd -c cherrypy.conf -i my_flask_server
Questions are:
Is wrapping Flask inside CherryPy still the preferred method of using Flask in production? https://stackoverflow.com/questions/4884541/cherrypy-vs-flask-werkzeug
Is this the proper way to use a .conf file to launch CherryPy and import the Flask app? I have scoured the CherryPy documentation, but I cannot find any use cases that match what I am trying to do here specifically.
Is the proper way to launch multiple CherryPy/Flask instances on a single machine to execute multiple cherryd commands (daemonizing with -d, etc) with unique .conf files for each port to be used (8891, 8892, etc)? Or is there a better "CherryPy" way to accomplish this?
Thanks for any help and insight.
I can't speak for Flask, but I can for CherryPy. That looks like the "proper way"...mostly. That line about a MethodDispatcher is a no-op since it only affects CherryPy Applications, and you don't appear to have mounted any (just a single Flask app instead).
Regarding point 3, you have it right. CherryPy allows you to run multiple Server objects in the same process in order to listen on multiple ports (or protocols), but it doesn't have any sugar for starting up multiple processes. As you say, multiple cherryd commands with varying config files is how to do it (unless you want to use a more integrated cluster/config management tool like eggmonster).
Terminology: Mounting vs Grafting
In principle this is a proper way to serve a flask app through cherrypy, just a quick note on your naming:
It is worth noting here that tree.mount is not a configuration key by itself - tree will lead to cherrypy._cpconfig._tree_config_handler(k, v) being called with the arguments 'mount', {'/': my_flask_server.app}.
The key parameter is not used at all by the _tree_config_handler so in your config "mount" is just an arbitrary label for that specific dict of path mappings. It also does not "mount" the application (it's not a CherryPy app after all). By that I mean, it does not cherrypy.tree.mount(…) it but rather cherrypy.tree.grafts an arbitrary WSGI handler onto your "script-name" (paths, but in CherryPy terminology) namespace.
Cherrypy's log message somewhat misleadingly says "Mounted <app as string> on /"]
This is a somewhat important point since with graft, unlike mount, you cannot specify further options such as static file service for your app or streaming responses on that path.
So I would recommend changing the tree.mount config key to something descriptive that does not invite reading too much semantics about what happens within CherryPy (since there is the cherrypy.tree.mount method) due to that config. E.g., tree.flask_app_name if you're just mapping that one app in that dict (there can be many tree directives, all of them just getting merged into the paths namespace) or tree.wsgi_delegates if you map many apps in that dict.
Using CherryPy to serve additional content without making an app of it
Another side note, if you want cherrypy to e.g. provide static file service for your app, you don't have to create a boilerplate cherrypy app to hold that configuration. You just have to mount None with the appropriate additional config. The following files would suffice to have CherryPy to serve static content from the subdirectory 'static' if they are put into the directory where you launch cherryd to serve static content (invoke cherryd as cherryd -c cherrypy.conf -i my_flask_server -i static:
static.py
import cherrypy
# next line could also have config as an inline dict, but
# file config is often easier to handle
cherrypy.tree.mount(None, '/static-path', 'static.conf')
static.conf
# static.conf
[/]
tools.staticdir.on = True
tools.staticdir.root = os.getcwd()
tools.staticdir.dir = 'static'
tools.staticdir.index = 'index.html'

How to deploy Django application at sub-url?

I need to set up a django development environment that is publicly viewable on the internet (I am doing this for school, and my projects need to be viewable by my professor, this isn't a setup that needs much security). I have a virtual server running Ubuntu 8.04 LTS.
I need to have multiple django applications running in subdirectories of my main site. That is, I need mysite.com to be a static page, mysite.com/wordpress to be my wordpress blog, and mysite.com/django1 mysite.com/django2 etc. to be django projects.
I am using apache, and I will either be using sqlite or mysql.
There seem to be as many different ways to install and configure django as there are websites offering advice, and all of them assume a single project is going to be the root of the website. I'd really appreciate some help, thank you.
You can use
WSGIScriptAlias /django1 /home/keratacon/www/django1/wsgi.py
WSGIScriptAlias /django2 /home/keratacon/www/django2/wsgi.py
in your apache+mod_wsgi config, assuming wsgi.py is the name of your wsgi script.
This blog explains the solution (assuming that mod_wsgi is used, with nginx/uwsgi the solution is similar apparently in nginx/uwsgi this is not necessary).
The first parameter of WSGIScriptAlias - the /sub-url will be stripped from the request url and the rest will go to your django app. If your Django app urls all start with /sub-url (which are stripped by mod_wsgi), then you will not be able to show the views at those urls, unless you "re-insert" the /sub-url to the request path part.
import django.core.handlers.wsgi
_application = django.core.handlers.wsgi.WSGIHandler()
def application(environ, start_response):
#the line below will re-append the sub-url to all urls
environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']
#this one will make django think that it's the only thing running on the server
environ['SCRIPT_NAME'] = '' # my little addition to make it work
return _application(environ, start_response)
Also in your urls.py all urls must be prefixed with the sub-url of your interest.
Finally, the WSGIScriptAlias must be the same as your sub-url:
#the below line will "take-out" the sub-url and pass the rest
#to your wsgi script
WSGIScriptAlias /sub-url /path/to/wsgi_script
Where file /path/to/wsgi_script must contain the definition of application as shown in the first code snippet.
To make the "sub-url" setup explicit in Django, the equivalent request path patching would have to occur within the Django framework.

Categories

Resources