I've been trying to build an API for more than 4 hours now and I searched and asked everywhere I could but I can't find help. The problem is at the level of handling POST requests. I tried with NodeJS (testify and express (as well as middlewares)) and Python (Flask, bottle) and I still can't get why I get an empty object with express or None in python. I have the following code with bottle
1 from bottle import run, Bottle, post, request, redirect
2 import json
3
4 pika = Bottle()
5
6 #post("/shorturl")
7 def shorten():
8 data = request.json
9 #data = json.dumps(rdata)
10 print(data)
11 return f"You posted {data}"
12
13 run(host="localhost", port=3000, debug=True)
And I had the following code at the beginning (I deleted and restarted from scratch) - you can find tweet here.
I can't get None with flask and bottle when using request.get_json() and request.json() respectively which I've found are the way to do it from the docs.
Any help appreciated.
Thanks
Your python code seems to be correct, because I tried and it worked.
For troubleshooting you can insert:
print(request.headers['Content-Type']) #output should be: application/json'
print(request.body.read()) #output should be like: b'{"key":"value"}'
I used Postman and when I tried the first time, I made the mistake to select body form instead of raw. In Postman you have to write Content-Type:appliction/json in the header manually and insert the json as raw.
I assume in Restler it's similar (I never used Restler).
So if you have to configure it manually, make sure that your header contains 'Content-Type':'application/json'
And your body is set to raw and look like this.
If for example form-data was selected, the manually set header would not be used by postman and print(request.header['Content-Type']) would output something like this: multipart/form-data; boundary=--------------------------742283902180513961059188
Could imagine Restler has the same snare.
Here is a way to handle a dynamic routing of the api. Now you just have to add methods to the API class and they are automatically picked up by the bottle app. I am merging POST and GET into one method string to merge query parameters and forms into one payload which you can access via self.payload
import ujson as json
from login import User
def check(data):
try:
if isinstance(data, (str,bytes)):
return json.loads(data)
except:
return data
return data
def merge_dicts(*args):
result = {}
for dictionary in args:
result.update(dictionary or {})
return result
class API:
def __init__(self, payload, user):
self.payload = payload
self.option = ''
self.request = None
#classmethod
def bot(cls, request, option, user):
payload = merge_dicts(dict(request.forms), dict(request.query.decode())) # merge query and form inputs
slf = cls(payload, user)
slf.request = request
slf.option = str(option)
return slf
def test(self): # localhost/api/test
return self.payload
#get('/api/<command>')
#post('/api/<command>')
#get('/api/<command>/<option>')
#post('/api/<command>/<option>')
def routeapi(command='', option=''):
user = User()
wapi = API.bot(request, option, user)
func = getattr(wapi, f"{command}", None)
if callable(func):
result = func()
if result:
if request.method == 'GET':
response.headers['Content-Type'] = 'application/json'
response.headers['Cache-Control'] = 'no-cache'
return {command:json.check(result)}
else:
return {command:None}
Related
I want to pass a variable called manual to Flask a route, which will then do something based on the value in the POST form data. But the forms data is interpreted as string in flask even though I send it in a form as a dictionary.
here's the code
#app.route("/result", methods= [ 'POST', 'GET'])
def result():
manual = request.form.get("manual")
if manual is None:
return "manual is required"
here's how I am sending the data
r = requests.get('http://127.0.0.1:5000/result'
,data={manual':False})
I understand that I can do something like;
if manual == 'True'
but I don't want to be comparing strings, I want to do it in the standard way whichever it is.
Thanks
First of all, do a POST request, not a GET:
r = requests.post('http://127.0.0.1:5000/result', json={'manual': False})
Then (untested):
#app.route("/result", methods=['POST'])
def result():
json_data = flask.request.json
manual = json_data.get("manual")
if manual is None:
return "manual is required"
Have a look at the doc for details: More complicated POST requests.
Note that there are differences between using the data parameter and the json parameter. An important thing to note is the presence of the Content-Type header:
Using the json parameter in the request will change the Content-Type
in the header to application/json.
I am working on a simple service with my show_greeting endpoint handling Get request while set_greeting is my Post.
The purpose of this app is that when "header_message: {header parameter}" is sent to set_greeting, {header parameter} will be returned in the header for responses to show_greeting and to reset {header parameter}, "clear" would reset header_message and header.
I have tried using global variables but encountered an error with shadowing from outside the scope and am not sure which approach to take for this. For now, I would like to learn how to return {header parameter} from my /show_greeting endpoint.
Edit: The /show_greeting endpoint returns holiday_message from the request. The header that I would like to send in addition to holiday_message is "header_message".
My code is as follows:
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
(I do not know how to set header here from header_message in set_greeting)
return received['holiday_message']
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'header_message' in posted:
(I attempted save_message = posted['header_message'] here but this approach failed)
return "Header Message Set"
else:
return "Please Send A Header Message"
if __name__ == '__main__':
app.run()
My recommendation is to use the session object. It stores the data in a cookie, which is sent with every request.
If a cookie is not desired, there are other options for saving sessions. For this, however, an extension will be necessary.
Saving with global variables should also work, but is not recommended.
A file or a database can also be used if the data is to be saved across multiple requests from many users.
The data of the post body can be accessed via request.form, while the url parameters of a get request can be queried via request.args.
from flask import Flask
from flask import request, session
app = Flask(__name__)
app.secret_key = b'your secret here'
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
# get the saved message or an empty string if no message is saved
header_message = session.get('header_message', '')
return f"{received['holiday_message']} - {header_message}"
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.form
if 'header_message' in posted:
# store the message
session['header_message'] = posted['header_message']
return "Header Message Set"
else:
# clear the message
session.pop('header_message', None)
return "Please Send A Header Message"
Much success in your further steps.
If I understood your problem, you can work with "g" the flask global object.
Check this code, I expect it will fix your issue.
from flask import g # Added
from flask import Flask, request, make_response, Response
app = Flask(__name__)
#app.route('/show_greeting', methods=['GET'])
def show_greeting():
received = request.args
return g.saved_message # Modified
#app.route('/set_greeting', methods=['POST'])
def set_greeting():
posted = request.args
if 'message' in posted:
g.saved_message = posted['request'] # Added
return "Message Set"
else:
return "Please Send A Greeting Message"
if __name__ == '__main__':
app.run()
There are some operations that needs to be done before running some routes. For example :
check if we recognise the user,
check the language,
check the location,
set variables in the navbar (here after named header) of the html
and so on, then make decisions based on the outcome and lastly run the requested route.
I find it hard to use the respose.set_cookie("cookie_name", actual_cookie) inside a decorator. It seems flask has a "make_response" object that works well (see here on stack overflow issue 34543157 : Python Flask - Setting a cookie using a decorator), but I find it difficult to reproduce the same thing with bottle.
any how here is my attempt that is not working :
#python3
#/decorator_cookie.py
from bottle import request, response, redirect
from other_module import datamodel, db_pointer, secret_value #custom_module
import json
cookie_value = None
surfer_email_exist_in_db = None
header = None
db_pointer = instanciation_of_a_db_connexion_to_tables
surfer = db_pointer.get(request.get_cookie('surfer')) if db_pointer.get(request.get_cookie('surfer')) != None else "empty"
def set_header(func):
def header_manager():
global cookie_value, surfer_email_exist_in_db, header, db_pointer
cookie_value = True #for stack-overflow question convenience
surfer_email_exist_in_db = True #for stack-overflow question convenience
if not all([cookie_value, surfer_email_exist_in_db]):
redirect('/login')
else:
header = json.dumps(db_pointer.get('header_fr'))
response.set_cookie("header", header, secret = secret_value, path = "/", httponly = True)
return func()
return header_manager
and the main file where the routing goes to
#python3
#/main.py
from bottle import route, request
from decorator_cookie import set_header
from other_module secret_value
#route('/lets_try')
#set_header
def lets_try():
header = request.get_cookie('header', secret = secret_value)
print(header) #here I get None
return template('lets_try.tpl', headers = header)
I also tried set the cookie like that :
make_response = response(func).set_cookie("header", header, secret = secret_value, path = "/", httponly = True)
But got an error :)
Here is the response doc : Response documentation
Do you have any clues ?
Thanks
There is no issue with your code, what you are missing is understanding is understanding
Request 1 [By Browser/No Cookies] -> Request has No cookies -> Response you add cookie header
Request 2 [By Browser/Header Cookies] -> Request has Header cookies -> Response
So for your first request Request.get_cookie will return None but for your second request it will actually return the value
I am developing a django python web application. In my webpage, I am sending a request to my API by sending a 'term' and my API is supposed to return the 'content' field of the search.
My content contains 'xxx is good' in my database.
Here is my code in views.py
def get_RuleStatement(request):
if request.is_ajax():
q = request.GET.get('term', '')
rule_statements = RuleStatement.objects.filter(content__icontains = q )[:1]
results = []
for rule_statement in rule_statements:
rule_statement_json = {}
rule_statement_json['content'] = rule_statement.content
results.append(rule_statement_json)
data = json.dumps(results)
else:
data = 'fail'
mimetype = 'application/json'
return HttpResponse(data, mimetype)
For some reason, whenever I send the following request: http://website.com/api/get_RuleStatement/?term=xxx
It returns 'fail' even through my database contains data 'xxx is good'. Can anyone suggest where I am going wrong?
The only reason possible for it is is_ajax() is return False. That means your are not making the AJAX request. I believe you are making normal HTTP call to your endpoint.
See the django documentation and check that your HTTP request has the HTTP header HTTP_X_REQUESTED_WITH set with the string 'XMLHttpRequest'. Then request.is_ajax() will return True.
Note modern libraries like jQuery will automatically set this HTTP header for you; e.g., if you get jquery and do
<script>
$.get('/api/get_RuleStatement', {'term': 'xxx'})
</script>
Alternatively, you can get rid of the if request.is_ajax(): line and make it
def get_RuleStatement(request):
q = request.GET.get('term', '')
rule_statements = RuleStatement.objects.filter(content__icontains = q)[:1]
results = []
for rule_statement in rule_statements:
results.append({'content': rule_statement.content})
data = json.dumps(results)
mimetype = 'application/json'
return HttpResponse(data, mimetype)
if you don't care about it being an ajax call and want to be able to see the response otherwise. Also you may want to consider using JsonResponse instead of HttpResponse so you don't have to serialize or set MIME-type yourself. E.g.,
def get_RuleStatement(request):
q = request.GET.get('term', '')
rule_statements = RuleStatement.objects.filter(content__icontains = q)[:1]
results = []
for rule_statement in rule_statements:
results.append({'content': rule_statement.content})
return JsonResponse(results, safe=False)
# the safe = False is neccessary when
# you serialize non-dicts like results which is a list
Given a simple Flask application, I'm just curious about whether there is a proper way to modify a Response in the hooks such as process_response?
e.g. Given:
from flask import Flask, Response
class MyFlask(Flask):
def process_response(self, response):
# edit response data, eg. add "... MORE!", but
# keep eg mimetype, status_code
response.data += "... This is added" # but should I modify `data`?
return response
# or should I:
# return Response(response.data + "... this is also added",
# mimetype=response.mimetype, etc)
app = MyFlask(__name__)
#app.route('/')
def root():
return "abddef"
if __name__ == '__main__':
app.run()
Is it proper to just create a new response each time, or is it canonical to just edit in-place the response parameter and return that modified response?
This may be purely stylistic, but I'm curious – and I haven't noticed anything in my reading that would indicate the preferred way to do this (even though it's probably quite common).
Thanks for reading.
From the Flask.process_response docs:
Can be overridden in order to modify the response object before it's sent to the WSGI server.
The response object is created on flask dispacher mechanism (Flask.full_dispatch_request). So if you want to create response objects under your own way, override Flask.make_reponse. Use Flask.process_response only when the desired modifications can be made using the created response object parameter.
Actually, you can use Flask.process_response to intercept and modify the response this way:
from flask import Flask
import json
import ast
appVersion = 'v1.0.0'
class LocalFlask(Flask):
def process_response(self, response):
#Every response will be processed here first
response.headers['App-Version'] = appVersion
success = True if response.status_code in [ 200, 201, 204 ] else False
message = 'Ok' if success else 'Error'
dict_str = response.data.decode("UTF-8")
dataDict = ast.literal_eval(dict_str)
standard_response_data = {
'success': success,
'message': message,
'result': dataDict
}
response.data = json.dumps(standard_response_data)
super(LocalFlask, self).process_response(response)
return response