Flask POST Request Response With Processed Request [duplicate] - python

This question already has answers here:
Flask view return error "View function did not return a response"
(3 answers)
Closed 1 year ago.
I am trying to send a webhook with a json to my flask app- it then takes the key values and inputs it to my search engine and should reply back with the results and 200 (code that it was successful) my code looks like this:
from flask import Flask, request, abort
from Webhooks.TGSearchEngine import TGSearch
app = Flask(__name__)
#app.route('/', methods=['POST'])
def webhook():
sys.stdout.flush()
if request.method == 'POST':
reply = request.json['reply']
query = request.json['query']
channel = request.json['channel']
messageid = request.json['id']
return TGSearch.main(reply, query, channel, messageid), 200
else:
abort(400)
But I get TypeError( TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.
My TGSearch.main() module takes the vars and returns a dictionary with the results. I don't understand why it would throw this error. Any ideas?

If you are sure that TGSearch.main(reply, query, channel, messageid), 200 doesn't return None value, you had to convert the result of the response to a valid response object.
Try one of these options:
You can pass the result in a variable to a template rendering:
response = TGSearch.main(reply, query, channel, messageid), 200
return render_template("example.html", response)
You can convert the response into a JSON:
from flask.json import jsonify
result = TGSearch.main(reply, query, channel, messageid), 200
response = jsonify(result)
Or in the case you are getting a tuple, you can access separate values and make them a string.
result = TGSearch.main(reply, query, channel, messageid), 200 // "Message", 200
return str(result[0]) + str(result[1])

Flask must always return something. In your else statement you should add a return.
Note: you specified methods=['POST'] in your route, so the only method allowed will be POST, I don't think it's necessary to check if request.method == 'POST'

Related

Setting a parameter in Post body to header when accessing Get endpoint in Flask?

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()

Handle post request in flask and bottle

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}

Flask Python - int is not callable [duplicate]

This question already has an answer here:
Flask view raises TypeError: 'bool' object is not callable
(1 answer)
Closed 3 years ago.
from flask import Flask, jsonify, request
app = Flask(__name__)
#app.route('/add-two-numbers', methods=["POST"])
def add_two_numbers():
request_payload = request.get_json()
x = request_payload["x"]
y = request_payload["y"]
return str(x + y)
if __name__ == "__main__":
app.run(debug=True)
The above code is working.. however, I get an "int is not callable" error in my return statement when I do return x+y but it is fine when I do return str(x+y). Why is this?
This is answered here, but in short, a response can be a string, but not int.
The documentation:
If a response object of the correct type is returned it’s directly returned from the view.
If it’s a string, a response object is created with that data and the default parameters.
If a tuple is returned the items in the tuple can provide extra information. Such tuples have to be in the form (response, status, headers) or (response, headers) where at least one item has to be in the tuple. The status value will override the status code and headers can be a list or dictionary of additional header values.
If none of that works, Flask will assume the return value is a valid WSGI application and convert that into a response object.

Change response based on content type of request in Flask

I have a Flask application that returns both HTML pages and JSON responses to API requests. I want to change what an error handler returns based on the content type of the request. If the client requests application/json, I want to return a jsonify response, otherwise I want to return a render_template response. How can I detect what was requested and change the response appropriately?
The current error handlers I have only return an HTML response.
def register_errorhandlers(app):
"""Register error handlers."""
def render_error(error):
"""Render error template."""
# If a HTTPException, pull the `code` attribute; default to 500
error_code = getattr(error, 'code', 500)
return render_template('{0}.html'.format(error_code)), error_code
for errcode in [401, 404, 500]:
app.errorhandler(errcode)(render_error)
Use request.content_type to get the content type the client sent with the request. Use request.accept_mimetypes the get the mimetypes the client indicated it can accept in a response. Use these to determine what to return.
from flask import request, jsonify, render_template
if request.accept_mimetypes.accept_json:
return jsonify(...)
else:
return render_template(...)
I used the after_request decorator to do this and checked the content type:
#app.after_request
def after_request_helper(resp):
if resp.content_type == "text/html":
# If a HTTPException, pull the `code` attribute; default to 500
error_code = getattr(error, 'code', 500)
return render_template('{0}.html'.format(error_code)), error_code
else:
return app.errorhandler(errcode)(render_error)
A more detailed answer:
def wants_json_response():
return request.accept_mimetypes['application/json'] >= \
request.accept_mimetypes['text/html']
The wants_json_response() helper function compares the preference for JSON or HTML selected by the client in their list of preferred formats. If JSON rates higher than HTML, then it is necessary to return a JSON response.
Otherwise, return the original HTML responses based on templates.
For the JSON responses would slightly supplement the function with one condition:
if wants_json_response(): which is what you need. So the answer is in that.
If the condition is true we could write a function that would generate a response:
def api_error_response(status_code, message=None):
payload = {'error': HTTP_STATUS_CODES.get(status_code, 'Unknown error')}
if message:
payload['message'] = message
response = jsonify(payload)
response.status_code = status_code
return response
This function uses the handy HTTP_STATUS_CODES dictionary from Werkzeug (a core dependency of Flask) that provides a short descriptive name for each HTTP status code.
For easier and faster understanding, 'error' is used to represent errors, so you only need to worry about the numeric status code and the optional long description.
The jsonify() function returns a Flask Response object with a default status code of 200, so after the response is created, it is necessary to set the status code to the correct one for the error.
So if we put it all together now it would look like this:
# app/__init__.py
import requests
def register_errorhandlers(app):
from .errors import render_error
for e in [
requests.codes.INTERNAL_SERVER_ERROR,
requests.codes.NOT_FOUND,
requests.codes.UNAUTHORIZED,
]:
app.errorhandler(e)(render_error)
and
# app/errors.py
import requests
from flask import render_template, request, jsonify
from werkzeug.http import HTTP_STATUS_CODES
from .extensions import db
def api_error_response(status_code, message=None):
payload = {'error': HTTP_STATUS_CODES.get(status_code, 'Unknown error')}
if message:
payload['message'] = message
response = jsonify(payload)
response.status_code = status_code
return response
def wants_json_response():
return request.accept_mimetypes['application/json'] >= \
request.accept_mimetypes['text/html']
def render_error(e):
if requests.codes.INTERNAL_SERVER_ERROR == e.code:
db.session.rollback()
if wants_json_response():
return api_error_response(e.code)
else:
return render_template(f'{e.code}.html'), e.code
Additionally
Then they could use the response generation for other cases as well.
The most common error that the API is going to return is going to be
the code 400, which is the error for “bad request”. This is the error
that is used when the client sends a request that has invalid data in it.
In order to generate messages to the function below even easier in these cases, we forward only the required description - message.
def bad_request(message):
return api_error_response(400, message)
I hope this will help in approaching with errors :)

Return HTTP status code 201 in flask

We're using Flask for one of our API's and I was just wondering if anyone knew how to return a HTTP response 201?
For errors such as 404 we can call:
from flask import abort
abort(404)
But for 201 I get
LookupError: no exception for 201
Do I need to create my own exception like this in the docs?
You can use Response to return any http status code.
> from flask import Response
> return Response("{'a':'b'}", status=201, mimetype='application/json')
You can read about it here.
return render_template('page.html'), 201
You can do
result = {'a': 'b'}
return result, 201
if you want to return a JSON data in the response along with the error code
You can read about responses here and here for make_response API details
As lacks suggested send status code in return statement
and if you are storing it in some variable like
notfound = 404
invalid = 403
ok = 200
and using
return xyz, notfound
than time make sure its type is int not str. as I faced this small issue
also here is list of status code followed globally
http://www.w3.org/Protocols/HTTP/HTRESP.html
Hope it helps.
In your flask code, you should ideally specify the MIME type as often as possible, as well:
return html_page_str, 200, {'ContentType':'text/html'}
return json.dumps({'success':True}), 200, {'ContentType':'application/json'}
...etc
Ripping off Luc's comment here, but to return a blank response, like a 201 the simplest option is to use the following return in your route.
return "", 201
So for example:
#app.route('/database', methods=["PUT"])
def database():
update_database(request)
return "", 201
you can also use flask_api for sending response
from flask_api import status
#app.route('/your-api/')
def empty_view(self):
content = {'your content here'}
return content, status.HTTP_201_CREATED
you can find reference here http://www.flaskapi.org/api-guide/status-codes/
In my case I had to combine the above in order to make it work
return Response(json.dumps({'Error': 'Error in payload'}),
status=422,
mimetype="application/json")
Dependent on how the API is created, normally with a 201 (created) you would return the resource which was created. For example if it was creating a user account you would do something like:
return {"data": {"username": "test","id":"fdsf345"}}, 201
Note the postfixed number is the status code returned.
Alternatively, you may want to send a message to the client such as:
return {"msg": "Created Successfully"}, 201
for error 404 you can
def post():
#either pass or get error
post = Model.query.get_or_404()
return jsonify(post.to_json())
for 201 success
def new_post():
post = Model.from_json(request.json)
return jsonify(post.to_json()), 201, \
{'Location': url_for('api.get_post', id=post.id, _external=True)}
You just need to add your status code after your returning data like this:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello_world(): # put application's code here
return 'Hello World!',201
if __name__ == '__main__':
app.run()
It's a basic flask project. After starting it and you will find that when we request http://127.0.0.1:5000/ you will get a status 201 from web broswer console.
So, if you are using flask_restful Package for API's
returning 201 would becomes like
def bla(*args, **kwargs):
...
return data, 201
where data should be any hashable/ JsonSerialiable value, like dict, string.

Categories