i have trouble handling http post request with JSON as body of the request.
i am running IIS with python as server script.
this is the code that makes request:
var http = new XMLHttpRequest();
var url = "http://myurl.ext/py/script.py";
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
console.log(http.responseText);
}
}
data = {"field":"value", "number":5}
http.open('POST', url, true);
http.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
http.send(JSON.stringify(data));
on server side i have:
import cgi
import http.client
print("Content-Type: text/text")
print("")
print(cgi.parse())
print(http.client.HTTPResponse)
cgi.parse() gives empty string
http.client.HTTPResponse gives empty string
cgi.FieldStorage() gives empty string, but if i submit a form, it returns values of the input fields.
i want to send JSON data in the background to the script and return some processed values as JSON as well.
The cgi module is designed primarily with form processing from a POST request, or query string parsing from a GET request, in mind. As such it does not really provide much that might help you process a JSON request.
Keep in mind that all the CGI script does is read data from the process' environment and its standard input. Thus you can just read the body of the POST from sys.stdin:
#!/usr/bin/env python3
import sys
import json
from pprint import pprint
print('Content-Type: text/plain')
print()
try:
data = json.load(sys.stdin)
print("Received:")
pprint(data)
except json.JSONDecodeError as exc:
print('Failed to decode JSON request: {}'.format(exc))
All this script does is to decode the standard input as JSON and pretty print it back out in the response.
You might be better off looking at something more usable such as flask, bottle, etc.
to make it work you have to explicitly tell how much to read.
data = "";
if int(os.environ.get('CONTENT_LENGTH', 0)) != 0:
for i in range(int(os.environ.get('CONTENT_LENGTH', 0))):
data += sys.stdin.read(1)
print(data)
this what worked for me
Related
I am sending this data to my python flask, and I would like help with how to receive it.
I get the arguments 'guid' and 'emp_empresa' perfectly, but json comes very werido.
data sent:
Postman screenshot
python code:
#app.route('/send_func_info', methods=["POST"])
def send_func_info():
print(request.args.get('guid', ''))
print(request.args.get('cod_empresa', ''))
print(request.get_data())
return jsonify({'return': 'ok'})
result:
abc
123
b"[\r\n { \r\n cod_funcionario: '123',\r\n nome_funcionario: 'toin',\r\n funcao: 'carinha da \xc3\xa1gua'\r\n },\r\n {\r\n cod_funcionario: '456',\r\n nome_funciona
rio: 'juanzim',\r\n funcao: 'carinha da moto'\r\n\r\n },\r\n { \r\n cod_funcionario: '789',\r\n nome_funcionario: 'marquinhos',\r\n funcao: 'carinha da maratona'\r\n\r\n }
\r\n]"
127.0.0.1 - - [11/Aug/2020 15:40:00] "?[37mPOST /send_func_info?guid=abc&cod_empresa=123 HTTP/1.1?[0m" 200 -
Typically the method I use to get data usable for my applications
from flask import Flask, request
import json
#app.route('/create_servers', methods=['POST'])
def create_server():
data = request.data
data = json.loads(data)
server_type = data["server_type"]
I'm currently using flask version: Flask==1.1.1
JSON, which stands for JavaScript Object Notation, is an "open standard file format, and data interchange format". It is supposed to be human-readable, and it is very useful for transferring information from one place to another – it is language independent. It's often used in web applications like your flask one to exchange information from client to server, and it has started to replace XML (The X in AJAX) as the way that web apps do this.
JSON data is stored in attribute-value pairs, which is like a python dictionary: keys and values. In your HTML files it is likely you will use AJAX with JS, in which case, here is an example of how to do access JSON data, using jQuery.
JS:
<script type="text/javascript">
function getFeedback(source, dest) {
$.getJSON(
'/getfeedback',
{source:source, dest:dest}
).done(function(data) {
$(dest).text(data.text)
}).fail(function() {
$(dest).text(data.text)
)};
And the view function:
from flask import jsonify, request
#flask stuff blah blah blah
#app.route('/delete')
def delete():
try:
source = request.args.get('source')
dest = request.args.get('dest')
return jsonify(result="success",text="Your request from {} to {} was successful".format(source, dest)
except Exception as e:
return str(e)
If successful the JSON will look something like this:
{
"result":"success",
"text":"Your request from #htmlId to #htmlId2 was successful."
}
If you bind the JS function to an HTML event, for example the click of a button, and you include the parameters of the place that you want feedback on and the place you want the feedback to go to (for example "#paragraph1" and "#header1"), you can send a GET request without reloading the page.
Hi I am new to writing web APIs in python. And my understanding of REST is limited
I have a simple Flask API that takes in a python dict {'pdf':pdf_as_bytes, 'filename':string}
The below is my server script:
#app.route("/predict", methods=["POST"])
def predict():
data = {"success": False}
if flask.request.method == "POST":
pdf = flask.request.files["pdf"].read()
filename = flask.request.files["filename"].read().decode("utf-8")
assert isinstance(filename, str), "Got {}".format(type(filename))
assert isinstance(pdf, bytes), "Got {}".format(type(pdf))
# further processing happens and returns a json
This works as intended when I write a python client as follows:
import requests
import os
ip = "localhost"
port = 8605
url = "http://{}:{}/predict".format(ip,port)
path_to_pdf = "./617339931.pdf"
with open(path_to_pdf, "rb") as f:
pdf = f.read() # pdf is a bytes
# the payload must have the following fields: "pdf": bytes, "filename": string object
payload = {"pdf":pdf,"filename":os.path.basename(path_to_pdf).split(".")[0]}
# post request
result = requests.post(url=url, files=payload).json()
# the resulting json always has a field called as success, which returns True or False accordingly
if result["success"] == True:
print(result["data"].keys())
But, When I send a request using Postman I get a 400 Error! Below is the screen shot of the error
I don't understand. How can I change my server code so that it works with Postman and also Python client programs
I just did the same thing, and I think it's because of the double quotes you are putting in key and value, try to take them out.
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'm in the process of writing a very simple Python application for a friend that will query a service and get some data in return. I can manage the GET requests easily enough, but I'm having trouble with the POST requests. Just to get my feet wet, I've only slightly modified their example JSON data, but when I send it, I get an error. Here's the code (with identifying information changed):
import urllib.request
import json
def WebServiceClass(Object):
def __init__(self, user, password, host):
self.user = user
self.password = password
self.host = host
self.url = "https://{}/urldata/".format(self.host)
mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
mgr.add_password(None, "https://{}".format(self.host), self.user, self.password)
self.opener = urllib.request.build_opener(urllib.request.HTTPDigestAuthHandler(mgr))
username = "myusername"
password = "mypassword"
service_host = "thisisthehostinfo"
web_service_object = WebServiceClass(username, password, service_host)
user_query = {"searchFields":
{
"First Name": "firstname",
"Last Name": "lastname"
},
"returnFields":["Entity ID","First Name","Last Name"]
}
user_query = json.dumps(user_query)
user_query = user_query.encode("ascii")
the_url = web_service_object.url + "modules/Users/search"
try:
user_data = web_service_object.opener.open(the_url, user_query)
user_data.read()
except urllib.error.HTTPError as e:
print(e.code)
print(e.read())
I got the class data from their API documentation.
As I said, I can do GET requests fine, but this POST request gives me a 500 error with the following text:
Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
In researching this error, my assumption has become that the above error means I need to submit the data as multipart/form-data. Whether or not that assumption is correct is something I would like to test, but stock Python doesn't appear to have any easy way to create multipart/form-data - there are modules out there, but all of them appear to take a file and convert it to multipart/form-data, whereas I just want to convert this simple JSON data to test.
This leads me to two questions: does it seem as though I'm correct in my assumption that I need multipart/form-data to get this to work correctly? And if so, do I need to put my JSON data into a text file and use one of those modules out there to turn it into multipart, or is there some way to do it without creating a file?
Maybe you want to try the requests lib, You can pass a files param, then requests will send a multipart/form-data POST instead of an application/x-www-form-urlencoded POST. You are not limited to using actual files in that dictionary, however:
import requests
response = requests.post('http://httpbin.org/post', files=dict(foo='bar'))
print response.status_code
If you want to know more about the requests lib, and specially in sending multipart forms take a look at this:
http://docs.python-requests.org/en/master/
and
http://docs.python-requests.org/en/master/user/advanced/?highlight=Multipart-Encoded%20Files
#!/usr/bin/python
import json
import cgi
import os
print 'Content-Type: application/json'
print
response={'host':cgi.escape(os.environ["REMOTE_ADDR"])}
jsondata=json.dumps(response)
print jsondata
I am unable request url throw the following javascript/jquery code:
jquery.getJSON("http://ourdomain/cgi-bin/serverscript.py", function(data){
alert("----------");
});
When I was browsing the same url, I am getting data.
First, check that you're actually loading the jQuery library in your browser. If entering jQuery() or $() in the console returns undefined or [], then you're not loading the jQuery library in the page.
Second, the print method in your python code prints the data, but does not return it back to the calling function.
To fix this, change print to return. Also, I'd suggest changing your function to the following:
response = {'host' : cgi.escape(os.environ["REMOTE_ADDR"])}
return json.dumps(response)
Your print statements don't do anything and the entire purpose of this code snippet is to get the address of the server environment.
In fact, this can all be done in one single line:
return json.dumps( { 'host' : cgi.escape( os.environ[ "REMOTE_ADDR" ] ) } )
It is solved by using jsonp request.