I'm writing a template on bottle for every request. So localhost and locahost/mypage and localhost/mypage/about will call the same template. I checked here and find some good examples on matching all urls like this:
from bottle import route, run, template, static_file
#route("/<url:re:.+>")
def hello(url):
return template('page_template', url=url)
#route('/static/<filepath:path>', name='static')
def server_static(filepath):
return static_file(filepath, root='static')
run()
My questions are:
1) It doesn't match root. So if I type "localhost", it doesn't work.
2) Since there are static files, I have another route for static files serving. So if I type localhost/static/page, it doesn't return "hello world".
I believe I need to modify the regex (/<:re:.+>) to deal with both situations. Any help would be greatly appreciated,
#Michael
Good news: You can "stack" routes quite straightforwardly. Just do this:
#route("/")
#route("/<url:re:.+>")
def hello(url):
return template('page_template', url=url)
That will treat root the same as your existing regex route.
As for overlapping routes, according to the documentation, dynamic routes are evaluated in the order in which they were defined.
Related
notecard_router = APIRouter(
prefix = "/notecard",
tags = ['notecard']
)
Hi, I am trying to include a router to my app, but for some reason, the prefix that I give in the API router comes twice in the API paths. I have no idea why this is happening.
This is happening because from what I'm able to tell, you're not structuring your endpoints the way you want them. Instead of creating the API router and adding your prefix to it in each endpoint,
from fastapi import APIRouter
app = APIRouter(tags=["notecard"], prefix="/notecard")
#app.get("/notecard/index")
async def root():
...
When you do something like this, your endpoint actually becomes /prefix/notecard/index, which is /notecard/notecard/index. It looks like that's what you're doing. Instead of that, you can do
from fastapi import APIRouter
app = APIRouter(tags=["notecard"], prefix="/notecard")
#app.get("/index")
async def root():
...
And FastAPI will handle the prefixing for you.
Also, relevant snippets of your code or anything that can be used to reproduce your issue would make answering easier :)
I have my FastAPI app define in server.py
app = FastAPI(
debug=True, title="Microservice for APIs",
description="REST APIs",
version="0.0.1",
openapi_url="/v3/api-docs",
middleware=[
Middleware(AuthorizationMiddleware, authorizor=Auth())
])
In __init__.py, I have routes defined
from fastapi import APIRouter
api_router = APIRouter()
api_router.include_router(impl_controller.router, prefix="/impl/data",
tags=["APIs for Impl Management"])
In impl_controller.py, I have define routes like this
#router.get('{id}/get_all')
def hello_world():
return {"msg": "Hello World"}
#router.get('{id}/get_last')
def test():
return {"msg": "test"}
In the middleware, I'm trying to get request route and not the URL
def check_for_api_access(self, request: Request):
request_path = request.scope['path']
# route_path = request.scope['endpoint'] # This does not exists
url_list = [
{'path': route.path, 'name': route.name}
for route in request.app.routes
]
Result I'm expecting is: {id}/get_all for 1st request and {id}/get_last for 2nd request.
I'm able to get list of all paths in url_list but I want route path for the specific request
Tried solution provided here: https://github.com/tiangolo/fastapi/issues/486 that also not working for me
Try this:
def check_for_api_access(self, request: Request):
api_route = next(item for item in request.app.routes if isinstance(item, APIRoute) and item.dependant.cache_key[0] == request.scope['endpoint'])
print(api_route.path)
You may not be able to accomplish exactly what you need, though you can get very close with the standard framework (i.e. no fancy alterations of the framework).
In the middleware, you can access the request directly. This way, you'll be able to inspect the requested url, as documented in https://fastapi.tiangolo.com/advanced/using-request-directly/?h=request . The attributes that are accessible are described here https://www.starlette.io/requests/
N.B. Since you posted just snippets, it's very difficult to say where values/variables come from.
In your case, it's dead simple what is not working. If you looked at the urls I posted, the starlette docs, shows the attributes that you can access from a request. This contains exactly the attribute you are looking for.
Basically, change request_path = request.scope['path'] into request_path = request.url.path. If you have a prefix, then you'll also get that and that's why I said You may not be able to accomplish exactly what you need, though you can get very close with the standard framework (i.e. no fancy alterations of the framework). Still, if you know your prefix, you can remove it from the path (it's just a string).
I'm creating a large number of Flask routes using regular expressions. I'd like to have a unit test that checks that the correct routes exist and that incorrect routes 404.
One way of doing this would be to spin up a local server and use urllib2.urlopen or the like. However, I'd like to be able to run this test on Travis, and I'm assuming that's not an option.
Is there another way for me to test routes on my application?
Use the Flask.test_client() object in your unittests. The method returns a FlaskClient instance (a werkzeug.test.TestClient subclass), making it trivial to test routes.
The result of a call to the TestClient is a Response object, to see if it as 200 or 404 response test the Response.status_code attribute:
with app.test_client() as c:
response = c.get('/some/path/that/exists')
self.assertEquals(response.status_code, 200)
or
with app.test_client() as c:
response = c.get('/some/path/that/doesnt/exist')
self.assertEquals(response.status_code, 404)
See the Testing Flask Applications chapter of the Flask documentation.
Martjin's answer surely solve your issue, but some times you don't have the time (or will) to mock all the components you call in a route you want to test for existence.
And why would you need to mock? Well, the call get('some_route') will first check for this route to exists and then ... it will be executed!
If the view is a complex one and you need to have fixtures, envs variables and any other preparation just for test if the route exists, then you need to think again about your test design.
How to avoid this:
You can list all the routes in your app. An check the one you're testing is in the list.
In the following example, you can see this in practice with the implementation of a site-map.
from flask import Flask, url_for
app = Flask(__name__)
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
#app.route("/site-map")
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
# links is now a list of url, endpoint tuples
references:
get a list of all routes defined in the app
Helper to list routes (like Rail's rake routes)
Another way of testing a URL without executing the attached view function is using the method match of MapAdapter.
from flask import Flask
app = Flask(__name__)
#app.route('/users')
def list_users():
return ''
#app.route('/users/<int:id>')
def get_user(id):
return ''
Testing
# Get a new MapAdapter instance. For testing purpose, an empty string is fine
# for the server name.
adapter = app.url_map.bind('')
# This raise werkzeug.exceptions.NotFound.
adapter.match('/unknown')
# This raises werkzeug.exceptions.MethodNotAllowed,
# Although the route exists, the POST method was not defined.
adapter.match('/users', method='POST')
# No exception occurs when there is a match..
adapter.match('/users')
adapter.match('/users/1')
From Werkzeug documentation:
you get a tuple in the form (endpoint, arguments) if there is a match.
Which may be useful in certain testing scenarios.
Variable rules isn't working as I'd expect when static_url_path is used.
Simply, why does this work:
import flask
app = flask.Flask(__name__)
#app.route("/", defaults={"p": "")
#app.route("/<path:p>")
def main(p):
return "<h1>Hello %s</h1>" % p
if __name__ == '__main__':
app.run(debug=True)
But not this?
import flask
app = flask.Flask(__name__, static_url_path="")
#app.route("/", defaults={"p": "")
#app.route("/<path:p>")
def main(p):
return "<h1>Hello %s</h1>" % p
if __name__ == '__main__':
app.run(debug=True)
Note the added static_url_path
Some background details; I'm new to both Flask and AngularJS, but are using them here in conjunction with each other and for those not familiar with AngularJS; I'm using AngularJS to produce a single-page application in which paths are used for dynamically replacing content within a document, as opposed to reloading the whole page. Thus, I need all paths routed to the same html document (not included in the above example), ideally coming form the same function, so as to let AngularJS handle rendering as opposed to Flask.
The below answer solved that issue, but it isn't working when status_url_path is used, and I can't figure out why.
Flask route for AngularJS with HTML5 URL mode
Adding this for reference, as he is doing this exact thing.
http://www.youtube.com/watch?v=2geC50roans
You are effectively telling Flask to map /<path:static_path> to static files. That means that everything is now considered static. Don't do this!
Angular pages don't need to call into Flask static routes; keep your static routes under /static, have / produce your Angular application, then use other routes to handle Angular AJAX calls. These can have proper paths; Angular doesn't dictate what routes your Flask server will respond to.
Then keep your static route to serve the JavaScript and CSS and images, e.g. the actual static content referenced from your HTML page.
Server.Transfer is sort of like a Redirect except instead of requesting the browser to do another page fetch, it triggers an internal request that makes the request handler "go to" another request handler.
Is there a Python equivalent to this in Google App Engine?
Edit: webapp2
With most Python frameworks the request handler is simply a function: I should imagine you can just import the actual handler function you want to use and pass it the parameters you received in the current handler function.
In Django (for example): you usually have a function that takes at least 1 parameter, the request object. You should be able to simply import the next handler and then return the result of executing it. Something like:
def actual_update_app_queue_settings(request):
return HttpResponse()
def update_app_queue_settings(request):
return actual_update_app_queue_settings(request):
For the framework you've mentioned, probably something like this:
class ProductHandler(webapp2.RequestHandler):
def get(self, product_id):
self.response.write('You requested product %r.' % product_id)
class ProductHandler2(webapp2.RequestHandler):
def get(self, product_id):
nph = ProductHandler()
nph.initialize(request, response)
nph.get(product_id)
I'm fudging that by looking at http://webapp-improved.appspot.com/guide/handlers.html: it looks reasonable. If you're using route annotations I'm honestly not sure what you do, but that might do it.
Usually, you just have to call the corresponding method.
For being more specific... Which flavour of AppEngine are you using? Java, Python, Go... Php?
If you are using java/servlet, then the "forward" is
protected void doGet(HttpServletRequest request, HttpServletResponse response){
request.getRequestDispatcher("/newurl").forward(request, response);
}