Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to include a variable in URL path using wsgi? (not query string)

Tags:

python

uwsgi

wsgi

I am sure there is an answer to this but I cannot seem to find it. Also, important to note that I am very new to Python.

I recently cloned this repo which uses python and wsgi https://github.com/hypothesis/via for routing.

What I want is to have a param in the url path (no query string) as such:

meow.com/cat_articles/:article_id # i.e. meow.com/cat_articles/677

How can I achieve that?

For reference, my end goal is to add my own path to this file:

https://github.com/hypothesis/via/blob/master/via/app.py

like image 640
dipole_moment Avatar asked Aug 10 '18 00:08

dipole_moment


2 Answers

How you'd add such a route to an app depends on what library or libraries that app is using to implement WSGI. I see that the app.py file you linked to is using werkzeug (as well as static).

Here are some useful references for routing with placeholders in werkzeug:

  • Routing section of the tutorial
  • More in-depth routing documentation
  • Working example code from the tutorial (which declares routing rules with placeholders here)

I've barely used werkzeug and won't claim this is definitely the best approach, but one option would be to add another WSGI app via werkzeug to the wsgi.DispatcherMiddleware call at the bottom of that file.

Here's some thrown-together sample code to get you started in the context of the app.py file you shared. Try deleting everything after this line and replacing it with this code:

from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException

my_url_map = Map([
    # Note that this rule builds on the `/cat_articles` prefix used in the `DispatcherMiddleware` call further down
    Rule('/<article_id>', endpoint='get_cat_article')
])

def cat_app(environ, start_response):
    urls = my_url_map.bind_to_environ(environ)
    try:
        endpoint, args = urls.match()
    except HTTPException, e:
        return e(environ, start_response)

    if endpoint == 'get_cat_article':
        # Note that werkzeug also provides objects for building responses: http://werkzeug.pocoo.org/docs/0.14/wrappers
        start_response('200 OK', [('Content-Type', 'text/plain')])
        return ['Finally, a cat-centric article about {0}'.format(args['article_id'])]
    else:
        start_response('404 Not Found', [('Content-Type', 'text/plain')])
        return ['Nothing is here...']


application = RequestHeaderSanitiser(app)
application = ResponseHeaderSanitiser(application)
application = Blocker(application)
application = UserAgentDecorator(application, 'Hypothesis-Via')
application = wsgi.DispatcherMiddleware(application, {
    '/cat_articles': cat_app,
    '/favicon.ico': static.Cling('static/favicon.ico'),
    '/robots.txt': static.Cling('static/robots.txt'),
    '/static': static.Cling('static/'),
    '/static/__pywb': static.Cling(resource_filename('pywb', 'static/')),
    '/static/__shared/viewer/web/viewer.html': redirect_old_viewer,
    '/h': redirect_strip_matched_path,
})

With that code, the path /cat_articles/plants should return:

Finally, a cat-centric article about plants

like image 171
jgaul Avatar answered Nov 12 '22 17:11

jgaul


Pure werkzueg path

Firstly I'm not quite sure if you want to modify the via project or just create something similar from scratch.

Upon inspection of the source code the via project shows to be based on the Werkzueg-library, which provides a URL Routing API supporting what your looking for

from werkzeug.wrappers import Request, Response
from werkzeug.wsgi import responder
from werkzeug.routing import Map, Rule


def show_cat_article(article_id): 
    ...


url_map = Map(
    [Rule("cat_articles/<int:article_id>", endpoint="show_cat_article")]
)
views = {"show_cat_article": show_cat_article}


@responder
def application(environ, start_response):
    request = Request(environ)
    urls = url_map.bind_to_environ(environ)
    return urls.dispatch(lambda e, v: views[e](request, **v),
                         catch_http_exceptions=True)

Easier route

Now if you're not constrained to using plain werkzeug directly I'd recommend taking a look into Flask by the same team, if your not familiar with flask it's described as follows "Flask is a microframework for Python based on Werkzeug, …". So basically a lightweight wrapper with convenient features.

Flask allows you to add a route with params via a decorator like this:

@app.route("meow.com/cat_articles/<int:article_id>")
def show_cat_article(article_id): 
    ...

Or using less magically using add_url_rule(…) (which is invoked by the decorator).

def show_cat_article(article_id): 
    ...


app.add_url_rule("meow.com/cat_articles/<int:article_id>", show_cat_article)
like image 2
Hultner Avatar answered Nov 12 '22 19:11

Hultner