Python - CGI Programming

Giao diện Cổng Chung, hay CGI, là một tập hợp các tiêu chuẩn xác định cách thức thông tin được trao đổi giữa máy chủ web và một kịch bản tùy chỉnh. Các thông số kỹ thuật CGI hiện đang được NCSA duy trì.

What is CGI?

  • Giao diện Cổng Chung, hay CGI, là một tiêu chuẩn cho các chương trình cổng bên ngoài để giao tiếp với các máy chủ thông tin như máy chủ HTTP.

  • Phiên bản hiện tại là CGI/1.1 và CGI/1.2 đang trong quá trình phát triển.

Web Browsing

Để hiểu khái niệm về CGI, hãy xem điều gì xảy ra khi chúng ta nhấp vào một liên kết siêu văn bản để truy cập một trang web hoặc URL cụ thể.

  • Trình duyệt của bạn liên hệ với máy chủ web HTTP và yêu cầu URL, tức là tên tệp.

  • Máy chủ Web phân tích URL và tìm kiếm tên tệp. Nếu nó tìm thấy tệp đó, nó sẽ gửi lại cho trình duyệt, nếu không, nó sẽ gửi một thông báo lỗi cho biết rằng bạn đã yêu cầu một tệp sai.

  • Trình duyệt web nhận phản hồi từ máy chủ web và hiển thị hoặc tệp đã nhận hoặc thông báo lỗi.

Tuy nhiên, có thể thiết lập máy chủ HTTP sao cho mỗi khi một tệp trong một thư mục nhất định được yêu cầu, tệp đó sẽ không được gửi lại; thay vào đó, nó sẽ được thực thi như một chương trình, và bất kỳ đầu ra nào của chương trình đó sẽ được gửi lại để trình duyệt của bạn hiển thị. Chức năng này được gọi là Giao diện Cổng Chung hoặc CGI và các chương trình này được gọi là kịch bản CGI. Các chương trình CGI này có thể là một kịch bản Python, kịch bản PERL, kịch bản Shell, chương trình C hoặc C++, v.v.

CGI Architecture Diagram

Cgi Architecture Diagram

Web Server Support and Configuration

Trước khi bạn tiến hành lập trình CGI, hãy đảm bảo rằng Máy chủ Web của bạn hỗ trợ CGI và nó được cấu hình để xử lý các chương trình CGI. Tất cả các chương trình CGI sẽ được thực thi bởi máy chủ HTTP đều được lưu trong một thư mục đã được cấu hình trước. Thư mục này được gọi là Thư mục CGI và theo quy ước, nó được đặt tên là /var/www/cgi-bin. Theo quy ước, các tệp CGI có phần mở rộng là cgi, nhưng bạn cũng có thể giữ các tệp của mình với phần mở rộng python .py .

Theo mặc định, máy chủ Linux được cấu hình để chỉ chạy các kịch bản trong thư mục cgi-bin trong /var/www. Nếu bạn muốn chỉ định bất kỳ thư mục nào khác để chạy các kịch bản CGI của mình, hãy chú thích các dòng sau trong tệp httpd.conf −

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

<Directory "/var/www/cgi-bin">
Options All
</Directory>

Dòng sau cũng nên được thêm vào để máy chủ apache xử lý tệp .py như một kịch bản cgi.

AddHandler cgi-script .py

Ở đây, chúng tôi giả định rằng bạn đã có Máy chủ Web hoạt động thành công và bạn có thể chạy bất kỳ chương trình CGI nào khác như Perl hoặc Shell, v.v.

First CGI Program

Dưới đây là một liên kết đơn giản, liên kết đến một script CGI có tên là hello.py . Tệp này được giữ trong thư mục /var/www/cgi-bin và có nội dung như sau. Trước khi chạy chương trình CGI của bạn, hãy đảm bảo rằng bạn đã thay đổi chế độ của tệp bằng cách sử dụng lệnh chmod 755 hello.py trên UNIX để làm cho tệp có thể thực thi.

print ("Content-type:text/html\r\n\r\n")
print ('<html>')
print ('<head>')
print ('<title>Hello Word - First CGI Program</title>')
print ('</head>')
print ('<body>')
print ('<h2>Hello Word! This is my first CGI program</h2>')
print ('</body>')
print ('</html>')

Note − Dòng đầu tiên trong kịch bản phải là đường dẫn đến chương trình thực thi Python. Nó xuất hiện như một chú thích trong chương trình Python, nhưng được gọi là dòng shebang.

Trong Linux, nó nên là #!/usr/bin/python3.

Trong Windows, nó nên là #!c:/python311/python.exd.

Nhập URL sau vào trình duyệt của bạn −

http://localhost/cgi-bin/hello.py

Hello Word! This is my first CGI program

Đoạn mã hello.py này là một script Python đơn giản, viết đầu ra của nó vào file STDOUT, tức là màn hình. Có một tính năng quan trọng và bổ sung là dòng đầu tiên được in ra Content-type:text/html\r\n\r\n . Dòng này được gửi trở lại trình duyệt và nó chỉ định loại nội dung sẽ được hiển thị trên màn hình trình duyệt.

Đến bây giờ, bạn chắc hẳn đã hiểu khái niệm cơ bản về CGI và bạn có thể viết nhiều chương trình CGI phức tạp bằng Python. Kịch bản này cũng có thể tương tác với bất kỳ hệ thống bên ngoài nào khác để trao đổi thông tin như RDBMS.

HTTP Header

Dòng Content-type:text/html\r\n\r\n là một phần của tiêu đề HTTP được gửi đến trình duyệt để hiểu nội dung. Tất cả các tiêu đề HTTP sẽ có dạng sau −

HTTP Field Name: Field Content

For Example
Content-type: text/html\r\n\r\n

Có một vài tiêu đề HTTP quan trọng khác mà bạn sẽ thường xuyên sử dụng trong lập trình CGI của mình.

Sr.No. Header & Description
1 Content-type: A MIME string defining the format of the file being returned. Example is Content-type:text/html
2 Expires: Date The date the information becomes invalid. It is used by the browser to decide when a page needs to be refreshed. A valid date string is in the format 01 Jan 1998 12:00:00 GMT.
3 Location: URL The URL that is returned instead of the URL requested. You can use this field to redirect a request to any file.
4 Last-modified: Date The date of last modification of the resource.
5 Content-length: N The length, in bytes, of the data being returned. The browser uses this value to report the estimated download time for a file.
6 Set-Cookie: String Set the cookie passed through the string

CGI Environment Variables

Tất cả các chương trình CGI đều có quyền truy cập vào các biến môi trường sau. Những biến này đóng vai trò quan trọng khi viết bất kỳ chương trình CGI nào.

Sr.No. Variable Name & Description
1 CONTENT_TYPE The data type of the content. Used when the client is sending attached content to the server. For example, file upload.
2 CONTENT_LENGTH The length of the query information. It is available only for POST requests.
3 HTTP_COOKIE Returns the set cookies in the form of key & value pair.
4 HTTP_USER_AGENT The User-Agent request-header field contains information about the user agent originating the request. It is name of the web browser.
5 PATH_INFO The path for the CGI script.
6 QUERY_STRING The URL-encoded information that is sent with GET method request.
7 REMOTE_ADDR The IP address of the remote host making the request. This is useful logging or for authentication.
8 REMOTE_HOST The fully qualified name of the host making the request. If this information is not available, then REMOTE_ADDR can be used to get IR address.
9 REQUEST_METHOD The method used to make the request. The most common methods are GET and POST.
10 SCRIPT_FILENAME The full path to the CGI script.
11 SCRIPT_NAME The name of the CGI script.
12 SERVER_NAME The server's hostname or IP Address
13 SERVER_SOFTWARE The name and version of the software the server is running.

Dưới đây là một chương trình CGI nhỏ để liệt kê tất cả các biến CGI. Nhấp vào liên kết này để xem kết quả Get Environment

import os

print ("Content-type: text/html\r\n\r\n");
print ("<font size=+1>Environment</font><\br>");
for param in os.environ.keys():
   print ("<b>%20s</b>: %s<\br>" % (param, os.environ[param]))

GET and POST Methods

Bạn chắc hẳn đã gặp nhiều tình huống khi cần truyền một số thông tin từ trình duyệt của bạn đến máy chủ web và cuối cùng đến chương trình CGI của bạn. Thông thường, trình duyệt sử dụng hai phương pháp để truyền thông tin này đến máy chủ web. Hai phương pháp này là Phương pháp GET và Phương pháp POST.

Passing Information using GET method

Phương thức GET gửi thông tin người dùng đã được mã hóa đính kèm vào yêu cầu trang. Trang và thông tin đã được mã hóa được tách biệt bởi ký tự ? như sau −

http://www.test.com/cgi-bin/hello.py?key1=value1&key2=value2
  • Phương thức GET là phương thức mặc định để truyền thông tin từ trình duyệt đến máy chủ web và nó tạo ra một chuỗi dài xuất hiện trong hộp Location: của trình duyệt của bạn.

  • Không bao giờ sử dụng phương thức GET nếu bạn có mật khẩu hoặc thông tin nhạy cảm khác để gửi đến máy chủ.

  • Phương thức GET có giới hạn kích thước: chỉ có 1024 ký tự có thể được gửi trong chuỗi yêu cầu.

  • Phương thức GET gửi thông tin bằng cách sử dụng tiêu đề QUERY_STRING và sẽ có thể truy cập trong chương trình CGI của bạn thông qua biến môi trường QUERY_STRING.

Bạn có thể truyền thông tin bằng cách đơn giản nối các cặp khóa và giá trị cùng với bất kỳ URL nào hoặc bạn có thể sử dụng thẻ HTML <FORM> để truyền thông tin bằng cách sử dụng phương thức GET.

Simple URL Example:Get Method

Đây là một URL đơn giản, truyền hai giá trị đến chương trình hello_get.py bằng phương thức GET.

/cgi-bin/hello_get.py?first_name=Malhar&last_name=Lathkar

Dưới đây là kịch bản hello_get.py để xử lý đầu vào được cung cấp bởi trình duyệt web. Chúng ta sẽ sử dụng mô-đun cgi, điều này giúp rất dễ dàng để truy cập thông tin đã được truyền đi.

# Import modules for CGI handling
import cgi, cgitb

# Create instance of FieldStorage
form = cgi.FieldStorage()

# Get data from fields
first_name = form.getvalue('first_name')
last_name = form.getvalue('last_name')

print ("Content-type:text/html")
print()
print ("<html>")
print ('<head>')
print ("<title>Hello - Second CGI Program</title>")
print ('</head>')
print ('<body>')
print ("<h2>Hello %s %s</h2>" % (first_name, last_name))
print ('</body>')
print ('</html>')

Điều này sẽ tạo ra kết quả sau −

Hello Malhar Lathkar

Simple FORM Example:GET Method

Ví dụ này truyền hai giá trị bằng cách sử dụng FORM HTML và nút gửi. Chúng tôi sử dụng cùng một script CGI hello_get.py để xử lý đầu vào này.

<form action = "/cgi-bin/hello_get.py" method = "get">
   First Name: <input type = "text" name = "first_name">  <br />

   Last Name: <input type = "text" name = "last_name" />
   <input type = "submit" value = "Submit" />
</form>

Dưới đây là kết quả thực tế của biểu mẫu trên, bạn nhập Họ và Tên và sau đó nhấp vào nút gửi để xem kết quả.

First Name:
Last Name:

Passing Information Using POST Method

Một phương pháp đáng tin cậy hơn để truyền thông tin đến một chương trình CGI là phương pháp POST. Phương pháp này đóng gói thông tin theo cách giống như phương pháp GET, nhưng thay vì gửi nó dưới dạng một chuỗi văn bản sau dấu ? trong URL, nó gửi nó dưới dạng một thông điệp riêng biệt. Thông điệp này được chuyển vào tập lệnh CGI dưới dạng đầu vào chuẩn.

Dưới đây là cùng một kịch bản hello_get.py xử lý cả phương thức GET và POST.

# Import modules for CGI handling 
import cgi, cgitb 

# Create instance of FieldStorage 
form = cgi.FieldStorage() 

# Get data from fields
first_name = form.getvalue('first_name')
last_name  = form.getvalue('last_name')

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Hello - Second CGI Program</title>"
print "</head>"
print "<body>"
print "<h2>Hello %s %s</h2>" % (first_name, last_name)
print "</body>"
print "</html>"

Hãy lấy lại ví dụ giống như trên, trong đó truyền hai giá trị bằng cách sử dụng FORM HTML và nút gửi. Chúng tôi sử dụng cùng một script CGI hello_get.py để xử lý đầu vào này.

<form action = "/cgi-bin/hello_get.py" method = "post">
First Name: <input type = "text" name = "first_name"><br />
Last Name: <input type = "text" name = "last_name" />

<input type = "submit" value = "Submit" />
</form>

Dưới đây là đầu ra thực tế của biểu mẫu trên. Bạn nhập Họ và Tên và sau đó nhấn nút gửi để xem kết quả.

First Name:
Last Name:

Passing Checkbox Data to CGI Program

Checkboxes được sử dụng khi cần chọn nhiều tùy chọn.

Dưới đây là mã HTML ví dụ cho một biểu mẫu với hai ô kiểm tra −

<form action = "/cgi-bin/checkbox.cgi" method = "POST" target = "_blank">
   <input type = "checkbox" name = "maths" value = "on" /> Maths
   <input type = "checkbox" name = "physics" value = "on" /> Physics
   <input type = "submit" value = "Select Subject" />
</form>

Kết quả của đoạn mã này có dạng như sau −

Maths Physics

Dưới đây là đoạn mã script checkbox.cgi để xử lý đầu vào từ trình duyệt web cho nút checkbox.

# Import modules for CGI handling 
import cgi, cgitb 

# Create instance of FieldStorage 
form = cgi.FieldStorage() 

# Get data from fields
if form.getvalue('maths'):
   math_flag = "ON"
else:
   math_flag = "OFF"

if form.getvalue('physics'):
   physics_flag = "ON"
else:
   physics_flag = "OFF"

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Checkbox - Third CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> CheckBox Maths is : %s</h2>" % math_flag
print "<h2> CheckBox Physics is : %s</h2>" % physics_flag
print "</body>"
print "</html>"

Passing Radio Button Data to CGI Program

Nút radio được sử dụng khi chỉ cần chọn một tùy chọn.

Dưới đây là mã HTML ví dụ cho một biểu mẫu với hai nút radio −

<form action = "/cgi-bin/radiobutton.py" method = "post" target = "_blank">
   <input type = "radio" name = "subject" value = "maths" /> Maths
   <input type = "radio" name = "subject" value = "physics" /> Physics
   <input type = "submit" value = "Select Subject" />
</form>

Kết quả của đoạn mã này có dạng như sau −

Maths Physics

Dưới đây là đoạn mã radiobutton.py để xử lý đầu vào do trình duyệt web cung cấp cho nút radio −

# Import modules for CGI handling 
import cgi, cgitb 

# Create instance of FieldStorage 
form = cgi.FieldStorage() 

# Get data from fields
if form.getvalue('subject'):
   subject = form.getvalue('subject')
else:
   subject = "Not set"

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Radio - Fourth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Selected Subject is %s</h2>" % subject
print "</body>"
print "</html>"

Passing Text Area Data to CGI Program

Phần tử TEXTAREA được sử dụng khi cần truyền văn bản nhiều dòng đến chương trình CGI.

Dưới đây là mã HTML ví dụ cho một biểu mẫu với hộp TEXTAREA −

<form action = "/cgi-bin/textarea.py" method = "post" target = "_blank">
   <textarea name = "textcontent" cols = "40" rows = "4">
      Type your text here...
   </textarea>
   <input type = "submit" value = "Submit" />
</form>

Kết quả của đoạn mã này có dạng như sau −

Dưới đây là đoạn script textarea.cgi để xử lý đầu vào từ trình duyệt web −

# Import modules for CGI handling 
import cgi, cgitb 

# Create instance of FieldStorage 
form = cgi.FieldStorage() 

# Get data from fields
if form.getvalue('textcontent'):
   text_content = form.getvalue('textcontent')
else:
   text_content = "Not entered"

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>";
print "<title>Text Area - Fifth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Entered Text Content is %s</h2>" % text_content
print "</body>"

Passing Drop Down Box Data to CGI Program

Hộp thả xuống được sử dụng khi chúng ta có nhiều tùy chọn nhưng chỉ một hoặc hai tùy chọn sẽ được chọn.

Dưới đây là mã HTML ví dụ cho một biểu mẫu với một hộp thả xuống −

<form action = "/cgi-bin/dropdown.py" method = "post" target = "_blank">
   <select name = "dropdown">
      <option value = "Maths" selected>Maths</option>
      <option value = "Physics">Physics</option>
   </select>
   <input type = "submit" value = "Submit"/>
</form>

Kết quả của đoạn mã này có dạng như sau −

Dưới đây là đoạn mã dropdown.py để xử lý đầu vào từ trình duyệt web.

# Import modules for CGI handling 
import cgi, cgitb 

# Create instance of FieldStorage 
form = cgi.FieldStorage() 

# Get data from fields
if form.getvalue('dropdown'):
   subject = form.getvalue('dropdown')
else:
   subject = "Not entered"

print "Content-type:text/html\r\n\r\n"
print "<html>"
print "<head>"
print "<title>Dropdown Box - Sixth CGI Program</title>"
print "</head>"
print "<body>"
print "<h2> Selected Subject is %s</h2>" % subject
print "</body>"
print "</html>"

Using Cookies in CGI

Giao thức HTTP là một giao thức không trạng thái. Đối với một trang web thương mại, cần phải duy trì thông tin phiên làm việc giữa các trang khác nhau. Ví dụ, một quy trình đăng ký người dùng kết thúc sau khi hoàn thành nhiều trang. Làm thế nào để duy trì thông tin phiên của người dùng trên tất cả các trang web?

Trong nhiều tình huống, việc sử dụng cookie là phương pháp hiệu quả nhất để ghi nhớ và theo dõi sở thích, mua sắm, hoa hồng và các thông tin khác cần thiết cho trải nghiệm người dùng tốt hơn hoặc thống kê trang web.

How It Works?

Máy chủ của bạn gửi một số dữ liệu đến trình duyệt của người truy cập dưới dạng cookie. Trình duyệt có thể chấp nhận cookie. Nếu có, nó sẽ được lưu trữ dưới dạng bản ghi văn bản thuần trên ổ cứng của người truy cập. Bây giờ, khi người truy cập đến một trang khác trên trang web của bạn, cookie sẽ có sẵn để truy xuất. Khi được truy xuất, máy chủ của bạn sẽ biết/nhớ những gì đã được lưu trữ.

Cookies là một bản ghi dữ liệu văn bản thuần túy gồm 5 trường có độ dài biến đổi -

  • Expires − Ngày mà cookie sẽ hết hạn. Nếu trường này để trống, cookie sẽ hết hạn khi người dùng đóng trình duyệt.

  • Domain − Tên miền của trang web của bạn.

  • Path − Đường dẫn đến thư mục hoặc trang web thiết lập cookie. Điều này có thể để trống nếu bạn muốn lấy cookie từ bất kỳ thư mục hoặc trang nào.

  • Secure − Nếu trường này chứa từ "secure", thì cookie chỉ có thể được truy xuất bằng một máy chủ an toàn. Nếu trường này để trống, thì không có hạn chế nào như vậy.

  • Name = Value − Cookies được thiết lập và truy xuất dưới dạng cặp khóa và giá trị.

Setting up Cookies

Rất dễ dàng để gửi cookie đến trình duyệt. Những cookie này được gửi cùng với Header HTTP trước trường Content-type. Giả sử bạn muốn thiết lập UserID và Password dưới dạng cookie. Việc thiết lập cookie được thực hiện như sau −

print "Set-Cookie:UserID = XYZ;\r\n"
print "Set-Cookie:Password = XYZ123;\r\n"
print "Set-Cookie:Expires = Tuesday, 31-Dec-2007 23:12:40 GMT;\r\n"
print "Set-Cookie:Domain = www.tutorialspoint.com;\r\n"
print "Set-Cookie:Path = /perl;\n"
print "Content-type:text/html\r\n\r\n"
...........Rest of the HTML Content....

Từ ví dụ này, bạn chắc hẳn đã hiểu cách thiết lập cookie. Chúng ta sử dụng Set-Cookie tiêu đề HTTP để thiết lập cookie.

Việc thiết lập các thuộc tính cookie như Expires, Domain và Path là tùy chọn. Cần lưu ý rằng cookie được thiết lập trước khi gửi dòng ma thuật "Content-type:text/html\r\n\r\n .

Retrieving Cookies

Rất dễ dàng để lấy tất cả các cookie đã được thiết lập. Cookies được lưu trữ trong biến môi trường CGI HTTP_COOKIE và chúng sẽ có dạng sau −

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

Dưới đây là một ví dụ về cách lấy cookie.

# Import modules for CGI handling 
from os import environ
import cgi, cgitb

if environ.has_key('HTTP_COOKIE'):
   for cookie in map(strip, split(environ['HTTP_COOKIE'], ';')):
      (key, value ) = split(cookie, '=');
      if key == "UserID":
         user_id = value

      if key == "Password":
         password = value

print "User ID  = %s" % user_id
print "Password = %s" % password

Điều này tạo ra kết quả sau cho các cookie được thiết lập bởi đoạn mã trên −

User ID = XYZ
Password = XYZ123

File Upload Example

Để tải lên một tệp, biểu mẫu HTML phải có thuộc tính enctype được đặt thành multipart/form-data . Thẻ input với kiểu file tạo ra một nút "Duyệt".

<html>
   <body>
      <form enctype = "multipart/form-data" action = "save_file.py" method = "post">
      <p>File: <input type = "file" name = "filename" /></p>
      <p><input type = "submit" value = "Upload" /></p>
      </form>
   </body>
</html>

Kết quả của đoạn mã này có dạng như sau −

Tệp:

Ví dụ trên đã bị vô hiệu hóa có chủ ý để tiết kiệm việc mọi người tải lên tệp trên máy chủ của chúng tôi, nhưng bạn có thể thử đoạn mã trên với máy chủ của bạn.

Dưới đây là kịch bản save_file.py để xử lý việc tải lên tệp −

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

form = cgi.FieldStorage()

# Get filename here.
fileitem = form['filename']

# Test if the file was uploaded
if fileitem.filename:
   # strip leading path from file name to avoid 
   # directory traversal attacks
   fn = os.path.basename(fileitem.filename)
   open('/tmp/' + fn, 'wb').write(fileitem.file.read())

   message = 'The file "' + fn + '" was uploaded successfully'
   
else:
   message = 'No file was uploaded'
   
print """\
Content-Type: text/html\n
<html>
   <body>
      <p>%s</p>
   </body>
</html>
""" % (message,)

Nếu bạn chạy đoạn kịch bản trên hệ điều hành Unix/Linux, thì bạn cần chú ý thay thế ký tự phân tách tệp như sau, nếu không trên máy Windows của bạn, câu lệnh open() ở trên sẽ hoạt động tốt.

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

How To Raise a "File Download" Dialog Box?

Đôi khi, bạn muốn cung cấp tùy chọn để người dùng có thể nhấp vào một liên kết và nó sẽ hiển thị một hộp thoại "Tải xuống File" cho người dùng thay vì hiển thị nội dung thực tế. Điều này rất đơn giản và có thể đạt được thông qua tiêu đề HTTP. Tiêu đề HTTP này sẽ khác với tiêu đề được đề cập trong phần trước.

Ví dụ, nếu bạn muốn tạo một tệp FileName có thể tải xuống từ một liên kết nhất định, thì cú pháp của nó như sau −

# HTTP Header
print "Content-Type:application/octet-stream; name = \"FileName\"\r\n";
print "Content-Disposition: attachment; filename = \"FileName\"\r\n\n";

# Actual File Content will go here.
fo = open("foo.txt", "rb")

str = fo.read();
print str

# Close opend file
fo.close()