I'm trying to wrap my Flask server in a class, so as to better fit in the structure of the rest of my application.
I have the below code:
class HTTPServer(object):
def __init__(self):
self.app = Flask(__name__)
self.app.add_url_rule('/', 'index', self.hello_world, methods=['POST'])
self.app.run(port=5050, use_reloader=False)
def hello_world(self, data):
print "Hello, World: {}".format(data)
However, if I send a POST request to localhost:5050/index I get a 404 error.
The Flask log shows the following:
127.0.0.1 - - [30/Aug/2019 11:17:52] "POST /index HTTP/1.1" 404 -
The same happens if I change ['POST'] to ['GET'] in methods and send a GET request.
However, if I remove the methods parameter from add_url_rule() entirely, I can send GET requests and they are handled appropriately.
I didn't understand the endpoint parameter of add_url_rule. It isn't the endpoint as seen by the client, but rather an internal name for the endpoint. The correct method call is:
self.app.add_url_rule('/index', 'hello_world', self.hello_world, methods=['POST'])
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 using flask restful for my API server, and would like to use flask_jwt in order to secure my endpoints.
This is my endpoint and how I add those to the API server
class Model(Resource):
def get(self):
return 1
api.add_resource(Model, '/path')
I would like to add a simple #jet_required decorator for my API endpoint. How can I get something that is similar to this
#app.route('/protected')
#jwt_required()
def protected():
return '%s' % current_identity
But using flask restful interface?
When I try to use the following, and accessing the endpoint I get this error
class Model(Resource):
#jwt_required
def get(self):
return 1
TypeError: .wrapper..decorator
at 0x7ff5fb262840> is not JSON serializable
127.0.0.1 - - [22/Jan/2020 10:10:28] "GET /resize HTTP/1.1" 500
Made it work using flask_jwt_extended, as in this example
Using the following code
app = Flask(__name__)
api = Api(app)
CORS(app)
app.config['JWT_SECRET_KEY'] = 'jwt-secret-string'
jwt = JWTManager(app)
#app.route('/protected')
#jwt_required()
def protected():
return '%s' % current_identity
That now returns a graceful message in cases of missing authentication header.
I am trying to run a very simple flask-application on a (shared) WSGI server. The code works fine when I run it with the build-in server, but if I try to POST to the URL on the production WSGI server, I receive a 404, The requested URL was not found on the server error.
This only occurs for POST requests, GET and PUT are processed as expected.
By removing the placeholder tid, flask can be convinced to properly process the request, but this is obviously not a proper solution.
The server is running Phusion Passenger, the flask version is 1.0.2.
As it is a shared server, I have no further access to the server configuration.
What can cause flask to seemingly forget routes on a WSGI-server?
A minimal example that reproduces to behaviour (on the server only, of course) can be seen below:
from flask import Flask
from flask.views import MethodView
app = Flask(__name__)
class API(MethodView):
def get(self, tid=0):
return "Test"
def put(self, tid=0):
return "Test"
def post(self, tid=0):
return "Test"
app.add_url_rule("/test/<int:tid>", view_func=API.as_view('api'))
You have to specify the methods you use in add_url_rule:
app.add_url_rule("/test/<int:tid>", view_func=API.as_view('api'), methods=['GET', 'PUT', 'POST'])
The documentation states that the preferred way to define a route is to include a trailing slash:
#app.route('/foo/', methods=['GET'])
def get_foo():
pass
This way, a client can GET /foo or GET /foo/ and receive the same result.
However, POSTed methods do not have the same behavior.
from flask import Flask
app = Flask(__name__)
#app.route('/foo/', methods=['POST'])
def post_foo():
return "bar"
app.run(port=5000)
Here, if you POST /foo, it will fail with method not allowed if you are not running in debug mode, or it will fail with the following notice if you are in debug mode:
A request was sent to this URL (http://localhost:5000/foo) but a redirect was issued automatically by the routing system to "http://localhost:5000/foo/". The URL was defined with a trailing slash so Flask will automatically redirect to the URL with the trailing slash if it was accessed without one. Make sure to directly send your POST-request to this URL since we can't make browsers or HTTP clients redirect with form data reliably or without user interaction
Moreover, it appears that you cannot even do this:
#app.route('/foo', methods=['POST'])
#app.route('/foo/', methods=['POST'])
def post_foo():
return "bar"
Or this:
#app.route('/foo', methods=['POST'])
def post_foo_no_slash():
return redirect(url_for('post_foo'), code=302)
#app.route('/foo/', methods=['POST'])
def post_foo():
return "bar"
Is there any way to get POST to work on both non-trailing and trailing slashes?
Please refer to this post:
Trailing slash triggers 404 in Flask path rule
You can disable strict slashes to support your needs
Globally:
app = Flask(__name__)
app.url_map.strict_slashes = False
... or per route
#app.route('/foo', methods=['POST'], strict_slashes=False)
def foo():
return 'foo'
You can also check this link. There is separate discussion on github on this one. https://github.com/pallets/flask/issues/1783
You can check request.path whether /foo/ or not then redirect it to where you want:
#app.before_request
def before_request():
if request.path == '/foo':
return redirect(url_for('foo'), code=123)
#app.route('/foo/', methods=['POST'])
def foo():
return 'foo'
$ http post localhost:5000/foo
127.0.0.1 - - [08/Mar/2017 13:06:48] "POST /foo HTTP/1.1" 123
Currently I am working on an Oauthlib-Flask implementation for a non-REST API. But I have two scenarios where I want to change/add a value of the flask request object. Since it is immutable this doesn't come with ease. I tried making a duplicate as it is suggested in Changing values on a werkzeug request object. But since the #oauth.authorize_handler uses the given request object I would have to replace it, which resolves in an UnboundLocalError: local variable 'request' referenced before assignment error. Here is my sample code (it is a part of the implicit grant):
#app.route('/oauth/authorize', methods=['GET', 'POST'])
#login
#oauth.authorize_handler
def authorize(*args, **kwargs):
if request.method == 'GET':
client_id = kwargs.get('client_id')
client = Client.query.filter_by(client_id=client_id).first()
kwargs['client'] = client
return render_template('authorize.html', **kwargs)
r = make_duplicate_request(request)
#Change/add values of r
request = r
return True
Am I doing something wrong or is there another possibility to change the request object?
Thanks for your help!
Update:
The code above describes the situation where I want to pass information to the tokensetter function. This could be done with a global variable, but I wanted to avoid that.
In my signup routine the client sends a request like this to the API:
params_signup = {
"schemas":["urn:scim:schemas:core:2.0:User"],
"expireIn":3600,
"username":"test#web.de",
"password":"123",
"access_token":"",
"externalId":"tmeinhardt",
"grant_type":"password",
"client_id":"1",
"params":{
"age":"20-30",
"gender":"m",
}
}
I need the grant_type and client_id part only for the tokenhandler and wanted to add it manually to the request object. But since this object is immutable...
Writing this for those who will come across this and are trying to implement an OAuth flow.
Don't use decorators use middleware instead
I believe you should handle this in a middleware. In the middleware, you can set the authorization property of the 2nd parameter of the call function which contains the current wsgi app environment variables you have passed in your init function.
Look at code below:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
cookie = Request(environ).cookies.get('access_token')
if cookie is not None:
environ['HTTP_AUTHORIZATION']='Bearer '+cookie
return self.app(environ, start_response)
you can create a new request and splat the headers into a new dict of an HttpRequest (eg. Authlib's HttpRequest)
from flask import request as _req
from authlib.oauth2.rfc6749 import HttpRequest
request = HttpRequest(
_req.method, _req.full_path, _req.data, {**_req.headers, **auth}
)
request.req = _req
then you can work with this new request instead of the Flask request