I want to routes all paths that start with something like “/api” to same handler function.
Such as:
/api/foo
/api/bar
/api/foo/bar
/api/bar/baz/this/that
All should be handled with one function and I should be able to get the full path after /api.
This feature is very handy and I used it often at Node.js Express framework. Now I am looking for ways to accomplish the same thing with Python Falcon framework.
More info can be found here; It defines the feature as "white-listed “global” functionality."
http://expressjs.com/api.html#app.all
Perhaps you are looking for Falcon's sink facility, e.g.:
class Sink(object):
def on_get(self, req, resp):
resp.body = ('\nTwo things awe me most, the starry sky '
'above me and the moral law within me.\n'
'\n'
' ~ Immanuel Kant\n\n')
app = falcon.API()
handler = Sink().on_get
app.add_sink(handler, prefix='/')
This will route all URLs to the sink handler.
If you're looking for a way to handle all requests before they get routed to appropriate resources, I suggest you to look into middleware components.
Related
Alt: How to use a variable and loop it in Flask app route such that it handles multiple webpages which have similar permalinks (i'm not sure what that is though).
This is a snippet of a simple example to demostrate what I mean:
#app.route('/say-hii-1')
def say_hii_1():
return """<h1>Hello 1!</h1>"""
#app.route('/say-hii-2')
def say_hii_2():
return """<h1>Hello 2!</h1>"""
#app.route('/say-hii-3')
def say_hii_3():
return """<h1>Hello 3!</h1>"""
You see, only the number changes in all routes, and the return values also have a pattern. My project is much more complex and has 10-20 such routes.Is there a way, in which I can reduce all of them to just one route?
Some information that you might need:
OS: Ubuntu 19.10
Python version: 3.7.5
What you're looking for is flask route parameters/dynamic routes/variable rules
Route parameters allow you to match your application's route to a pattern, by inserting variables with carats <>. If the url matches the pattern, it'll pass the variable into the function your route is linked to.
From there, you can do whatever sort of dyanmic behaviour you want, as per usual python.
As an example, you could implement what you're asking for in your example as follows:
#app.route('/say-hii-<int:hi_number>')
def say_hii(hi_number):
return "<h1>Hello " + str(hi_number) + "</h1>"
For more information, you can have a look at the flask quickstart:
https://flask.palletsprojects.com/en/1.1.x/quickstart/#routing
This is a fairly common use case, so be sure to look through the flask quickstart and guides next time!
I feel like I'm running into a brick wall, as I'm not getting anywhere with this, and I believe, simple task.
I'm trying to generate a URL by the likes of '/path/to/url', but upon gazing at multiple StackOverflow Q&A's, the official documentation for cherrypy, I still cannot seem to wrap my head around the issue.
Here's my code so far:
import details
import input_checker as input
import time
import cherrypy
class Index(object):
#cherrypy.expose
def input(self):
return input.check_input()
#cherrypy.expose
def stream(self):
while True:
return 'Hey'
#return input.check_input()
time.sleep(3)
if __name__ == '__main__':
index = Index()
cherrypy.tree.mount(index.stream(), '/input/stream', {})
cherrypy.config.update(
{'server.socket_host': '0.0.0.0'})
cherrypy.quickstart(index)
So essentially, I want to be able to visit http://127.0.0.1:8080/input/stream, and I will be returned with the given result.
After executing this code, and multiple variants of it, I'm still being returned with a 404 not found error, and I'm not sure what I need to do, in order to get it working.
Any tips and/or supporting documentation that I may have skimmed over?
Thanks guys.
So there are couple problems here, why do you use MethodDispatcher do you actually need it?
To serve you stream function on /input/stream you have to mount it as such:
cherrypy.tree.mount(index.stream(), '/input/stream', your_config)
note /input/stream instead of /stream.
But because you're using MethodDispatcher this will likely make your endpoint return 405 as GET is not allowed on this endpoint - to fix that just remove the MethodDispatcher bit.
But if you do require MethodDispatcher you will have to refactor a bit to something like that:
class Stream:
exposed = True # to let cherrypy know that we're exposing all methods in this one
def GET(self):
return something
stream = Stream()
cherrypy.tree.mount(stream , '/index/stream',
{'/':
{'request.dispatch': cherrypy.dispatch.MethodDispatcher()}
}
)
Also make sure to not actually call your methods when mounting them into cherrypy tree, just pass in the name of the function/class
I have a simple TurboGears 2 script, named app.py:
#!/usr/bin/env python3
from wsgiref.simple_server import make_server
from tg import expose, TGController, AppConfig
class RootController(TGController):
#expose()
def lookup(self, name):
return name
config = AppConfig(minimal=True, root_controller=RootController())
print("Serving on port 5000...")
httpd = make_server('', 5000, config.make_wsgi_app())
httpd.serve_forever()
When I run app.py and visit http://localhost:5000/lookup/dave, I see "dave" as expected. But when I visit http://localhost:5000/lookup/dave%2Fdavid, I get a 404 error instead of "dave/david". It seems that TurboGears is splitting 'dave%2Fdavid' into two separate arguments.
How can I get TurboGears to respect that an escaped slash is different from an unescaped slash?
It's not possible to distinguish / from %2F in standard WSGI applications. There was a long argument about this in 2008, but the WSGI spec authors decided that compatibility with CGI--which does not distinguish between the two either--was more important than letting the programmer decide how to interpret a %2F.
In the end I decided to convert / to %252F when building paths, and then run replace('%2F', '/') on every variable I get from the path. This is not a very elegant solution, but unless the WSGI authors have a change of heart I don't see a better option.
For your specific example you can probably work-around that by adding *args to your lookup and join name based on them:
class RootController(TGController):
#expose()
def lookup(self, *args):
name = '/'.join(args)
return name
I want to build a client for my cloud endpoints in python like described in the Documentation.
I want to build the api from a Managed VM, so i get the path to the API by calling
modules.get_hostname(module="default")
This works fine for the devserver and i can create the complete path to the discovery endpoint, however on the live system this returns the url to a certain version like:
20150628t110011.default.guestbook.appspot.com
Thus the complete path to the API (default module) would be
https://20150628t110011.default.guestbook.appspot.com/_ah/api/discovery/v1/apis/guestbook/v1/rest?userIp=182.177.0.4"
But there is no discovery document, maybe due to the fact, that the certificate does not match a url that long and the https fails.
Is there a proper way to receive the base url to the default module? like so:
default.guestbook.appspot.com
because that would result in a working discovery endpoint:
https://default.guestbook.appspot.com/_ah/api/discovery/v1/apis/guestbook/v1/rest?userIp=182.177.0.4"
I would like to avoid doing string operations here, because on the local devserver this would not work as the module url resolves to something like localhost:1234.
You probably want to go through the GAE URl routing doc: https://cloud.google.com/appengine/docs/python/modules/routing#routing_via_url
Key points in there:
Google does not issue SSL certificates for double-wildcard domains
hosted at appspot.com, the certificate won't work for https://20150628t110011.default.guestbook.appspot.com
You can get the certificate to work using the -dot- delimiters; in particular the default version of the default
module can be accessed directly at guestbook.appspot.com
The problem gets even more complicated if your app has multiple modules and also if it's mapped to a custom domain.
While trying to address such complications I realized that modules.get_hostname() is nowadays no longer able to perform the original function that its name implies (I guess because of the multiple possible paths for accessing the same entity). Which probably explains why they won't attempt to fix the api to return a proper hostname: (see this Q&A)
But the info it can return (as applicable depending on the invocation arguments and the execution context) is IMHO extremely useful, allowing one to programatically obtain proper hostnames/URLs for all 3 possible app usage contextes: on the development server, on the .appspot.com domain and on custom domains mapping (including with hostname-based mapping):
<instance_id>.<module_version>.<module_name>.<app_name>.(appspot.com|<devserver_hostname>:<port#>)
This would be, for example, my approach for an app not interested in anything below the module name and using hostname-based custom domain dispatch routing - modules mapped to different hostnames):
def get_module_url(self, module_name='default'):
host_name = modules.get_hostname(module=module_name)
if os.environ.get('SERVER_SOFTWARE').startswith('Development'):
return 'http://' + host_name
app_name = app_identity.get_application_id()
split_name = self.request.host.split(':')[0].split('.')
if split_name[-2] == 'appspot':
new_host_name = app_name if module_name == 'default' else module_name + '-dot-' + app_name
else:
# custom hostname-based domain mapping, default module goes to `www`.mydomain.com
new_host_name = 'www' if module_name == 'default' else module_name
if app_name.endswith('-staging'):
# copy of the GAE app for staging purposes on mydomain.com
new_host_name += '-staging'
return '.'.join(['https://' + new_host_name] + split_name[1:])
As per this thread, unfortunately manual conversion is required to convert from the . hostname to -dot-.
I'm using pylons, and want to use clever css.
I created a controller SassController to handle .sass requests, but in the config/routing.py, I don't know how to write the mapping.
What I want is:
client request: http://localhost:5000/stylesheets/questions/index.sass
all such requests will be handled by SassController#index
I tried:
map.connect('/{path:.*}.sass', controller='sass', action='index')
But found only: http://localhost:5000/xxx.sass will be handled, but http://localhost:5000/xxx/yyy.sass won't.
What should I do now?
The routing code using regular expressions so you can make it eat everything in the url regardless of slashes.
The docs are here
It'll look something like:
map.connect(R'/{path:.*?}.sass', controller='SassController', action='index')
#in the SassController
def index(self, path):
return path
http://localhost:5000/blah/blah/something.sass will call SassController.index with path = blah/blah/something