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"])
Related
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.
I have been following this Flask pyjwt guide, however my web app is somewhat similar to Miguel's microblog example that uses render_template() and redirect(url_for(...)) for navigation.
I have implemented an encoding and decoding service in my application, however I do not know how to properly return the encoded JWT token to the user using redirect()
My code is as follows:
#app.route('/', methods=['GET', 'POST'])
def login():
login_form = LoginForm()
username = login_form.username.data
password = login_form.password.data
if is_user_valid(username, password):
return redirect(url_for('home'), auth_service.encode_token(username))
render_template('login.html', form=login_form)
My problem is placing the auth token inside the redirect method causes a page to appear saying "Redirecting... you should be redirected, if not click here", which I do not want. I do not particularly wish to change my redirect methods to something similar to make_response(jsonify(...)) as I would then need to handle these responses in the front end when this is a simple login page.
How should I be returning the auth token to the client correctly?
Typically you attach it to response headers which is what your redirect method returns.
response = redirect(url_for('home'))
response.headers['X-JWT-TOKEN'] = auth_service.encode_token(username)
return response
However, I'm not sure if this is gonna be very useful in your setup when using render_template, because you can not easily access these headers on the frontend.
You have two alternatives here:
Make your login endpoint an API. This way you can return the token for the client-side code to handle and store it somehow.
Drop the JWT and stick to using sessions. I know that this is not what you expected, but outside of building APIs JWT-based auth is not very helpful.
This question already has an answer here:
Store post data for use after authenticating with Flask-Login
(1 answer)
Closed 6 years ago.
I'm using flask-login to handle authentication for my app, which is an API that expects HTTP Basic Authorization headers as part of each request (so the user can login and make the request without worrying about sessions, cookies, or having to do the login and requesting in separate steps).
My requests are working out like this:
POST /api/group/48
GET /login?next=%2Fapi%2Fgroup%2F48
GET /api/group/48
That is, a POST request to /api/group/48 is getting intercepted and redirected to the /login endpoint (as expected). What happens in /login is not interactive - it takes the Basic Authorization header and logs the user in.
After the login has completed, the client is redirected back to /api/group/48 - but this time as a GET request, not a POST. And in this app, the /api/group/48 endpoint is expecting only POST data, so it dies with a 405 (Method not allowed) error.
Is this the expected behavior of flask-login? How can I have it pass through the POST request as originally submitted? (or alternatively, should I be using some different architecture so that the redirect to /login, then back to /api/group/48 doesn't take place and the POST data isn't lost?)
I haven't included code, since I don't think this is a code-specific issue. But if it turns out I'm doing something wrong, I can post some sample code.
Thanks to anyone who can help.
This is more then a redirect (which also might still be problematic for some browsers / clients). Your client would need to remember the body of the initial request, and re-post it later. No such mechanism exists in HTTP specification.
What you can do is remember the request (in session on something similar), then do a redirect, and then process the stored request. I don't think there are any shortcuts here.
If you really need to redirect as POST, you can do this.
Suppose you have a view with redirect like this (taken shamelessly from flask-login docs):
#app.route('/login', methods=['GET', 'POST'])
def login():
# Here we use a class of some kind to represent and validate our
# client-side form data. For example, WTForms is a library that will
# handle this for us, and we use a custom LoginForm to validate.
form = LoginForm()
if form.validate_on_submit():
# Login and validate the user.
# user should be an instance of your `User` class
login_user(user)
flask.flash('Logged in successfully.')
next = flask.request.args.get('next')
# next_is_valid should check if the user has valid
# permission to access the `next` url
if not next_is_valid(next):
return flask.abort(400)
return flask.redirect(next or flask.url_for('index'))
return flask.render_template('login.html', form=form)
Then as this answer quotes Wikipedia:
Many web browsers implemented this code in a manner that violated this
standard, changing the request type of the new request to GET,
regardless of the type employed in the original request (e.g. POST).
1 For this reason, HTTP/1.1 (RFC 2616) added the new status codes
303 and 307 to disambiguate between the two behaviours, with 303
mandating the change of request type to GET, and 307 preserving the
request type as originally sent.
So your answer is change redirect to flask.redirect(next or flask.url_for('index'), code=307)
Also note that you'll probably need to redirect to login view as POST also to make it work. You can make your custom unauthorized handler:
#login_manager.unauthorized_handler
def unauthorized():
# do stuff
return a_response
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'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.