I'm writing a very simple flask app (a URL shortener) that should be able to redirect certain requests to other arbitrary domains. However, I am running into problems with the redirection. This stripped-down version, for example, doesn't work:
from app import app, db
from flask import abort, redirect
#app.route('/')
def index():
return "Hello, world"
#app.route('/favicon.ico')
def favicon():
abort(404)
#app.route('/<slug>')
def redirect(slug):
return redirect('http://google.com/')
Perhaps naively, I expected this to redirect to google.com, but instead the redirect seems to get "captured" by Flask, and it tries to route the redirected URL back through the redirect handler (e.g. redirect(slug="http://google.com/")) until it eats all the stack space through recursion and errors out. I can't seem to figure out why this would happen, or how to work around it, but I'd really appreciate a pointer in the right direction.
The problem is in this function:
#app.route('/<slug>')
def redirect(slug):
return redirect('http://google.com/')
You named the function redirect(), so in the scope of the function when you call return redirect(...) this is interpreted as a recursive call because the view function shadows Flask's function of the same name. And the URL that you pass is mapped to the slug argument in the recursive call.
Change the name of the view function from redirect() to something else and your code will work just fine.
Related
I want to build a Flask route which only accepts POST requests.
So far, I have tried achieving this goal by usind the methods parameter of the route decorator.
#app.route("/register")
def register(methods=["POST"]):
return "register endpoint"
However, when trying to send a GET request to this route with Postman, it simply returns "register endpoint", even though i only added POST to the methods parameter.
How can I make my route ONLY accept POST requests and return an error in all other cases?
You almost got it, the "methods=[]" should be in the decorator :
#app.route("/register", methods=["POST"])
I am developing an API using Flask, a service not meant to be rendering any templates but simply returning json in the form of {"message": "message here", "status_code": 200, "data": {}}".
I have several routes and each modifies a global response object attached to Flask.g:
from flask import g
from project import app
#app.route('/users/', methods=["GET"])
def get_users():
...
g.res.data["message"] = "Successfully queried users as specified."
# default g.res.status_code == 200
#app.route('/users/', methods=["POST"])
def post_users():
...
g.res.data["message"] = "Successfully created User."
g.res.status_code = 201
Then, returning the response data to the user is handled by an app.after_request:
from flask import g, jsonify
from project import app
#app.after_request
def after_request(response: None):
return jsonify(g.res)
However, Flask still (correctly) believes that the view function is not returning a response, since technically the view function is not, but the after_request handler is.
Is there any inherit problems in my approach to handling the response data? I purposely switched to using app.after_request to avoid duplicating return jsonify(g.res) at the end of each view function.
If there are no problems with this way, how should I go about disabling these warnings?
Edit: The error is being raised in Flask.app.make_response where a ValueError is raised on the response being None and other conversions are attempted later (from a str to a Flask.Response object, for example).
I will likely just modify this file directly to handle my specific use-case until I find an idiomatic way to approach this.
Subclass the Flask class and override the make_response method. Move the logic from your handler into the method. If there was no response returned by the view but there is valid data in g.res, then build the response and return it, otherwise defer to the default behavior.
class GResFlask(Flask):
def make_response(self, rv):
if not rv and g.res.data:
return jsonify(g.res)
return super().make_response(rv)
app = GResFlask(__name__)
It's important to note that after_request handlers may be added by extensions, and the interface provided by Flask means that they will be expecting a valid response object. So you always want to return a valid response from make_response.
Lets say that I have the following
from flask import Flask, render_template
import config
import utils
app = Flask(__name__)
#app.route("/")
def index():
# call utils.function_foo(app)
return render_template("index.html")
#app.route("/about/")
def about():
# call utils.function_foo(app)
return render_template("about.html")
# ... more endpoint handling
if __name__ == "__main__":
app.run(debug=True)
What I want to do is perform function_foo before each routing function has a chance to return.
#app.before_request
def function_foo(app):
# Something foo'ey.
Is not the solution since this calls function_foo every time the server gets any HTTP request.
This means that if I request the about page, and it has 30 images, js files, css files, etc that it has to request, then function_foo will be called 31 times before the about page is loaded. I want function_foo to be called once in this case, not 31.
If anyone knows a thing about this, then I would greatly appreciate some information on it.
Cheers!
If you want to call a function before or after the route function, you can write your own decorator.
It seams that you don't really want that:
What I want to do is perform function_foo before each routing function has a chance to return.
If you want to call function_foo before the rendering, you can write your own rendering function:
def my_rendering(*args, **kwargs):
function_foo()
return render_template(*args, **kwargs)
in Flask Framework, define a route
#main.route('/')
def index():
return render_template('index.html')
only can you use get method to request the index.html.
if I try to post some data to the index.html.
I got a method not allow webpage return. and that's correct.
but my problem is, Is there anyway I can customize the page by myself?
for example, return json data, instead of a method not allowed webpage??
You can create error handler
from flask import jsonify
#app.errorhandler(405)
def method_not_allowed(e):
return jsonify({'error': 405}), 405
http://flask.pocoo.org/docs/0.10/patterns/errorpages/
I modify the login of flaskr sample app, the first line get error. But www.html is in the template dir.
return redirect(url_for('www'))
#return redirect(url_for('show_entries'))
display error:
werkzeug.routing.BuildError
BuildError: ('www', {}, None)
return redirect(url_for('www')) would work if you have a function somewhere else like this:
#app.route('/welcome')
def www():
return render_template('www.html')
url_for looks for a function, you pass it the name of the function you are wanting to call. Think of it like this:
#app.route('/login')
def sign_in():
for thing in login_routine:
do_stuff(thing)
return render_template('sign_in.html')
#app.route('/new-member')
def welcome_page():
flash('welcome to our new members')
flash('no cussing, no biting, nothing stronger than gin before breakfast')
return redirect(url_for('sign_in')) # not 'login', not 'sign_in.html'
You could also do return redirect('/some-url'), if that is easier to remember. It is also possible that what you want, given your first line, is just return render_template('www.html').
And also, not from shuaiyuancn's comment below, if you are using blueprints, url_for should be invoked as url_for('blueprint_name.func_name') Note you aren't passing the object, rather the string. See documentation here.
Assuming that def www(): is already defined (as suggested by unmounted's awesome answer), this error can also be thrown if you are using a blueprint which has not been registered.
Make sure to register these when app is first instantiated. For me it was done like this:
from project.app.views.my_blueprint import my_blueprint
app = Flask(__name__, template_folder='{}/templates'.format(app_path), static_folder='{}/static'.format(app_path))
app.register_blueprint(my_blueprint)
And within my_blueprint.py:
from flask import render_template, Blueprint
from flask_cors import CORS
my_blueprint = Blueprint('my_blueprint', __name__, url_prefix='/my-page')
CORS(my_blueprint)
#metric_retriever.route('/')
def index():
return render_template('index.html', page_title='My Page!')
I came across this error
BuildError: ('project_admin', {}, None)
when I had a call like
return redirect(url_for('project_admin'))
in which I was trying to reference the project_admin function within my Blueprint. To resolve the error, I added a dot before the name of the function, like this:
return redirect(url_for('.project_admin'))
and voila, my problem was solved.