How to make API call in Flask? - flask

I am trying to make request to Clash of Clan Api and after requesting the right data it returns 200 ok & if i search wrong data it returns 404 not found. How to flash message after the data is not found according to the HTTP response from the API?
my views in flask
#app.route('/player', methods=['GET', 'POST'])
def player():
headers = header
url = ('https://api.clashofclans.com/v1/players/{}')
query = request.form.get('search')
player_id = urllib.parse.quote(query)
stats = requests.get(url.format(player_id), headers=headers).json()
return render_template('player.html', stats=stats, data=stats['achievements'])

stats = requests.get(url.format(player_id), headers=headers).json()
Here, you just take the JSON from the body and discard a bunch of useful data. Instead,
response = requests.get(url.format(player_id), headers=headers)
stats = response.json()
status_code = response.status_code
success = response.ok
# ...
You can see all the things you can get from the Response object in API documentation.

Related

Django freezes when reading data from request.body

Let's say we have a simple Django view:
def my_view(request):
content = request.body
# some actions with content varible
response = HttpResponse('<h1>It work!</h1>')
And a simple api client, let's say based on the requests library, sending malformed Django view data:
headers = dict()
headers['Accept'] = '*/*'
headers['Content-Length'] = '13409'
headers['Content-Type'] = 'application/x-compressed'
headers['Expect'] = '100-continue'
headers['Host'] = '127.0.0.1:8000'
headers['User-Agent'] = 'Api client'
headers['content-encoding'] = 'gzip'
url = 'http://127.0.0.1:8000/api'
request_body = ''
r = requests.post(
url,
data=request_body,
headers=headers
)
As you can see, request_body contains an empty string, but the Content-Length header stores the value 13409. When such a request arrives, Django hangs on the line reading request.body. No exceptions occur. How to solve this problem? I cannot influence the client, so the only thing I can do is rewrite the Django view. Django version 3.2.15 is used.

POST request parameters returning "none"

I have the following POST request:
import requests
payload = {'key1':'value1'}
r = requests.post('http://127.0.0.1:5000/test', params=payload)
print(r.url)
print(r.text)
My flask app tries to return the value from key1:
from flask import Flask, request
app = Flask(__name__)
#app.route('/test', methods = ["GET", "POST"])
def query_params():
val = request.args.get("key1")
return val
Going to http://127.0.0.1:5000/test returns
TypeError TypeError: The view function for 'query_params' did not
return a valid response. The function either returned None or ended
without a return statement.
Output from flask debugger:
127.0.0.1 - - [21/May/2022 21:47:17] "POST /test?key1=value1 HTTP/1.1" 200 -
What am I missing here? Thank you very much for your help!
Cheers,
Mario
when you visit the http://127.0.0.1:5000/test from your browser, its a GET request and there are no parameters passed in your request.
if you visit http://127.0.0.1:5000/test?key1=value1, your expected output will be printed.
Regarding the requests.post snippet you used: if you see the documentation, params is usually used in GET requests, the POSTS get the data argument. but seems your code works, it appends the parameters to the request (as would have happened in GET) and makes a POST request. Interesting finding!
r = requests.post('http://127.0.0.1:8000/test', data=payload)
you could enhance your code by using a "fallback" value if the parameter is not present:
#app.route('/test', methods = ["GET", "POST"])
def query_params():
val = request.args.get("key1", "parameter was not provided")
return val
To conclude, i think you should decide if the request method to submit the data should be a GET or a POST, and then update your code accordingly (if GET, your snippets is OK, if you should use POST, try to switch the params to data and then your flask route code to work with the new payload "format".
updated code to "launch a python script if the flask app receives a POST request with a specific key:value pair":
#app.route('/test', methods = ["GET", "POST"])
def query_params():
if request.method == 'POST':
val = request.args.get("the expected key", "parameter was not provided")
if val == "the expected value":
# do the things you want to do
return "processing done!"

Data in request.body can't be found by request.data - Django Rest Framework

I'm writing a django application. I am trying to call my django rest framework from outside, and expecting an answer.
I use requests to send some data to a function in the DRF like this:
j=[i.json() for i in AttachmentType.objects.annotate(text_len=Length('terms')).filter(text_len__gt=1)]
j = json.dumps(j)
url = settings.WEBSERVICE_URL + '/api/v1/inference'
headers = {
'Content-Disposition': f'attachment; filename={file_name}',
'callback': 'http://localhost',
'type':j,
'x-api-key': settings.WEBSERVICE_API_KEY
}
data = {
'type':j
}
files = {
'file':file
}
response = requests.post(
url,
headers=headers,
files=files,
json=data,
)
In the DRF, i use the request object to get the data.
class InferenceView(APIView):
"""
From a pdf file, extract infos and return it
"""
permission_classes = [HasAPIKey]
def post(self, request):
print("REQUEST FILE",request.FILES)
print("REQUEST DATA",request.data)
callback = request.headers.get('callback', None)
# check correctness of callback
msg, ok = check_callback(callback)
if not ok: # if not ok return bad request
return build_json_response(msg, 400)
# get zip file
zip_file = request.FILES.get('file', None)
parsed = json.loads(request.data.get('type', None).replace("'","\""))
The problem is that the data in the DRF are not received correctly. Whatever I send from the requests.post is not received.
I am sending a file and a JSON together. The file somehow is received, but other data are not.
If I try to do something like
request.data.update({"type":j})
in the DRF, the JSON is correctly added to the data, so it is not a problem with the JSON I'm trying to send itself.
Another thing, request.body shows that the JSON is somehow present in the body, but request.data can't find it.
I don't want to use request.body directly because I can't understand why it is present in the body but not visible with request.data.
In this line
response = requests.post(
url,
headers=headers,
files=files,
json=data,
)
replace json=data with data=data
like this:
response = requests.post(
url,
headers=headers,
files=files,
data=data,
)

Why postman POST method do not provide params in `json` format?

In my flask project, there is a route:
def request_parse(req_data):
if req_data.method == 'POST':
data = req_data.json
elif req_data.method == 'GET':
data = req_data.args
return data
#app.route('/api/d/u', methods=['POST'])
def update(): # name, domain_list, pem_key, pem_cert, origin_ips
data = request_parse(request)
name = data.get('name')
domain_list = data.get('domain_list')
pem_key = data.get('pem_key')
pem_cert = data.get('pem_cert')
origin_ips = data.get('origin_ips')
in Postman I request it like this:
I use postman request the api:
you see it is POST method, and in my project debug, I found the request data is in form,not in json.
I also tried form-data and x-www-form-urlencoded format, all are in form.
why postman POST method do not provide params to request.json? and is it possible to provide params in request.json?
If you want to send it as JSON, change from x-www-form-urlencoded to raw and you should see a drop down for Text, JSON, HTML. You can then select JSON

Why i am getting 400 Bad Request error when sending json data in Flask?

I am trying to write a small restful api application, i am using Chrome Postman extension for sending requests to the app .
I believe that my code does not have mistakes but every time i am sending post request a 400 Bad Request error raising , here is my code:
#api_route.route('/api', methods=['GET'])
def api():
return jsonify({'message':'Api v1.0'})
#api_route.route('/api', methods=['POST'])
def create_user():
data = request.get_json()
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message', 'New User Created!'})
The json data that i am sending looks like this:
{"email" : "Test", "password" : "123123123"}
Why i am getting the 400 error ??
Update:
Screenshots for the requests using Postman:
GET Request
POST Request
Here i am initiating api route inside api controller :
from flask import Blueprint
api_route = Blueprint(
'api',
__name__
)
from . import views
then i am registering it inside def create_app() function :
from .api import api_route
app.register_blueprint(api_route)
Here are the extensions that i am using in my application:
toolbar = DebugToolbarExtension()
assets_env = Environment()
cache = Cache()
moment = Moment()
htmlminify = HTMLMIN()
csrf = CSRFProtect()
jac = JAC()
googlemap = GoogleMaps()
session = Session()
principal = Principal()
I solved the problem, i've initiated CSRFProtect with app so i need to include X-CSRFToken in all my requests, so i have two choices:
1 - To include the csrf_token in request.headers for all the requests
2 - Using #csrf.exempt decorator that coming with flask_wtf.csrf
For now i am using #csrf.exempt, so it become like this:
#api_route.route('/api', methods=['GET','POST'])
#csrf.exempt
def create_user():
if request.method == 'GET':
return jsonify({'message' : 'API v1.0'})
elif request.method == 'POST':
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user_api = Api(email=data['email'], password=hashed_password)
db.session.add(new_user_api)
db.session.commit()
return jsonify({'message' : 'New user created!'})
return return jsonify({'message' : 'No user has been added!'})
Thanks for #MrPyCharm for his interests , salute :) .
A good approach would be to structure your views as follows:
Instead of creating view with same route for different request methods, you can handle the request methods in the same view:
#api_route.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'GET':
return jsonify({'message':'Api v1.0'})
else:
data = request.get_json(force=True)
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message': 'New User Created!'})
# Just in case the if condition didn't satisfy
return None
A note for anyone else experiencing this with PostMan and Flask - you will also hit a HTTP 404 if your URL in PostMan is HTTPS but your Flask app only handles HTTP.