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

3

7

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

Cadent answered 10/8, 2018 at 0:24 Comment(0)
N
4

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:

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

Nikolenikoletta answered 12/8, 2018 at 7:34 Comment(0)
C
2

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)
Cisneros answered 17/8, 2018 at 13:57 Comment(0)
B
0

Have you considered this library? I think it will do the job for you!

https://pypi.org/project/uritemplate/

Baccivorous answered 14/8, 2018 at 22:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.