Hello we have problem to POST data using curl !!
curl "http://127.0.0.1:8000/construct" -X POST -H "Content-Type: application/json" -d '{"journal_id": "JE000001", "entry_date" : "2016-11-06", "create_time" : "2016-11-06 18:00:00", "created_by": "Adam", "post_status": "P", "account_code" : "100000", "amount" : 16453.24, "dr_cr" : "C"}'
Return 400
#app.route("/construct", methods = ['GET', 'POST'])
def construct():
if request.method == 'POST':
data = request.form['data']
data1 = loads(data)
conn, cursor = database_connect()
data1 = data_contruct_new(data1)
data1['pre_hash'] = get_pre_hash(cursor)
data1['nouce'] = call_nouce()
data1['hash'] = call_hash(data1)
conn.close()
return jsonpify(data1)
else:
return request.method # return method for test
Your service is expecting doubly-encoded data:
data = request.form['data']
data1 = loads(data)
Instead of just parsing the contents of the request as JSON, you're parsing the contents of the request as www-form-urlencoded, with a single field named data, and then parsing the contents of that form field as JSON.
The form field is the incoming contents parsed as a form. If you don't have one, don't use this.
If you want to parse the raw contents, that's either data or get_data:
data = request.data
… or:
data = request.get_data()
But if you've done everything properly on both sides, you don't even need this, just json or get_json:
data1 = request.get_json()
If you, for some reason, really do want doubly-encoded data, you need to generate that on the client side, and send the right Content-Type. That would look something like this mess:
'curl "http://127.0.0.1:8000/construct" -X POST -F 'data=%7B%22journal_id%22%3A%20%22JE000001%22%2C%20%22entry_date%22%20%3A%20%222016-11-06%22%2C%20%22create_time%22%20%3A%20%222016-11-06%2018%3A00%3A00%22%2C%20%22created_by%22%3A%20%22Adam%22%2C%20%22post_status%22%3A%20%22P%22%2C%20%22account_code%22%20%3A%20%22100000%22%2C%20%22amount%22%20%3A%2016453.24%2C%20%22dr_cr%22%20%3A%20%22C%22%7D'
Hopefully you can see that this is not what you actually want, unless the broken server side is completely out of your hands and can't be fixed.
Related
I'm getting a POST request inside a Flask app from Slack. The request is sent when a user presses on an interactive message button. According to Slack docs I must extract the body of the request to verify the signature.
My computed signature doesn't match the one sent by Slack, though.
In fact, the body of the request comes as some encoded string. The string is actually an encoded dictionary instead of a query str parameters, as expected.
Here's the beginning of my view:
#app.route('/register', methods=['POST'])
def register_visit():
data = request.get_data()
signature = request.headers.get('X-Slack-Signature', None)
timestamp = request.headers.get('X-Slack-Request-Timestamp', None)
signing_secret = b'aaaaaaaaaaaaaaaa'
# old message, ignore
if round(actual_time.time() - float(timestamp)) > 60 * 5:
return
concatenated = ("v0:%s:%s" % (timestamp, data)).encode('utf-8')
computed_signature = 'v0=' + hmac.new(signing_secret, msg=concatenated, digestmod=hashlib.sha256).hexdigest()
if hmac.compare_digest(computed_signature, signature):
...
I've tried to format the received data to make it look like:
token=fdjkgjl&user_id=1234... but I am not aware of all of the necessary parameters that have to be present in the data.
Any ideas are highly appreciated.
The body of the message is following - after being URL decoded (note I've modified possibly sensitive data):
b'payload={"type":"interactive_message","actions":
[{"name":"yes_button","type":"button","value":"236"}],"callback_id":"visit_button","team":{"id":"fffff","domain":"ffff"},"channel":{"id":"ffff","name":"directmessage"},"user":{"id":"ffffff","name":"fffft"},"action_ts":"1540403943.419120","message_ts":"1541403949.000100","attachment_id":"1","token":"8LpjBuv13J7xAjhl2lEajoBU","is_app_unfurl":false,"original_message":{"text":"Test","bot_id":"DDDDDDDDD","attachments":[{"callback_id":"visit_button","text":"Register","id":1,"color":"3AA3E3","actions":[{"id":"1","name":"yes_button","text":"Yes","type":"button","value":"236","style":""}],"fallback":"Register"}],"type":"message","subtype":"bot_message","ts":"1540413949.000100"},"response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":"464662548327.425084163429.dda35a299eedb940ab98dbb9386b56f0"}'
The reason you are getting the "garbled" data is that you are using request.get_data(). That method will return the raw data of a request, but not do any decoding for you.
Much more convenient is to use request.form.get('payload'), which will directly give you the JSON string of the request object. You can then convert that into a dict object with json.loads() to process it further in your app.
Note that the format you received is the correct format for interactive messages. You will not get a query string (e.g. "token=abc;user_id?def...") as you suggested (like for slash command requests). Interactive message request will always contain the request as JSON string in a payload form property. See here for reference.
Here is a simple working example, which will reply a greeting to the user that pressed the button. It will work directly with Slack, but I recommend using Postman to test it.
#app.py
from flask import Flask, request #import main Flask class and request object
import json
app = Flask(__name__) #create the Flask app
#app.route('/register', methods=['POST'])
def register_visit():
slack_req = json.loads(request.form.get('payload'))
response = '{"text": "Hi, <#' + slack_req["user"]["id"] + '>"}'
return response, 200, {'content-type': 'application/json'}
if __name__ == '__main__':
app.run(debug=True, port=5000) #run app in debug mode on port 5000
OK, the issue wasn't related to how Slack sends me the message. It was about misunderstanding which data comes as bytes and which data is unicode. The culprit was string formatting in my case - the line concatenated = ("v0:%s:%s" % (timestamp, data)).encode('utf-8') should have been concatenated = (b"v0:%b:%b" % (timestamp.encode("utf-8"), data)). Data is already bytes, timestamp meanwhile is unicode.
Cannot believe I've banged my head on this for hours -_-
#app.route('/register', methods=['POST'])
def register_visit():
data = request.get_data()
signature = request.headers.get('X-Slack-Signature', None)
timestamp = request.headers.get('X-Slack-Request-Timestamp', None)
signing_secret = b'aaaaaaaaaaaaaaaa'
# old message, ignore
if round(actual_time.time() - float(timestamp)) > 60 * 5:
return
concatenated = (b"v0:%b:%b" % (timestamp.encode("utf-8"), data))
computed_signature = 'v0=' + hmac.new(signing_secret, msg=concatenated,
digestmod=hashlib.sha256).hexdigest()
if hmac.compare_digest(computed_signature, signature):
...
This worked for me
from urllib import parse
parsed_text = parse.unquote('your bytes text here')
I am a beginner in making API. I have followed the blog https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask and able to create the Get - Post API method.
I am using Flask to making Rest API. Please see the code below in which I want to take question as input in API and return an answer in JSON format by making my code as an API.
app = Flask(__name__)
#app.route('/match/api/v1', methods = ['POST'])
def my_form_post():
if not request.json or not 'question' in request.json:
abort(400)
input_text_p = request.json['question'] # access input from curl request
reference_data = request.json['data'] # to access data field from the API request
path = 'airtel_faq.xlsx'
question_list, answer_list = read_excel_file(path) # reading some reference data from an excel file
input_text = input_text_p.translate(None, string.punctuation) # remove punctuation
final_answer = find_similarity(input_text, answer_list, question_list)
print "Final Answer is : ", final_answer
values = [
{'id' : 1,
'answer' : final_answer # answer I want in JSON
'done' : False
}
]
return jsonify({'values': values}), 201
if __name__ == '__main__':
app.run(debug = True)
I am trying to pass hash input for data tag in the request to API. I am not aware how to do this. The curl request I am making is giving error:
Failed to decode JSON object: Expecting ',' delimiter or '}': line 1 column 80 (char 79)
The curl request I am making :
curl -i -H "Content-Type: application/json" -X POST -d '{"question":"Hi","cutoff":"0.3", "data":"{"q":"hello whats going on","a":"I am fine","type":"1"}"}' http://localhost:5000/match/api
Please let me know how to do this. Do I have to include anything in my script. Or is there a way to pass the JSON file in API call by giving path to the file.
In order to parse the JSON request, it needs to be valid JSON. Try this request:
curl -i -H "Content-Type: application/json" -X POST -d '{"question": "Hi", "cutoff": "0.3", "data": {"q": "hello whats going on", "a": "I am fine", "type": "1"}}' http://localhost:5000/match/api
I want to use Flask Restful to update a list with an object using a PUT request.
The resulting JSON should look like:
{"EmployeeID":"12345", "firstname":"Joe","lastname":"Bloggs","SupervisorName":"Name","SupervisorID":"2468","role":"Role","active":"True","hours":["{'date':'01/01/2017','qty':'3','project':'Project 1'}"]"}
The Hours field in the JSON is a list. The aim is to append an object to the list on each PUT request.
The Parser for hours is:
parser.add_argument('hours', action='append')
The Put method code is:
def put(self, EmployeeID=None):
data = parser.parse_args()
if not data:
data = {'ERROR': 'No Data'}
return jsonify(data)
else:
if EmployeeID:
if mongo.db.employee.find_one({'EmployeeID': EmployeeID}):
mongo.db.employee.update_one({'EmployeeID': EmployeeID}, {set: {"hours": data.get('hours')}})
return {'response': 'Employee:'+str(EmployeeID)+' updated'}
else:
return {'Error': 'employee ' + str(EmployeeID) + ' not found'}
else:
return {'response': 'Employee ID missing'}
Is the update_one method the right one to use here?
using curl PUT request :
curl -H "Content-type: application/json" -X PUT -d '{"EmployeeID":"1234",...,"hours":{'time':'','qty':'3','project':'Project 1'}}' http://127.0.0.1:5000/api/people/1234
Gave the error:
{
"message": "Failed to decode JSON object: Expecting property name enclosed in double quotes: line 1 column 168 (char 167)"
}
But When I add the quotations into the request it return an error:
{
"message": "Failed to decode JSON object: Unterminated string starting at: line 1 column 167 (char 166)"
}
I can't figure out whether there is an issue with the requests or with the actual Flask-Restful code.
What is the correct way to go about updating a Mongo document list with an object?
I don't have much experience with Flask, but from the error messages you've posted, it looks like the JSON in the request cannot be correctly decoded by Flask. I believe this is because you are using single quotes in your JSON string and are wrapping the entire JSON with yet another set of single quotes.
Try to just use double quotes in your JSON string and wrap the entire JSON string in single quotes like so:
curl -H "Content-type: application/json" -X PUT -d '{"EmployeeID":"1234","hours":{"time":"","qty":"3","project":"Project 1"}}' http://127.0.0.1:5000/api/people/1234
I think that should solve the issue of not being able to decode JSON form the request.
If anyone runs into this issue I finally got there.
I had to change the RequestParser:
parser.add_argument('hours')
Removing the action='append'
Then using the mongo update:
mongo.db.employee.update_one({'EmployeeID': EmployeeID}, {'$addToSet': {"hours": data.get('hours')}})
so when I run the python code the server (google) give me a different response than when I run curl command. Can someone tell me where I'm wrong please?
code:
import urllib2, simplejson
def MapsWIFI(card):
req = urllib2.Request("https://www.googleapis.com/geolocation/v1/geolocate?key=AI...")
jWifi = """
{
"wifiAccessPoints": [
{
"macAddress": "64:D1:A3:0A:11:65",
"channel": 6,
},
... #some AP here
]
}
"""
print jWifi
req.add_header("Content-Type", "application/json")
jWifiReport = urllib2.urlopen(req,simplejson.dumps(jWifi)).read()
print jWifiReport
APdetected = str(len(wifiCell))
mapsDict = simplejson.loads(jWifiReport)
location = str(mapsDict.get("location",{}))[1:-1]
accuracy = "Accuracy: "+str(mapsDict.get("accuracy",{}))[1:-1]
mapMe = "|---"+location.split(",")[0]+"\n|---"+location.split(",")[1][1:]+"\n|---$
return mapMe
MapsWIFI("wlp8s0")
And the command is:
curl -d #file2.json -H "Content-Type: application/json" -i "https://www.googleapis.com/geolocation/v1/geolocate?key=AI..."
where file2.json contains exactly jWifi in that format.
The problem is that, as said, the location returned by the code is different from the location returned by curl. I don't get error code so I thing that the syntax is correct.
The data is already a JSON encoded string, you don't want to encode it twice.
Pass it in without encoding it again:
jWifiReport = urllib2.urlopen(req, jWifi).read()
You only need to encode if you have a Python data structure (a dictionary in this case).
I am about to implement a Push Task queue in Flask/Google App Engine.
Essentially I would like to POST to the API and execute the underlying work in the task queue.
The initial entry point is a REST API (flask_restful)
class FTRecordsAPI(Resource):
def post(self):
arguments = self.reqparser.parse_args()
json_records = arguments.get('records')
user = User.query(...).get()
if user:
taskqueue.add(url='/worker/', params={'user': user})
return '', 201
else:
return '', 401
The worker is defined as a view in the url.py:
app.add_url_rule('/worker', 'worker',
view_func=csrf_protect.exempt(TaskView.as_view('taskView')))
And the TaskView is:
from flask.globals import request
class TaskView(MethodView):
def post(self):
user = request.json['user']
return "OK"
Strangely when I debug in the TaskView nowhere in the request object is any trace of the user object I sent to the /worker. However I find in there the records object which was from the previous call ?!
What am I missing please?
Try:
taskqueue.add(url='/worker', params={'user': user}, method="POST")
and
user = request.form.get('user')
As marcadian pointed out, taskqueue uses POST by default, so perhaps you need the request.form to access the POST vars.
Sorry for the thread resurrection, but should you want to use json directly you can do the following for instance:
taskstop = taskqueue.add(url='/stop_std_watcher',
target='worker',
payload=json.dumps({'watcherjson': client.client_watcher}),
method='POST',
headers={'Content-Type': 'application/json'})
the important bits being to add the headers and payload kwargs
On the receiving end, you got then:
#app.route('/stop_std_watcher', methods=['POST'])
def stop_std_watcher():
# curl -i -H "Content-Type: application/json" -X POST -d #stop.json http://localhost:8081/stop_std_watcher
watcherjson = request.json['watcherjson']
I added the curl in comments should you want to test your post route manually, can be useful