I have got Apache2 Installed and Python working.
I am having a problem though. I have two pages.
One a Python Page and the other an Html Page with JQuery
Can someone please tell me how I can get my ajax post to work correctly.
<html>
<head>
</head>
<body>
<script>
$(function()
{
alert('Im going to start processing');
$.ajax({
url: "saveList.py",
type: "post",
data: {'param':{"hello":"world"}},
dataType: "application/json",
success : function(response)
{
alert(response);
}
});
});
</script>
</body>
</html>
And the Python Code
import sys
import json
def index(req):
result = {'success':'true','message':'The Command Completed Successfully'};
data = sys.stdin.read();
myjson = json.loads(data);
return str(myjson);
OK, let's move to your updated question.
First, you should pass Ajax data property in string representation. Then, since you mix dataType and contentType properties, change dataType value to "json":
$.ajax({
url: "saveList.py",
type: "post",
data: JSON.stringify({'param':{"hello":"world"}}),
dataType: "json",
success: function(response) {
alert(response);
}
});
Finally, modify your code a bit to work with JSON request as follows:
#!/usr/bin/python
import sys, json
result = {'success':'true','message':'The Command Completed Successfully'};
myjson = json.load(sys.stdin)
# Do something with 'myjson' object
print 'Content-Type: application/json\n\n'
print json.dumps(result) # or "json.dump(result, sys.stdout)"
As a result, in the success handler of Ajax request you will receive object with success and message properties.
You should read json data like this:
#!/usr/bin/env python3
import os
import sys
import json
content_len = int(os.environ["CONTENT_LENGTH"])
req_body = sys.stdin.read(content_len)
my_dict = json.loads(req_body)
With the following code, you can run into problems:
myjson = json.load(sys.stdin)
or written less succinctly:
requ_body = sys.stdin.read()
my_dict = json.load(requ_body)
That does work for me when my cgi script is on an apache server, but you can't count on that working in general--as I found out when my cgi script was on another server. According to the cgi spec:
RFC 3875 CGI Version 1.1 October 2004
4.2. Request Message-Body
Request data is accessed by the script in a system-defined method;
unless defined otherwise, this will be by reading the 'standard
input' file descriptor or file handle.
Request-Data = [ request-body ] [ extension-data ]
request-body = <CONTENT_LENGTH>OCTET
extension-data = *OCTET
A request-body is supplied with the request if the CONTENT_LENGTH is
not NULL. The server MUST make at least that many bytes available
for the script to read. The server MAY signal an end-of-file
condition after CONTENT_LENGTH bytes have been read or it MAY supply
extension data. Therefore, the script MUST NOT attempt to read more
than CONTENT_LENGTH bytes, even if more data is available. However,
it is not obliged to read any of the data.
The key line is:
the script MUST NOT attempt to read more
than CONTENT_LENGTH bytes, even if more data is available.
Apparently, apache sends an eof signal to the cgi script immediately after sending the request body to the cgi script, which causes sys.stdin.read() to return. But according to the cgi spec, a server is not required to send an eof signal after the body of the request, and I found that my cgi script was hanging on sys.stdin.read()--when my script was on another server, which eventually caused a timeout error.
Therefore, in order to read in json data in the general case, you should do this:
content_len = int(os.environ["CONTENT_LENGTH"])
req_body = sys.stdin.read(content_len)
my_dict = json.loads(req_body)
The server sets a bunch of environment variables for cgi scripts, which contain header information, one of which is CONTENT_LENGTH.
Here is what a failed curl request looked like when I used myjson = json.load(sys.stdin):
-v verbose output
-H specify one header
--data implicitly specifies a POST request
Note that curl automatically calculates a Content-Length header
for you.
~$ curl -v \
> -H 'Content-Type: application/json' \
> --data '{"a": 1, "b": 2}' \
> http://localhost:65451/cgi-bin/1.py
* Trying ::1...
* TCP_NODELAY set
* Connection failed
* connect to ::1 port 65451 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 65451 (#0)
> POST /cgi-bin/1.py HTTP/1.1
> Host: localhost:65451
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 16
>
* upload completely sent off: 16 out of 16 bytes
=== hung here for about 5 seconds ====
< HTTP/1.1 504 Gateway Time-out
< Date: Thu, 08 Mar 2018 17:53:30 GMT
< Content-Type: text/html
< Server: inets/6.4.5
* no chunk, no close, no size. Assume close to signal end
<
* Closing connection 0
Adding a little bit to the great #7stud's answer
I had some problems with content length when reading unicode which I fixed by reading from buffer:
content_length = int(os.environ["CONTENT_LENGTH"])
data = sys.stdin.buffer.read(content_length).decode('utf-8')
Related
I am trying to build a PayPal sandbox platform with JavaScript, but unfortunately I did not find any working (at least for me) content from PayPal for setting up a platform app with vanilla JavaScript.
I used this PayPal guide to integrate the following code:
<!-- Replace "test" with your own sandbox Business account app client ID -->
<script src="https://www.paypal.com/sdk/js?client-id=test¤cy=USD"></script>
<!-- Set up a container element for the button -->
<div id="paypal-button-container"></div>
<script>
paypal.Buttons({
// Sets up the transaction when a payment button is clicked
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [{
amount: {
value: '77.44' // Can also reference a variable or function
}
}]
});
},
// Finalize the transaction after payer approval
onApprove: (data, actions) => {
return actions.order.capture().then(function(orderData) {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
const transaction = orderData.purchase_units[0].payments.captures[0];
alert(`Transaction ${transaction.status}: ${transaction.id}\n\nSee console for all available details`);
// When ready to go live, remove the alert and show a success message within this page. For example:
// const element = document.getElementById('paypal-button-container');
// element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Or go to another URL: actions.redirect('thank_you.html');
});
}
}).render('#paypal-button-container');
</script>
It was working for "simple" payment, when I directly put in the client id of the person receiving the money, but I would like to use this as some kind of platform/marketplace, where different people offer stuff and receive their money directly...
I found this configuration guide to referre to an merchant within the platform and changed
<script src="https://www.paypal.com/sdk/js?client-id=test¤cy=USD"></script>
to
<script src="https://www.paypal.com/sdk/js?client-id=YOUR_CLIENT_ID&merchant-id=XXX"></script>
and I used the client sandbox id of the platform (create with PayPal) and added the id of the account, which should receive the money. But since then the payment process is not working anymore and I receive a bunch of errors:
POST https://www.sandbox.paypal.com/v2/checkout/orders/1N412585WU345820U/capture 403
Here and according to PayPal API responsesit says, that my platform needs approval, but according to the info box inside my PayPal platform app it says, that sandbox apps do not need approval to run:
POST https://www.sandbox.paypal.com/smart/api/order/1N412585WU345820U/capture 500
According to PayPal API responses this seems to be a PayPal side error, but I believe in my case it could be solved within the "main problem" (whatever it is...)
Uncaught Error: Api: /smart/api/order/1N412585WU345820U/capture returned status code: 500 (Corr ID: 3d42a7056c067)
According to PayPal API responses this seems to be an internal server error by PayPal as well
It still could be that I am using the false JavaScript snippet to integrate a PayPay Platform, but so far I found nothing different, that appeared to me to suit better...
Update I 13.04.
This is the exact code block I am currently trying to get to work:
<script src="https://www.paypal.com/sdk/js?client-id=ARoShWzmlqdngxfJ31ly7ZADrsyWU3Fa3fhlOJF0mJXdpIaZ5z99R7VkQuMEb_4Z_azlHfuo7Mk4k5qK&merchant-id=<?= $paypal ?>¤cy=EUR"></script>
<div id="paypal-button-container"></div>
<script>
paypal.Buttons({
createOrder: (data, actions) => {
return actions.order.create({
purchase_units: [{
amount: {
value: '<?= $amount ?>' // Can also reference a variable or function
}
}]
});
},
onApprove: (data, actions) => {
return actions.order.capture().then(function(orderData) {
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
const transaction = orderData.purchase_units[0].payments.captures[0];
actions.redirect('http://localhost/exit.php');
});
}
}).render('#paypal-button-container');
</script>
I fetch the PHP variables from my database, where all information is stored. Also tried to add the IDs manually instead of PHP variables, but did not change anything.
BTW: If I click the PayPal-Button to pay, the second window to log into PayPal opens up and I can log in with one of my sandbox accounts, also the price is currect. Just after trying to complete the payment I am not forwarded to http://localhost/exit.php, but receive the below erros instead.
Here is an image of all the logs, which appear. Just recognized a time out error as the first error, but not sure, if this triggers the problem:
Update II 13.04.
As mentioned in this comment I tried to onboard the sellers via the Partner Referrals API using this sample from PayPal. I first created an access token and then wanted to generate a singup link with this access token. But instead of the desired response I receive an INVALID_REQUEST message inside my command prompt while trying to generate the singup link:
{"name":"INVALID_REQUEST","message":"Request is not well-formed, syntactically incorrect, or violates schema.","debug_id":"b54a8fd49ab4c","information_link":"","details":[{"issue":"INVALID_PARAMETER_SYNTAX","description":"Referral data is not a valid json object."}],"links":[]}* Connection #0 to host api-m.sandbox.paypal.com left intact
I search on Google, but did not really find helpful responses. Also the information_link-field is unfortunately empty.
This was my request:
curl -v -X POST https://api-m.sandbox.paypal.com/v2/customer/partner-referrals \ -H "Content-Type: application/json" \ -H "Authorization: Bearer A21AAJAr6XCnxUwsXGpH_Hv55XrrDh-wct-FnmSFQzFrY4mNHatkD--IaHXlUSOUBipsItfdxb56S371AdrEK7gFmcHEXEiJA" \ -d '{"tracking_id": "PayPal-ID","operations": [{"operation": "API_INTEGRATION", "api_integration_preference": {"rest_api_integration": {"integration_method": "PAYPAL","integration_type": "THIRD_PARTY","third_party_details": {"features": ["PAYMENT","REFUND"]}}}}],"products": ["EXPRESS_CHECKOUT"],"legal_consents": [{"type": "SHARE_DATA_CONSENT","granted": true}]}'
And here is the response I got:
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 151.101.1.35:443...
* Connected to api-m.sandbox.paypal.com (151.101.1.35) port 443 (#0)
* schannel: disabled automatic use of client certificate
* schannel: ALPN, offering http/1.1
* schannel: ALPN, server accepted to use http/1.1
> POST /v2/customer/partner-referrals HTTP/1.1
> Host: api-m.sandbox.paypal.com
> User-Agent: curl/7.79.1
> Accept: */*
> Content-Type: application/json
> Authorization: Bearer A21AAJAr6XCnxUwsXGpH_Hv55XrrDh-wct-FnmSFQzFrY4mNHatkD--IaHXlUSOUBipsItfdxb56S371AdrEK7gFmcHEXEiJA
> Content-Length: 14
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< Connection: keep-alive
< Content-Length: 278
< Content-Type: application/json;charset=utf-8
< Server: nginx/1.18.0 (Ubuntu)
< Cache-Control: max-age=0, no-cache, no-store, must-revalidate
< Etag: W/"116-GOos19pXDvjfMGG/lkQZOQx3ysM"
< Paypal-Debug-Id: b54a8fd49ab4c
< Set-Cookie: ts_c=vr%3D2321e53c1800a609769dd99dffff68d4%26vt%3D2321e53c1800a609769dd99dffff68d5; Domain=.paypal.com; Path=/; Expires=Sun, 13 Apr 2025 07:00:42 GMT; Secure; SameSite=None; HttpOnly
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< Accept-Ranges: bytes
< Via: 1.1 varnish, 1.1 varnish
< Edge-Control: max-age=0
< Date: Wed, 13 Apr 2022 13:34:25 GMT
< X-Served-By: cache-fra19126-FRA, cache-muc13976-MUC
< X-Cache: MISS, MISS
< X-Cache-Hits: 0, 0
< X-Timer: S1649856865.498776,VS0,VE171
<
{"name":"INVALID_REQUEST","message":"Request is not well-formed, syntactically incorrect, or violates schema.","debug_id":"b54a8fd49ab4c","information_link":"","details":[{"issue":"INVALID_PARAMETER_SYNTAX","description":"Referral data is not a valid json object."}],"links":[]}* Connection #0 to host api-m.sandbox.paypal.com left intact
Note: Unnecessary use of -X or --request, POST is already inferred.
* Could not resolve host: \
* Closing connection 1
curl: (6) Could not resolve host: \
Note: Unnecessary use of -X or --request, POST is already inferred.
* Could not resolve host: \
* Closing connection 2
curl: (6) Could not resolve host: \
Note: Unnecessary use of -X or --request, POST is already inferred.
* Could not resolve host: \
* Closing connection 3
curl: (6) Could not resolve host: \
Note: Unnecessary use of -X or --request, POST is already inferred.
* Closing connection -1
curl: (3) URL using bad/illegal format or missing URL
curl: (3) bad range specification in URL position 2:
[{operation:
^
Solution
Had to onboard seller first like Preston PHX mentioned in the comments using the cUrl commands from the PayPal documentation. My code from Update II 13.04. was correct. It just happend, that it got cut off inside the command prompt. So using Postman solved the issue and everything is now working!
The 500 error is not relevant, and only occurring because of a fallback attempt after the 403.
The reason for the 403 may be various, but if you are including a merchant-id on the SDK line then there should also be a payee object with a merchant_id key with that value in the order creation. See the orders create API reference for details.
Ensure the REST APP with the client-id you are using has the appropriate permissions. Changes to permissions may take up to 9 hours if the existing access token is cached, but you can also create a new app.
I am trying to send a Hexadecimal command to a TV RS232 through a Global Cache HTTP to RS232 device. Global cache can also operate device with RS232 over TCP/UDP port 4999.
Global Cache has a URL on the device that can take raw data and pass it on to the RS232 device or we can POST to the URL port 80 from file containing the HEX command but in the field, our sending device will need to create the HEX command without accessing a file and must use the HTTP POST rather than sending direct to port 4999.
Here is examples that work:
Command line:
echo -n -e "\x7F\x08\x99\xA2\xB3\xC4\x02\xFF\x01\x07\xCF" | nc 192.168.1.222 4999
Node JS TCP
hexString = "7F0899A2B3C402FF0107CF";
rawHex = Buffer.from(hexString, 'hex');
console.log(rawHex);
client.connect(4999, '192.168.1.222', () => {
console.log("Connected");
client.write(rawHex); //This will send the byte buffer over TCP
client.end();
})
Node JS HTTP
hexString = "7F0899A2B3C402FF0138CF";
rawHex = Buffer.from(hexString, 'hex');
console.log(rawHex);
client = net.createConnection({
host: '192.168.1.222',
port: 80,
path: '/api/v1/serialports/1/sendserial/'
}, () => {
console.log("Connected");
client.write(rawHex); //This will send the byte buffer over HTTP
client.end();
}
)
Insominia
First write the command line output to a file:
echo -n -e "\x7F\x08\x99\xA2\xB3\xC4\x02\xFF\x01\x07\xCF" > /tmp/hexcommand
Send that file as data with HTTP POST
> POST /api/v1/serialports/1/sendserial HTTP/1.1
> Host: 192.168.1.222
> User-Agent: insomnia/2021.7.2
> Content-Type: application/octet-stream
> Accept: */*
> Content-Length: 11
| �����8�
As mentioned, the device that will send this command strings cannot access a file and does not have node.js. So we need to get the POST data HEX buffer as a text data and send it with a HTTP POST.
FAILS:
Trying some javascript encoded HEX or other web url style hex encoder do not work. If I process the ASCII representation with some javascript examples and send the result, it is not affecting the device.
Example:
str = "7F0899A2B3C402FF0107CF";
function convertString(){
return(str.split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(2,"0"),""))
}
Result of the function is more like web compatible hex. Notice the "text/plain" type in following POST example.
GlobalCache document specifies text/plain is required, so we are not sure why the application stream worked on the previous working example.
> POST /api/v1/serialports/1/sendserial HTTP/1.1
> Host: 192.168.1.222
> User-Agent: insomnia/2021.7.2
> Content-Type: text/plain
> Accept: */*
> Content-Length: 11
| 37463038393941324233433430324646303130374346
Also, sending the ASCII representation with the same POST method fails on all cases.
7F0899A2B3C402FF0107CF
7f0899a2b3c402ff0107cf
7F 08 99 A2 B3 C4 02 FF 01 07 CF
\x7F\x08\x99\xA2\xB3\xC4\x02\xFF\x01\x07\xCF
None of above are equivalent to the working examples that produce the usable HEX output.
Thanks for any advice how to change that ASCII text into a string that may go working across the HTTP Post
I am trying to learn to build a web application, and that application needs data generated from a python script. After googling around. I found this link and it seems that I need to:
write a server side application in Python. Define a URL(route) that runs your script.
in my Javascript code, make an HTTP request to the URL defined in Step 1.
In my java script, I have the following ajax call, I'm not too sure what goes in the url field:
$.ajax({
type: "get",
url: "http://localhost:5000",
cache: false,
async: "asynchronous",
dataType: "text",
success: function (data) {
//console.log(JSON.stringify(data));
console.log("---->" + data);
},
error: function (request, status, error) {
console.log("Error: " + error);
},
});
As for my web server side, I wanted to write it from sockets since I want to learn some socket programing as well, so following another post I wrote my server below, in this server, my goal is to just return a simple string to prove that this works, but ultimately I want to be able to return a json object :
import socket
import threading
import json
import pdb
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 5000))
sock.listen(1)
print("Listening at------>>> ", sock.getsockname())
connections = []
# Reply as HTTP/1.1 server, saying "HTTP OK" (code 200).
response_proto = 'HTTP/1.1'
response_status = '200'
response_status_text = 'OK' # this can be random
res_status = "{} {} {}".format(response_proto, response_status,
response_status_text)
response_body_raw = "hello world"
# Clearly state that connection will be closed after this response,
# and specify length of response body
response_headers = {
'Content-Type': 'text; encoding=utf8',
'Content-Length': len(response_body_raw),
'Connection': 'close',
}
response_headers_raw = ''.join('%s: %s\n' % (k, v) for k, v in
response_headers.items())
def handler(c, a):
global connections
while True:
data = c.recv(1024)
print(data)
for connection in connections:
# sending all this stuff
connection.sendall(res_status.encode('utf-8'))
connection.sendall('\n'.encode('utf-8'))
connection.sendall(response_headers_raw.encode('utf-8'))
# to separate headers from body
connection.sendall('\n'.encode('utf-8'))
connection.sendall(response_body_raw.encode('utf-8'))
if not data:
connections.remove(c)
c.close()
break
while True:
c, a = sock.accept()
print("Connected by------->>>", a)
cThread = threading.Thread(target=handler, args=(c, a))
cThread.daemon = True
cThread.start()
connections.append(c)
when I run my website using VS code live server extension, I get the following errors:
Access to XMLHttpRequest at 'http://localhost:5000/?_=1586356660223' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
GET http://localhost:5000/?_=1586356660223 net::ERR_FAILED
I looked into the No 'Access-Control-Allow-Origin' error, and it seems that I cannot provide url as localhost in my ajax call. if not, then what should I put in the url field if I want to talk to my local server?
Add a Access-Control-Allow-Origin to your response header:
response_headers = {
'Access-Control-Allow-Origin': '*',
...
}
So, as already mentioned in my Comment, I used a Flask server to process the POST-Data sent with Ajax.
Basically, you can set up the server like this:
from flask import Flask, requests
app = Flask(__name__)
#app.route("/", methods=['POST', 'GET'])
def main_page():
return "200"
if __name__ == "__main__":
app.run(debug=True, host='192.169.178.62')
with the host='192.169.178.62', you can specify the IP you want to run your Flask app.
I would suggest you find out your Computers IP, and either use that one to run Flask or use an IP in the same network.
In your AJAX, you need to enter this URL to send the request to.
If anything is not working as it should, feel free to contact me.
There's a file 1740 bytes long, it's contents is read to a Buffer res. res.length is 1740 and res.toString('binary', 0, res.length).length is also 1740.
I send a POST request using request lib
request.post({
url: endpoint,
headers: headers,
body: res.toString('binary', 0, res.length)
}, callback);
The request goes to a gSOAP server. Through hours of debugging on the server I send request to, we found the following: the request that comes to the server is 1753 bytes long and some characters are converted. In particular, hex B7 becomes C2 B7, so it's converted as described here: http://www.fileformat.info/info/unicode/char/b7/index.htm
I tried setting encoding: 'binary' and encoding: null to request params, same result (with encoding : null I only get the error message as a buffer, but that's all).
I tried using https library and piping a strean into the request, same result.
Best regards, Alexander
EDIT
At the moment, I found a workaround with cURL, just sending a request from cli with --data-binary "#file_to_which_i_dumped_the_request"' does the thing. But the app and the nodejs server itself is shipped within an installer, so we'd have to install cURL on users' machines too which is... acceptable, but is not the best option.
So is there way to send a binary POST body with nodejs?
Thanks.
Don't use the binary string encoding: it has been deprecated (see here) and it only makes sense if "the other side" will decode it back to a buffer.
Just use the buffer directly:
request.post({
url : endpoint,
headers : headers,
body : res
}, callback);
I want to use javascript to retrieve a json object from a python script
Ive tried using various methods of ajax and post but cant get anything working.
For now I have tried to set it up like this
My Javascript portion:
I have tried
$.post('./cgi-bin/serverscript.py', { type: 'add'}, function(data) {
console.log('POSTed: ' + data);
});
and
$.ajax({
type:"post",
url:"./cgi-bin/serverscript.py",
data: {type: "add"},
success: function(o){ console.log(o); alert(o);}
});
My Python
import json import cgi import cgitb cgitb.enable() data = cgi.FieldStorage()
req = data.getfirst("type") print "Content-type: application/json"
print print (json.JSONEncoder().encode({"status":"ok"}))
I am getting a 500 (internal server error)
Have you tried doing just
print (json.JSONEncoder().encode({"status":"ok"}))
instead of printing the content-type and a blank line?
Have you checked your host's server logs to see if it's giving you any output?
Before asking here, a good idea would be to ssh to your host, if you can, and running the program directly, which will most likely print the error in the terminal.
This is far too general at the moment, there are so many reasons why a CGI request can fail ( misconfigured environment, libraries not installed, permissions errors )
Go back and read your servers logs and see if that shines any more light on the issue.