Post JSON to Python CGI

后端 未结 2 1059
误落风尘
误落风尘 2020-11-30 09:17

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

相关标签:
2条回答
  • 2020-11-30 09:43

    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
    
    0 讨论(0)
  • 2020-11-30 09:45

    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.

    0 讨论(0)
提交回复
热议问题