Rewrite a URL with Flask - python

Is it possible to rewrite a URL with Flask e.g. if a POST request is received via this route:
#app.route('/', methods=['GET','POST'])
def main():
if request.method == 'POST':
#TODO: rewrite url to something like '/message'
return render_template('message.html')
else:
return render_template('index.html')
I'm aware I can use a redirect and setup another route but I prefer to simply modify the url if possible.

You can call your route endpoint functions from another routes:
# The “target” route:
#route('/foo')
def foo():
return render_template(...)
# The rewrited route:
#route('/bar')
def bar():
return foo()
You can even access g etc. This approach can be also used when the Flask's routing facilities cannot do something that you want to implement.

This is actual solution to this problem (but maybe a dirty hack):
def rewrite(url):
view_func, view_args = app.create_url_adapter(request).match(url)
return app.view_functions[view_func](**view_args)
We invoke the URL dispatcher and call the view function.
Usage:
#app.route('/bar')
def the_rewritten_one():
return rewrite('/foo')

I think you could be interested in redirect behavior
from flask import Flask,redirect
then use
redirect("http://www.example.com", code=302)
see: Redirecting to URL in Flask

from flask import Flask, redirect, url_for
app = Flask(__name__)
#app.route("/")
def home():
return "hello"
#app.route("/admin"):
def admin():
return redirect(url_for("#name of function for page you want to go to"))

Related

How to create first_or_redirect() like first_or_404() in flask application

I have a flask app with flask login. I want to protect one user from accessing another user's data.
This returns error, which is fine, but I would like to have a redirect to home instead.
#login_required
def myurl(po_id):
current_po = Purchaseorder.query.filter_by(id=po_id, owner=current_user.id)\
.first_or_404()
# delete edit ..
I tried also:
#login_required
def myurl(po_id):
current_po = Purchaseorder.query.filter_by(id=po_id).first()
if current_po.owner!=current_user.id:
redirect ...
But seems too much writing every time.
Is there a solution for that and maybe also for .all() ?
You can easily write your own decorator and control if user was authorized or not:
from functools import wraps
from flask import Flask, redirect, url_for, session
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
def wrapper_redirect(f):
#wraps(f)
def wrapped_func(*args, **kwargs):
if not session.get("login"):
return redirect(url_for("index"))
else:
return f(*args, **kwargs)
return wrapped_func
#app.route("/")
def index():
session["login"] = True # log in
return "<h1>index page</h1>"
#app.route("/test")
#wrapper_redirect
def hello_world():
if session.get("login"):
del session["login"] # log out
return "<p>Hello, World!</p>"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=9999)

How to access Flask Request header outside route method

I want to access headers for a certain API calls outside of its api route. I tried using the app_context and test_request_context but it doesn't seem to work.
from flask import Flask, request
app = Flask("app")
def access_header_for_something():
with app.test_request_context():
with app.app_context():
print(request.headers.get("Authorization"), request.host, request.path)
#app.route('/')
def index():
access_header_for_something()
return 'hello'
if __name__=="__main__":
app.run(debug=True)
Any suggestions would be really helpful
The above code snippet work with slight tweak:
from flask import Flask, request
app = Flask("app")
def access_header_for_something():
with app.app_context():
print(request.headers.get("Authorization"), request.host, request.path)
#app.route('/')
def index():
access_header_for_something()
return 'hello'
if __name__=="__main__":
app.run(debug=True)

Injecting a Flask Request into another Flask App

Is there a way to inject a Flask request object into a different Flask app. This is what I'm trying to do:
app = flask.Flask(__name__)
#app.route('/foo/<id>')
def do_something(id):
return _process_request(id)
def say_hello(request):
# request is an instance of flask.Request.
# I want to inject it into 'app'
I'm trying this with Google Cloud Functions, where say_hello() is a function that is invoked by the cloud runtime. It receives a flask.Request as the argument, which I want to then process through my own set of routes.
I tried the following, which doesn't work:
def say_hello(request):
with app.request_context(request.environ):
return app.full_dispatch_request()
This responds with 404 errors for all requests.
Edit:
The simple way to implement say_hello() is as follows:
def say_hello(request):
if request.method == 'GET' and request.path.startswith('/foo/'):
return do_something(_get_id(request.path))
flask.abort(404)
This essentially requires me to write the route matching logic myself. I'm wondering if there's a way to avoid doing that, and instead use Flask's built-in decorators and routing capabilities.
Edit 2:
Interestingly, dispatching across apps work locally:
app = flask.Flask(__name__)
# Add app.routes here
functions = flask.Flask('functions')
#functions.route('/', defaults={'path': ''})
#functions.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def catch_all(path):
with app.request_context(flask.request.environ):
return app.full_dispatch_request()
if __name__ == '__main__':
functions.run()
But the same technique doesn't seem to work on GCF.
I wouldn't recommend this method, but this is technically possible by abusing the request stack and rewriting the current request and re-dispatching it.
However, you'll still need to do some type of custom "routing" to properly set the url_rule, as the incoming request from GCF won't have it (unless you explicitly provide it via the request):
from flask import Flask, _request_ctx_stack
from werkzeug.routing import Rule
app = Flask(__name__)
#app.route('/hi')
def hi(*args, **kwargs):
return 'Hi!'
def say_hello(request):
ctx = _request_ctx_stack.top
request = ctx.request
request.url_rule = Rule('/hi', endpoint='hi')
ctx.request = request
_request_ctx_stack.push(ctx)
return app.dispatch_request()

Call a Flask Blueprint route using a string (as route) from before_request

I need to achieve the following with my python code:
Whenever a new request comes, say, /someurl1, it should call category_route using the URL given below in the code.
#app.before_request
def before_request():
url = '/category/c6f8f568-0a25-4ea7-999c-20099effa5f1'
# Now here I need a piece of code which calls the below route's method, with above url.
#category.route('/category/<category_id>', methods=['GET'])
def category_route(category_id):
return {"status": "in category"}
The page should now be redirected.
If you want a redirect, you can use redirect. I'd advise you to not use before_request for that.
#app.route('/someurl1')
def someurl():
url = '/category/c6f8f568-0a25-4ea7-999c-20099effa5f1'
return redirect(url)
#category.route('/category/<category_id>', methods=['GET'])
def category_route(category_id):
return {"status": "in category"}
If you do not actually want to redirect, but just return the return value of your other view, you can call the function directly:
#app.before_request
def before_request():
category_id = 'c6f8f568-0a25-4ea7-999c-20099effa5f1'
return category_route(category_id)
#category.route('/category/<category_id>', methods=['GET'])
def category_route(category_id):
return {"status": "in category"}

How do I make sure a flask app has been authorized on all routes?

I am, still, a n00b in Python and Flask. I am creating routes using flask for an API. I am integrating with the GitHub API using GitHub-Flask. Is there an easy and consistent way to check for a current access/request token before executing code for each route? Other than copy/pasting the same check all over the place, hopefully; which is currently what I am doing.
#app.route('/resource_1', methods=['GET'])
def get_resource_1():
if not ACCESS_TOKEN:
return redirect(url_for('login'))
# ... do stuff for route
#app.route('/resource_2', methods=['GET'])
def get_resource_2():
if not ACCESS_TOKEN:
return redirect(url_for('login'))
# ... do other stuff for this route
I imagine there is a much better way that this but I don't know how to find it.
If you only want to protect certain routes, you can use a view decorator. This would look something like this:
from functools import wraps
from flask import g, request, redirect, url_for
def login_required(f):
#wraps(f)
def decorated_function(*args, **kwargs):
if not ACCESS_TOKEN:
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
Then you decorate each resource like so:
#app.route('/resource_2', methods=['GET'])
#login_required
def get_resource_2():
View decorators are documented here:
http://flask.pocoo.org/docs/0.10/patterns/viewdecorators/
If you want to protect absolutely every request then you can use the before_request handler on either the app or the blueprint level:
#app.before_request
def before_request():
if not ACCESS_TOKEN:
return redirect(url_for('login'))

Categories