Python3 CGI 프로그래밍



CGI란 무엇입니까

CGI는 현재 NCSA에서 관리하고 있습니다. NCSA는 CGI를 다음과 같이 정의합니다.

CGI(Common Gateway Interface), 공통 게이트웨이 인터페이스로, HTTP 서버와 같은 서버에서 실행되는 프로그램입니다. 동일한 클라이언트 HTML 페이지 인터페이스.


웹 탐색

CGI 작동 방식을 더 잘 이해하려면 웹 페이지에서 링크나 URL을 클릭하는 과정부터 시작해 보세요.

  • 1. 브라우저를 사용하여 URL에 액세스하고 HTTP 웹에 연결하세요. 섬기는 사람.

  • 2. 웹 서버는 요청 정보를 받은 후 URL을 구문 분석하여 해당 파일이 서버에 존재하는지 확인하고, 파일이 존재하면 해당 파일의 내용을 반환합니다. 그렇지 않으면 오류 메시지가 반환됩니다.

  • 3. 브라우저는 서버로부터 정보를 받아 수신된 파일이나 오류 메시지를 표시합니다.

CGI 프로그램은 Python 스크립트, PERL 스크립트, SHELL 스크립트, C 또는 C++ 프로그램 등이 될 수 있습니다.


CGI 아키텍처 다이어그램

Cgi01.png


웹 서버 지원 및 구성

CGI 프로그래밍을 수행하기 전에 웹 서버가 CGI를 지원하고 CGI 처리기를 구성했는지 확인하세요.

Apache는 CGI 구성을 지원합니다:

CGI 디렉터리 설정:

ScriptAlias /cgi-bin/ /var/www/cgi-bin/

모든 HTTP 서버 실행 CGI 프로그램은 미리 구성된 디렉터리에 저장됩니다. 이 디렉토리를 CGI 디렉토리라고 하며 관례적으로 이름은 /var/www/cgi-bin입니다.

CGI 파일의 확장자는 .cgi이며, Python에서도 .py 확장자를 사용할 수 있습니다.

기본적으로 Linux 서버가 실행되도록 구성된 cgi-bin 디렉터리는 /var/www입니다.

CGI 스크립트를 실행하기 위해 다른 디렉터리를 지정하려면 다음과 같이 httpd.conf 구성 파일을 수정할 수 있습니다.

<Directory "/var/www/cgi-bin">
   AllowOverride None
   Options +ExecCGI
   Order allow,deny
   Allow from all
</Directory>

AddHandler에 .py 접미사를 추가하면 .py로 끝나는 Python 스크립트 파일에 액세스할 수 있습니다.

AddHandler cgi-script .cgi .pl .py

첫 번째 CGI 프로그램

우리는 Python을 사용하여 첫 번째 CGI 프로그램을 만듭니다. 파일 이름은 hello.py입니다. 파일은 /var/www/cgi-bin 디렉터리에 있습니다.

#!/usr/bin/python3

print ("Content-type:text/html")
print ()                             # 空行,告诉服务器结束头部
print ('<html>')
print ('<head>')
print ('<meta charset="utf-8">')
print ('<title>Hello Word - 我的第一个 CGI 程序!</title>')
print ('</head>')
print ('<body>')
print ('<h2>Hello Word! 我是来自php中文网的第一CGI程序</h2>')
print ('</body>')
print ('</html>')

hello.py 파일을 저장한 후 수정하고 파일 권한을 755로 수정합니다.

chmod 755 hello.py

위 프로그램은 브라우저에서 액세스할 때 다음 결과를 표시합니다.

1026.jpg

이 hello.py 스크립트는 간단한 Python 스크립트입니다. , 스크립트 첫 번째 줄의 출력 콘텐츠는 "Content -type:text/html"이며 브라우저에 전송되고 표시된 콘텐츠 유형이 "text/html"임을 브라우저에 알려줍니다.

print를 사용하여 빈 줄을 출력하여 서버에 헤더 정보를 끝내도록 지시합니다.


HTTP 헤더

hello.py 파일 콘텐츠 "Content-type: text/html"은 HTTP 헤더의 일부로, 브라우저에 파일의 콘텐츠 유형을 알려주기 위해 브라우저로 전송됩니다.

HTTP 헤더의 형식은 다음과 같습니다.

HTTP 字段名: 字段内容

예:

Content-type: text/html

다음 표는 CGI 프로그램에서 HTTP 헤더에 일반적으로 사용되는 정보를 소개합니다.

描述
Content-type:请求的与实体对应的MIME信息。例如: Content-type:text/html
Expires: Date响应过期的日期和时间
Location: URL用来重定向接收方到非请求URL的位置来完成请求或标识新的资源
Last-modified: Date请求资源的最后修改时间
Content-length: N请求的内容长度
Set-Cookie: String设置Http Cookie

CGI 환경 변수

모든 CGI 프로그램은 CGI 프로그램에서 중요한 역할을 하는 다음 환경 변수를 받습니다.

变量名描述
CONTENT_TYPE这个环境变量的值指示所传递来的信息的MIME类型。目前,环境变量CONTENT_TYPE一般都是:application/x-www-form-urlencoded,他表示数据来自于HTML表单。
CONTENT_LENGTH如果服务器与CGI程序信息的传递方式是POST,这个环境变量即使从标准输入STDIN中可以读到的有效数据的字节数。这个环境变量在读取所输入的数据时必须使用。
HTTP_COOKIE客户机内的 COOKIE 内容。
HTTP_USER_AGENT提供包含了版本数或其他专有数据的客户浏览器信息。
PATH_INFO这个环境变量的值表示紧接在CGI程序名之后的其他路径信息。它常常作为CGI程序的参数出现。
QUERY_STRING如果服务器与CGI程序信息的传递方式是GET,这个环境变量的值即使所传递的信息。这个信息经跟在CGI程序名的后面,两者中间用一个问号'?'分隔。
REMOTE_ADDR这个环境变量的值是发送请求的客户机的IP地址,例如上面的192.168.1.67。这个值总是存在的。而且它是Web客户机需要提供给Web服务器的唯一标识,可以在CGI程序中用它来区分不同的Web客户机。
REMOTE_HOST这个环境变量的值包含发送CGI请求的客户机的主机名。如果不支持你想查询,则无需定义此环境变量。
REQUEST_METHOD提供脚本被调用的方法。对于使用 HTTP/1.0 协议的脚本,仅 GET 和 POST 有意义。
SCRIPT_FILENAMECGI脚本的完整路径
SCRIPT_NAMECGI脚本的的名称
SERVER_NAME这是你的 WEB 服务器的主机名、别名或IP地址。
SERVER_SOFTWARE这个环境变量的值包含了调用CGI程序的HTTP服务器的名称和版本号。例如,上面的值为Apache/2.2.14(Unix)

다음은 CGI 환경 변수를 출력하는 간단한 CGI 스크립트입니다.

#!/usr/bin/python3

import os

print ("Content-type: text/html")
print ()
print ("<meta charset=\"utf-8\">")
print ("<b>环境变量</b><br>")
print ("<ul>")
for key in os.environ.keys():
    print ("<li><span style='color:green'>%30s </span> : %s </li>" % (key,os.environ[key]))
print ("</ul>")

위 내용을 test.py로 저장하고, 파일 권한을 755로 수정합니다. 실행 결과는 다음과 같습니다.

1027.jpg


GET 및 POST method

Browse 서버 클라이언트는 GET 방식과 POST 방식 두 가지 방식으로 서버에 정보를 전송합니다.

GET 메소드를 사용하여 데이터 전송

GET 메소드는 인코딩된 사용자 정보를 서버로 전송합니다. 데이터 정보는 아래와 같이 "?"로 구분된 요청 페이지의 URL에 포함됩니다.

http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
기타 GET 요청에 대한 정보 참고:
  • GET 요청은 캐시될 수 있습니다

  • GET 요청은 브라우저 기록에 남아 있습니다

  • GET 요청은 북마크에 추가될 수 있습니다

  • GET 요청은 민감한 데이터를 처리할 때 사용해서는 안 됩니다

  • GET 요청에는 길이 제한이 있습니다

  • GET 요청은 데이터를 검색하는 데에만 사용해야 합니다

간단한 URL 예: GET 메서드

다음은 GET 메서드를 사용하여 hello_get에 두 개의 URL을 보내는 간단한 URL입니다. .py 프로그램 매개변수:

/cgi-bin/test.py?name=php中文网&url=http://www.php.cn

다음은 hello_get.py 파일의 코드입니다.

#!/usr/bin/python3

# CGI处理模块
import cgi, cgitb 

# 创建 FieldStorage 的实例化
form = cgi.FieldStorage() 

# 获取数据
site_name = form.getvalue('name')
site_url  = form.getvalue('url')

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>php中文网 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2>%s官网:%s</h2>" % (site_name, site_url))
print ("</body>")
print ("</html>")

파일 저장 후 hello_get.py를 수정하고 파일 권한을 755로 수정합니다.

chmod 755 hello_get.py

브라우저 요청 출력 결과:

1028.jpg

간단한 폼 예시 : GET 방식

다음은 HTML을 이용하여 GET 방식으로 두 개의 데이터를 서버로 보내는 폼입니다. 제출한 서버 스크립트도 hello_get.py 파일이고, hello_get.html 코드는 다음과 같습니다. :

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
<form action="/cgi-bin/hello_get.py" method="get">
站点名称: <input type="text" name="name">  <br />

站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>

기본적으로 cgi-bin 디렉토리는 스크립트 파일만 저장할 수 있습니다. hello_get.html을 테스트 디렉토리에 저장하고 파일 권한을 755로 수정합니다.

chmod 755 hello_get.html

Gif 데모는 다음과 같습니다.

hello_get.gif

POST 방법을 사용하여 데이터 전송

POST 방법을 사용하여 서버에 데이터 전송 사용자 비밀번호와 같은 일부 민감한 정보는 POST를 사용하여 데이터를 전송해야 하므로 더 안전하고 안정적입니다.

다음도 hello_get.py인데, 브라우저에서 제출한 POST 폼 데이터도 처리할 수 있습니다.

#!/usr/bin/python3

# CGI处理模块
import cgi, cgitb 

# 创建 FieldStorage 的实例化
form = cgi.FieldStorage() 

# 获取数据
site_name = form.getvalue('name')
site_url  = form.getvalue('url')

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>php中文网 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2>%s官网:%s</h2>" % (site_name, site_url))
print ("</body>")
print ("</html>")

다음은 POST 메서드(method="post)를 통해 서버 스크립트 hello_get.py에 데이터를 제출하는 폼입니다. "):

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
<form action="/cgi-bin/hello_get.py" method="post">
站点名称: <input type="text" name="name">  <br />

站点 URL: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>
</body>
</html>
</form>

Gif 데모는 다음과 같습니다:

hello_post.gif

CGI 프로그램을 통해 체크박스 데이터 전송

체크박스는 하나 이상의 옵션 데이터를 제출하는 데 사용되며 HTML 코드는 다음과 같습니다.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
<form action="/cgi-bin/checkbox.py" method="POST" target="_blank">
<input type="checkbox" name="php" value="on" /> php中文网
<input type="checkbox" name="google" value="on" /> Google
<input type="submit" value="选择站点" />
</form>
</body>
</html>

다음은 checkbox.py 파일의 코드:

#!/usr/bin/python3

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('google'):
   google_flag = "是"
else:
   google_flag = "否"

if form.getvalue('php'):
   php_flag = "是"
else:
   php_flag = "否"

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>php中文网 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> php中文网是否选择了 : %s</h2>" % php_flag)
print ("<h2> Google 是否选择了 : %s</h2>" % google_flag)
print ("</body>")
print ("</html>")

checkbox.py 권한 수정:

chmod 755 checkbox.py

브라우저 액세스 Gif 데모 이미지:

checkbox.gif

CGI 프로그램을 통해 라디오 데이터 전송

Radio는 하나의 데이터만 서버로 전송하며, HTML 코드는 다음과 같습니다:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
<form action="/cgi-bin/radiobutton.py" method="post" target="_blank">
<input type="radio" name="site" value="php" /> php中文网
<input type="radio" name="site" value="google" /> Google
<input type="submit" value="提交" />
</form>
</body>
</html>

radiobutton.py 스크립트 코드는 다음과 같습니다:

#!/usr/bin/python3

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('site'):
   site = form.getvalue('site')
else:
   site = "提交数据为空"

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>php中文网 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> 选中的网站是 %s</h2>" % site)
print ("</body>")
print ("</html>")

radiobutton.py 수정 권한:

chmod 755 radiobutton.py

브라우저 액세스 Gif 데모 사진:

radiobutton.gif

Transfer Textarea data through CGI program

Textarea는 여러 줄의 데이터를 서버로 전송합니다. HTML 코드는 다음과 같습니다.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
<form action="/cgi-bin/textarea.py" method="post" target="_blank">
<textarea name="textcontent" cols="40" rows="4">
在这里输入内容...
</textarea>
<input type="submit" value="提交" />
</form>
</body>
</html>

textarea.py 스크립트 코드는 다음과 같습니다.

#!/usr/bin/python3

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('textcontent'):
   text_content = form.getvalue('textcontent')
else:
   text_content = "没有内容"

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>php中文网 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> 输入的内容是:%s</h2>" % text_content)
print ("</body>")
print ("</html>")

textarea.py 수정 권한:

chmod 755 textarea.py

브라우저 액세스 Gif 데모:

textarea.gif

CGI 프로그램을 통해 드롭다운 데이터를 전송합니다.

HTML 드롭다운 상자 코드는 다음과 같습니다.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
<form action="/cgi-bin/dropdown.py" method="post" target="_blank">
<select name="dropdown">
<option value="php" selected>php中文网</option>
<option value="google">Google</option>
</select>
<input type="submit" value="提交"/>
</form>
</body>
</html>

dropdown.py 스크립트 코드는 다음과 같습니다.

#!/usr/bin/python3

# 引入 CGI 处理模块 
import cgi, cgitb 

# 创建 FieldStorage的实例 
form = cgi.FieldStorage() 

# 接收字段数据
if form.getvalue('dropdown'):
   dropdown_value = form.getvalue('dropdown')
else:
   dropdown_value = "没有内容"

print ("Content-type:text/html")
print ()
print ("<html>")
print ("<head>")
print ("<meta charset=\"utf-8\">")
print ("<title>php中文网 CGI 测试实例</title>")
print ("</head>")
print ("<body>")
print ("<h2> 选中的选项是:%s</h2>" % dropdown_value)
print ("</body>")
print ("</html>")

Modify dropdown.py 권한:

chmod 755 dropdown.py

브라우저 액세스 Gif 데모 이미지:

dropdown.gif


CGI에 사용되는 쿠키

http 프로토콜의 큰 단점은 사용자의 신원을 판단하지 않는다는 점이며, 이는 프로그래머에게 큰 불편을 안겨줍니다. 쿠키 기능의 등장으로 이러한 단점이 보완되었습니다.

쿠키는 고객이 스크립트에 접속할 때 고객의 브라우저를 통해 고객의 하드 드라이브에 기록 데이터를 쓰는 것입니다. , 신원 식별 기능을 달성하기 위해 다음 번에 고객이 스크립트에 액세스할 때 데이터 정보가 검색됩니다. 쿠키는 신원 확인에 자주 사용됩니다.

 

쿠키 구문

http 쿠키는 파일 전송보다 빠른 http 헤더를 통해 전송됩니다. 헤더의 set-cookie 구문은 다음과 같습니다.

Set-cookie:name=name;expires=date;path=path;domain=domain;secure
  • name=name: 쿠키에는 다음이 필요합니다. 값을 설정합니다(이름에는 ";" 및 ","을 사용할 수 없음). 이름 값이 여러 개 있는 경우 ";"을 사용하여 구분합니다(예: name1=name1;name2=name2;name3). =이름3.

  • expires=날짜: 쿠키 유효 기간, 형식: 만료="Wdy,DD-Mon-YYYY HH:MM:SS"


  • path=경로: 쿠키 설정 지원됨 path가 경로인 경우 쿠키는 이 디렉터리의 모든 파일과 하위 디렉터리에 적용됩니다(예: path="/cgi-bin/"). path가 파일인 경우 쿠키는 이 파일에 적용됩니다. 예: 경로="/cgi-bin/cookie.cgi".

  • domain=domain: 쿠키에 유효한 도메인 이름(예: domain="www.php.cn"

  • secure:) 이 플래그가 제공되면 쿠키는 SSL 프로토콜 서버의 https만 전달하여 전달할 수 있습니다.

  • 쿠키 수신은 환경 변수 HTTP_COOKIE를 설정하여 이루어집니다. CGI 프로그램은 이 변수를 검색하여 쿠키 정보를 얻을 수 있습니다.


쿠키 설정

쿠키 설정은 매우 간단합니다. 쿠키는 http 헤더에 별도로 전송됩니다. 다음 예에서는 쿠키에 이름을 설정하고 만료됩니다.

#!/usr/bin/python3
# 
print ('Content-Type: text/html')
print ('Set-Cookie: name="php中文网";expires=Wed, 28 Aug 2016 18:30:00 GMT')
print ()
print ("""
<html>
  <head>
    <meta charset="utf-8">
    <title>php中文网(php.cn)</title>
  </head>
    <body>
        <h1>Cookie set OK!</h1>
    </body>
</html>
""")

위 코드를 cookie_set.py에 저장하고 cookie_set.py 권한을 수정합니다.

chmod 755 cookie_set.py

위 예에서는 Set-Cookie 헤더 정보를 사용하여 쿠키 정보를 설정합니다. 이는 선택 사항입니다. 만료 시간 만료, 도메인 이름 및 경로와 같은 쿠키의 다른 속성이 설정됩니다. 이 정보는 "Content-type:text/html" 앞에 설정됩니다.


쿠키 정보 검색

쿠키 정보 검색 페이지는 매우 간단합니다. 쿠키 정보는 CGI 환경 변수 HTTP_COOKIE에 저장됩니다.

key1=value1;key2=value2;key3=value3....

다음은 쿠키 정보를 검색하는 간단한 CGI 프로그램입니다.

#!/usr/bin/python3

# 导入模块
import os
import Cookie

print ("Content-type: text/html")
print ()

print ("""
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
<h1>读取cookie信息</h1>
""")

if 'HTTP_COOKIE' in os.environ:
    cookie_string=os.environ.get('HTTP_COOKIE')
    c=Cookie.SimpleCookie()
    c.load(cookie_string)

    try:
        data=c['name'].value
        print ("cookie data: "+data+"<br>")
    except KeyError:
        print ("cookie 没有设置或者已过去<br>")
print ("""
</body>
</html>
""")

위 내용 바꾸기 cookie_get.py에 코드 저장하고 cookie_get.py 권한 수정:

chmod 755 cookie_get.py

위 쿠키 설정 색상 Gif는 다음과 같습니다.

cookie.gif

파일 업로드 예시

HTML 설정 파일 업로드 형식

enctype 속성을 multipart/form-data로 설정해야 하며 코드는 다음과 같습니다.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
 <form enctype="multipart/form-data" 
                     action="/cgi-bin/save_file.py" method="post">
   <p>选中文件: <input type="file" name="filename" /></p>
   <p><input type="submit" value="上传" /></p>
   </form>
</body>
</html>

save_file.py 스크립트 파일 코드는 다음과 같습니다.

#!/usr/bin/python3

import cgi, os
import cgitb; cgitb.enable()

form = cgi.FieldStorage()

# 获取文件名
fileitem = form['filename']

# 检测文件是否上传
if fileitem.filename:
   # 设置文件路径 
   fn = os.path.basename(fileitem.filename)
   open('/tmp/' + fn, 'wb').write(fileitem.file.read())

   message = '文件 "' + fn + '" 上传成功'
   
else:
   message = '文件没有上传'
   
print ("""\
Content-Type: text/html\n
<html>
<head>
<meta charset="utf-8">
<title>php中文网(php.cn)</title>
</head>
<body>
   <p>%s</p>
</body>
</html>
""" % (message,))

위 코드를 save_file.py에 저장하고 save_file.py 권한 수정:

chmod 755 save_file.py

위 쿠키에 대한 색상 Gif를 아래와 같이 설정합니다.

savefile.gif

사용 중인 시스템이 Unix/Linux인 경우 창 아래의 파일 구분 기호만 교체해야 합니다. open() 문을 사용하려면:

fn = os.path.basename(fileitem.filename.replace("\", "/" ))


파일 다운로드 대화 상자

us 먼저 프로그램 다운로드를 위해 현재 디렉터리에 foo.txt 파일을 만듭니다.

파일 다운로드는 HTTP 헤더 정보를 설정하여 이루어집니다. 함수 코드는 다음과 같습니다.

#!/usr/bin/python3

# HTTP 头部
print ("Content-Disposition: attachment; filename=\"foo.txt\"")
print ()
# 打开文件
fo = open("foo.txt", "rb")

str = fo.read();
print (str)

# 关闭文件
fo.close()