Python cho người Việt

Python cho người Việt

Entries tagged “web”

Lập trình web với Python (7)

written by Nguyễn Thành Nam, on Aug 11, 2011 3:00:00 PM.

Trong bài cuối của loạt bài Lập trình web với Python, chúng ta sẽ bàn đến chuẩn WSGI (Web Server Gateway Interface).

WSGI, khác với HTTP, CGI và FCGI, không phải là chuẩn giao thức liên lạc (communication protocol) mà là chuẩn giao tiếp (standard interface) giữa ứng dụng máy chủ (server) và các khung xương (framework) hay các ứng dụng web (web application). Hình tượng hóa mà nói, WSGI nằm phía trên HTTP/CGI/FCGI và phía dưới ứng dụng thật sự. Lớp WSGI giúp lớp ứng dụng trao đổi với lớp máy chủ theo một cách khả chuyển, tức là một ứng dụng WSGI có thể chạy như nhau trên máy chủ khác nhau như Apache, NGINX, hay Lighttpd, sử dụng các giao thức khác nhau như CGI, FCGI, SCGI, hay AJP. Nói một cách khác, WSGI "che" cách liên lạc qua mạng và tạo điều kiện cho ứng dụng web tập trung vào việc xử lý các vấn đề quan trọng hơn.

Một ứng dụng WSGI là một đối tượng gọi được (callable object). Một đối tượng gọi được có thể là một hàm, một phương thức, hoặc một đối tượng có hàm call. Đối tượng gọi được này phải nhận hai tham số là environstart_response. Tham số environ là một từ điển với các khóa theo chuẩn CGI và một số khóa đặc biệt mà máy chủ WSGI có thể truyền cho ứng dụng. start_response là một đối tượng gọi được do máy chủ WSGI cung cấp cho ứng dụng để ứng dụng bắt đầu việc truyền dữ liệu cho máy chủ WSGI. start_response nhận hai tham số là dòng trạng thái trả lời (status string) và một danh sách bộ-2 (list of 2-tuple) các đầu mục (header), mỗi bộ-2 bao gồm tên và giá trị của đầu mục. Giá trị trả về của ứng dụng WSGI là một bộ khả lặp (iterable) sinh ra nội dung sẽ được máy chủ WSGI truyền lại cho máy chủ HTTP hoặc trình duyệt.

Ví dụ:

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']

Đầu tiên, ta thiết lập chuỗi trạng thái là 200 OK, xác định đầu mục Content-typetext/plain rồi gọi start_response với các thông tin như vậy. Giá trị trả về là một danh sách với phần tử duy nhất là chuỗi Hello world!\n. Ta cũng có thể trả về chuỗi Hello world! trực tiếp mà không cần đặt nó vào trong một danh sách vì bản thân một chuỗi cũng là một đối tượng khả lặp. Tuy nhiên, làm như vậy không được khuyến khích vì khi đó máy chủ WSGI sẽ phải làm việc nhiều hơn, lặp qua từng ký tự H, e, l, l, o... thay vì lấy thẳng chuỗi trả lời.

Chúng ta sẽ viết lại ứng dụng đếm số lần truy cập như trong kỳ trước theo dạng một ứng dụng WSGI. Chúng ta sẽ tạo tập tin C:\Program Files\Apache Software Foundation\Apache2.2\fcgi-bin\hello2.py với nội dung như sau:

#!C:\Python26\python.exe
from flup.server import fcgi

class HelloApp(object):

	def __init__(self):
		self.count = 0

	def __call__(self, environ, start_response):
		self.count += 1
		start_response('200 OK', [('Content-type', 'text/plain')])
		return ['Hello WSGI World %d' % self.count]

if __name__ == "__main__":
	webapp = HelloApp()
	fcgi.WSGIServer(webapp, bindAddress=("localhost", 8888)).run()

Thực thi ứng dụng này với lệnh python hello2.py, chạy máy chủ Apache với các thiết lập đã làm trong bài viết kỳ trước, và truy cập vào địa chỉ http://localhost/fcgi-bin/hello.py thì chúng ta sẽ thấy hình như sau:

wsgi1.png

Khi làm tươi trình duyệt thì chúng ta nhận được hình sau:

wsgi2.png

So sánh ứng dụng viết theo WSGI và ứng dụng viết theo các giao thức CGI, hay FCGI ta thấy rõ rằng ứng dụng WSGI không cần quan tâm đến việc dữ liệu sẽ được truyền cho trình duyệt bằng cách nào. Ứng dụng WSGI chỉ quan tâm đến việc tạo ra dữ liệu gì và đẩy chỗ dữ liệu đó cho lớp WSGI bên dưới. Lớp này sẽ tự động thực hiện việc truyền tới trình duyệt theo cách tốt nhất có thể.

Tuy nhiên, ứng dụng WSGI cũng phải biết rõ cách hoạt động của máy chủ WSGI. Ví dụ, một ứng dụng WSGI chạy trên máy chủ WSGI theo mô hình CGI thì sẽ không thể trông chờ đến việc sử dụng lại biến toàn cục vì mỗi yêu cầu được một tiến trình riêng xử lý. Đồng thời ứng dụng WSGI cũng phải đảm bảo rằng các chuỗi trả về phải là chuỗi byte (byte string) và không được sử dụng chuỗi unicode (unicode string). Lý do là vì giao thức HTTP không hiểu unicode. Do đó, tốt nhất là ứng dụng WSGI nên gọi encode trên các chuỗi unicode để chuyển các chuỗi unicode thành các chuỗi byte trước khi đưa xuống cho máy chủ WSGI.

Một điểm hay của giao tiếp WSGI là một ứng dụng WSGI có thể gói (wrap) một ứng dụng WSGI khác bên trong. Điều này cho phép chúng ta tạo ra các ứng dụng WSGI hoạt động như các phần giữa (middleware), hoặc bộ lọc (filter).

Ví dụ:

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']

def real_app(environ, start_response):
    r = simple_app(environ, start_response)
    return ['Tag!\n'] + r

Với đoạn mã trên, ứng dụng real_app đã gói ứng dụng simple_app và chèn vào một chuỗi Tag!\n phía trước những gì mà simple_app gửi về. Đây là một cách để tạo nên các ứng dụng web lớn từ việc ghép các ứng dụng web nhỏ lại với nhau.

Chúng ta dừng loạt bài Lập trình web với Python tại đây. Sau 7 bài viết ngắn gọn (nhưng diễn ra trong một khoảng thời gian dài), chúng ta đã xem xét qua việc cài đặt Apache, và Python, rồi các giao thức nền tảng như HTTP, CGI. Từ đó, chúng ta bàn đến các giao thức hiện đại hơn, có một số ưu điểm tốt như FCGI với ví dụ đếm số lần truy cập. Cuối cùng chúng ta dừng lại với một thảo luận ngắn về giao tiếp WSGI, là giao tiếp phổ thông nhất để viết ứng dụng web trong thế giới Python.

Tôi hy vọng sẽ gặp bạn đọc trong các bài viết khác. Để thảo luận về loạt bài này, bạn có thể sử dụng diễn đàn http://vithon.org/forum/Forum/show/7/Ung_dung_mang_web.html.

Lập trình web với Python (6)

written by Nguyễn Thành Nam, on Jul 1, 2011 5:05:00 PM.

Phần trước chúng ta đã xem xét mô hình hoạt động của một ứng dụng FCGI. Trong phần này chúng ta sẽ viết thử một chương trình FCGI như đã đề cập.

Trước tiên, chúng ta sẽ cần cài đặt thư viện flup của tác giả Allan Saddi. Làm việc này khá đơn giản, chúng ta chỉ việc tải tập tin ez_setup.py từ địa chỉ http://peak.telecommunity.com/dist/ez_setup.py về một nơi nào đó trên máy, ví dụ như C:\Python26\Scripts. Sau đó chúng ta sẽ gọi lệnh ở màn hình DOS như sau:

C:\>c:\Python26\scripts\ez_setup.py flup

Gói flup sẽ được cài đặt trong C:\Python26\lib\site-packages.

Ở bước 2, chúng ta sẽ tải về tập tin http://www.fastcgi.com/dist/mod_fastcgi-2.4.6-AP22.dll, đổi tên nó thành mod_fastcgi.dll và đặt nó trong C:\Program Files\Apache Software Foundation\Apache2.2\modules.

Kế tiếp bước 3, chúng ta sẽ cần cấu hình Apache thành một FCGI client. Chúng ta cần sửa tập tin httpd.conf để thêm vào các dòng sau ở dưới cùng:

LoadModule fastcgi_module modules/mod_fastcgi.dll
Alias "/fcgi-bin/hello.py" "C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/hello.py"
<Directory "C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/">
    Order allow,deny
    Allow from all
</Directory>
FastCgiExternalServer "C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/hello.py" -host localhost:8888

Dòng LoadModule bảo Apache nạp mô-đun mod_fastcgi mà chúng ta vừa làm ở bước 2. Dòng Alias cho Apache biết rằng mọi yêu cầu đến đường dẫn (URI) /fcgi-bin/hello.py thật chất là đến tập tin C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/hello.py. Chúng ta cần dòng này bởi vì hello.py sẽ được tạo ở ngoài DocumentRoot. Cũng chính vì thư mục fcgi-bin không nằm trong DocumentRoot nên chúng ta cũng cần lệnh Directory để cho phép Apache phục những yêu cầu truy cập vào thư mục đó. Và cuối cùng lệnh FastCgiExternalServer bảo Apache rằng tập tin hello.py thật chất là một ứng dụng FCGI đã được chạy sẵn tại địa chỉ localhost ở cổng 8888. Chính câu lệnh này sẽ chuyển Apache thành một FCGI client, chuyển tiếp yêu cầu từ trình duyệt đến FCGI server do chúng ta phát triển. Vì chúng ta đang cài đặt trên Windows nên chúng ta chỉ có một lựa chọn là sử dụng FCGI theo chế độ trao đổi qua socket, do đó chúng ta phải dùng FastCgiExternalServer.

Bước 4 là tạo tập tin hello.py trong thư mục C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin. Nếu chưa có thư mục này, bạn đọc hãy tự tạo ra nó. Tập tin hello.py sẽ có nội dung như sau:

#!C:\Python26\python.exe
from flup.server import fcgi

count = 0

class HelloApp(fcgi.WSGIServer):

	def handler(self, req):
		global count
		count += 1
		req.stdout.write('Content-Type: text/plain\n')
		req.stdout.write('\n')
		req.stdout.write('Hello World %d' % count)

if __name__ == "__main__":
	HelloApp(application=None, bindAddress=("localhost", 8888)).run()

Trước hết, chúng ta nhập mô-đun flup.server.fcgi. Kế tiếp chúng ta khai báo một biến đếm tên count với giá trị khởi tạo là 0. Sau đó chúng ta khai báo ứng dụng của chúng ta bằng cách định nghĩa lớp HelloApp kế thừa lớp WSGIServer trong mô-đun flup.server.fcgi. HelloApp sẽ định nghĩa lại hàm handler để xử lý các yêu cầu nhận được.

Hàm handler nhận vào một tham số đó là đối tượng req chứa thông tin về các biến môi trường, các dòng dữ liệu chuẩn như stdin, stdout. Chúng ta sẽ cộng một vào biến đếm toàn cục, và xuất giá trị này ra lại cho trình duyệt. Như một ứng dụng CGI (FCGI) thông thường, trước hết chúng ta cần phải xác định kiểu dữ liệu trả về thông qua Content-Type, theo sau bởi một dòng trống đánh dấu sự chấm dứt các đầu đề (header) và bắt đầu của nội dung, và cuối cùng là nội dung.

Hai dòng lệnh cuối chỉ đơn giản là chạy ứng dụng FCGI này ở địa chỉ localhost và cổng 8888.

Để kiểm tra, chúng ta sẽ cần thực hiện ba việc: chạy ứng dụng hello.py, chạy Apache, và dùng trình duyệt xem trang http://localhost/fcgi-bin/hello.py**. Để chạy ứng dụng hello.py chúng ta chỉ cần nhập lệnh sau ở màn hình DOS:

c:\Program Files\Apache Software Foundation\Apache2.2\fcgi-bin>python hello.py

Khi dùng trình duyệt vào xem trang web như trên thì chúng ta sẽ nhận được hình sau:

hello1.png

Nếu làm tươi trình duyệt thì chúng ta sẽ thấy giá trị đếm tăng lên:

hello2.png

Như vậy chứng tỏ rằng các yêu cầu đều được phục vụ bởi một tiến trình duy nhất. Đây chính là lợi điểm của FCGI so với CGI.

Trong bài sau, chúng ta sẽ xem xét cách phát triển ứng dụng web với WSGI.

Lập trình web với Python (5)

written by Nguyễn Thành Nam, on Mar 24, 2011 5:39:00 PM.

Bẵng một thời gian, chúng ta lại quay lại với loạt bài Lập trình web với Python. Trong kỳ này, chúng ta sẽ bàn đến mô hình FastCGI (FCGI).

Cái tên FCGI khiến người ta liên tưởng đến mô hình CGI như đã trình bày trong kỳ trước. Nhưng, thật tế là FCGI là một mô hình rất khác so với CGI.

Các ứng dụng FCGI là các ứng dụng bền, tức là một tiến trình FCGI có thể phục vụ nhiều yêu cầu chứ không phải chỉ một yêu cầu như ứng dụng CGI. Do đó, các ứng dụng FCGI có thể sử dụng các biến có phạm vi ứng dụng (application-scoped variable, tương đương với các biến toàn cục, global variable), tồn tại qua nhiều yêu cầu.

Xét ví dụ một chương trình web hiển thị số lần truy cập vào trang.

  • Với mô hình CGI, khi có yêu cầu gửi đến, máy chủ web (web server, httpd) sẽ tạo một tiến trình khác, chạy chương trình CGI, chương trình này khởi tạo biến đếm với giá trị 0, tăng nó lên 1, và hiển thị số 1 rồi tiến trình kết thúc. Khi có yêu cầu thứ hai đến, máy chủ lại tạo một tiến trình khác, chạy chương trình CGI, chương trình này khởi tạo biến đếm với giá trị 0, tăng nó lên 1, và hiển thị số 1, và kết thúc tiến trình. Như vậy, vì mỗi yêu cầu được một tiến trình riêng phục vụ nên chúng ta sẽ cần phải lưu biến đếm vào một tài nguyên ngoài (ví dụ như tập tin, hoặc cơ sở dữ liệu), và các tiến trình truy cập vào cùng tài nguyên này.
  • Với mô hình FCGI, một tiến trình FCGI sẽ được chạy trước. Chương trình này được gọi là một FCGI server. Chương trình này khởi tạo biến đếm với giá trị 0 và bước vào một vòng lập vô tận để nhận yêu cầu. Trình duyệt gửi yêu cầu đến máy chủ web. Khi này máy chủ web sẽ chuyển yêu cầu này đến ứng dụng FCGI đã được chạy trước đó. Như vậy, máy chủ web đóng vai trò là một FCGI client. Ở phía FCGI server, yêu cầu được chấp nhận (accept), biến đếm được tăng lên thành 1, kết quả trả về lại cho httpd, và FCGI server quay lại vòng lập nhận yêu cầu. Máy chủ web trả lại kết quả cho trình duyệt. Khi có yêu cầu kế tiếp, máy chủ httpd lại chuyển yêu cầu đó cho cùng FCGI server. Yêu cầu này được chấp nhận, biến đếm được tăng thành 2 vì đang trong cùng một tiến trình, kết quả trả về lại cho httpd và tiếp tục như đã bàn.

Vì mô hình hoạt động của FCGI khác với CGI nên việc trao đổi giữa FCGI client và FCGI server (giữa httpd và ứng dụng FCGI) không thể sử dụng cùng một giao thức đơn giản như ứng dụng CGI nữa. Việc này đòi hỏi một giao thức riêng cho FCGI.

Mô hình hoạt động đơn giản của FCGI được miêu tả trong hình dưới.

/static/web-programming/fcgi/overview_lifecycle.png

Ngoài lợi ích về tốc độ (do không phải tạo tiến trình mới để phục vụ mỗi yêu cầu, và khả năng chia sẻ tài nguyên thông qua biến có phạm vi ứng dụng), các ứng dụng FCGI còn có thể được triển khai trên hệ thống khác với hệ thống máy chủ web, giúp cho việc phân bổ tài nguyên giữa httpd và FCGI server được hiệu quả hơn. FCGI hỗ trợ hai cách giao tiếp là thông qua stream pipe, và thông qua socket. Cách giao tiếp stream pipe tương tự như việc gửi và nhận dữ liệu qua stdin, và stdout. Cách giao tiếp này được sử dụng cho các ứng dụng FCGI triển khai trên cùng hệ thống với httpd. Cách giao tiếp socket sử dụng một kết nối TCP để đóng gói và gửi dữ liệu của env-vars, stdin, stdout, và stderr tới ứng dụng FCGI. Điều này cho phép ứng dụng FCGI được triển khai trên một hệ thống khác, và httpd sẽ liên lạc với ứng dụng này dựa vào kết nối mạng TCP. Do đó, việc tối ưu tài nguyên hệ thống có thể được thực hiện dễ dàng hơn so với ứng dụng CGI. Đối với ứng dụng FCGI, FCGI server có thể được chạy trong chế độ đa tiến trình, hoặc đa tiểu trình đều được. FCGI server có thể được chạy trên một máy 8 lõi, trong khi httpd chạy trên một máy khác đơn nhân. FCGI server phục vụ các dữ liệu động, trong khi httpd phục vụ các dữ liệu tĩnh như hình ảnh, trang web tĩnh.

Chúng ta vừa xem qua mô hình hoạt động của FCGI. Đi sâu hơn vào cấu trúc từng ứng dụng FCGI thì dúng ta có ba phần chính sau:

  • Mã khởi tạo
  • Vòng lặp nhận yêu cầu
  • Xử lý yêu cầu và trả kết quả

Trong ví dụ về ứng dụng hiển thị số lượt truy cập, mã khởi tạo là việc khởi tạo biến đếm toàn cụ với giá trị 0.

Vòng lặp nhận yêu cầu thường là một vòng lặp vô hạn, tương tự như:

while True:
    fcgi_accept()
    # xử lý yêu cầu như CGI

Trong khi yêu cầu được chấp nhận, thì dữ liệu nhập cũng được tách ra thành env-vars, stdin, stdout, và stderr. Điều này khiến cho việc xử lý yêu cầu của một ứng dụng FCGI không khác bao nhiêu so với việc xử lý của một ứng dụng CGI. Tất cả đều được thực hiện trên các biến môi trường, và các luồng chuẩn. Điều khác biệt đó là sau khi xử lý xong yêu cầu thì ứng dụng FCGI lại quay lại vòng lặp nhận yêu cầu mà không chấm dứt tiến trình như một ứng dụng CGI.

Vì không phải chấm dứt tiến trình sau mỗi yêu cầu nên các tiến trình FCGI có thể được “sử dụng lại”. Một ứng dụng FCGI có thể được khởi tạo trước, với 10 tiến trình chẳng hạn, và đặt trong một bể (pool) tiến trình sẵn sàng. Khi có yêu cầu đến, thì máy chủ web có thể ngay lập tức lấy ra một tiến trình từ bể này, và gửi thông tin đến nó. Khi kết thúc xử lý yêu cầu, máy chủ web lại đưa tiến trình này vào bể. Việc khởi tạo trước tiết kiệm được thời gian khởi tạo ban đầu, nhưng sẽ chiếm bộ nhớ, ngay cả khi chưa có yêu cầu nào được nhận.

Sau đây là mô hình chi tiết của một ứng dụng FCGI.

/static/web-programming/fcgi/detailed_lifecycle.png

Hẹn gặp các bạn vào bài tới. Chúng ta sẽ viết thử một ứng dụng FCGI đơn giản với mô-đun fcgi của Allan Saddi.

Lập trình web với Python (4)

written by Nguyễn Thành Nam, on Mar 10, 2010 12:46:00 PM.

Trong kỳ này, chúng ta sẽ xem xét một chương trình CGI có xử lý dữ liệu nhập.

Ở kỳ trước, chúng ta đã biết cấu trúc chung của một chương trình CGI. Chương trình CGI mà chúng ta dùng làm ví dụ không thật sự hữu dụng vì chúng chỉ xuất giá trị ngẫu nhiên.

Một chương trình CGI hữu dụng thường sẽ nhận dữ liệu nhập qua các mẫu đơn (form), xử lý các dữ liệu này, và trả về kết quả.

Các dữ liệu nhập được truyền đến ứng dụng web ở dạng bộ 2 (khóa, giá trị) (key-value pair). Ví dụ nếu chúng ta điền vào một mẫu đơn có trường nhập tên là name và giá trị của nó là Nguyễn Việt Nam thì khóa sẽ là name và giá trị là Nguyễn Việt Nam. Thông thường có hai phương thức truyền dữ liệu đến ứng dụng web là GET và POST.

Trong phương thức GET, các bộ 2 (khóa, giá trị) này sẽ được truyền qua URL. Với ví dụ ở trên, URL sẽ có dạng http://host/path?name=Nguyễn+Việt+Nam. Bạn đọc sẽ chú ý các điểm quan trọng sau:

  1. Đi ngay phía sau đường dẫn đến ứng dụng CGI là một dấu chấm hỏi (?). Ký tự này dùng để thông báo phần phía sau là dữ liệu nhập.

  2. Phân cách giữa khóa và giá trị của dữ liệu nhập là dấu bằng (=).

  3. Khóa và giá trị được chuyển mã theo dạng phù hợp. Chúng ta thấy rằng ký tự khoảng trắng được chuyển thành ký tự cộng (+). Việc chuyển mã này nhằm làm cho URL không chứa các ký tự đặc biệt có thể gây nhầm lẫn.

  4. Không được nêu rõ trong ví dụ là ký tự phân cách các bộ 2 (khóa, giá trị) là ký tự và (&).

Trong phương thức POST, các bộ 2 (khóa, giá trị) được truyền trong nội dung yêu cầu HTTP, và không hiện rõ cho người dùng.

Hãy thử nghiệm với chương trình ví dụ sau (đặt tên nó là fp.py):

#!c:/python26/python

print "Content-Type: text/html"
print

print """<html>
      <head>
              <title>CGI form processing</title>
      </head>
      <body>
              <fieldset>
                      <legend>Number guessing game</legend>
                      <form method="POST">
                              <label for="number">Enter a number</label>
                              <input type="text" name="number" value="" />
                              <input type="submit" />
                      </form>
              </fieldset>
      </body>
</html>
"""

Kết quả mà chúng ta nhận được là một mẫu đơn như hình:

/static/web-programming/cgi/form1.png

Một số điểm quan trọng trong chương trình này là sự sử dụng các thẻ liên quan đến mẫu đơn.

  1. Trước hết là thẻ form dùng để thông báo sự bắt đầu của một mẫu đơn. Các thuộc tính (attribute) thông thường của thẻ này gồm methodaction.

    1. Thuộc tính method xác định phương thức truyền dữ liệu. Chúng ta có thể sử dụng GET hoặc POST.

    2. Thuộc tính action xác định đường dẫn đến chương trình CGI sẽ xử lý mẫu đơn này. Nếu không xác định thì chính địa chỉ hiện tại sẽ được dùng.

  2. Thẻ input xác định một trường nhập. Thẻ này có các thuộc tính chính là type, name, và value.

    1. Thuộc tính type xác định kiểu nhập là một ô nhập (text box), một nút nhấn (button), một nút chọn (radio button), hoặc một tập tin (file). Ở ví dụ này chúng ta xác định một ô nhập với kiểu text.

    2. Thuộc tính name xác định khóa của bộ 2. Trong ví dụ này khóa nhập là number.

    3. Thuộc tính value xác định giá trị khởi đầu của trường nhập này. Chúng ta để trống.

  3. Thẻ input với thuộc tính typesubmit sẽ tạo một nút nhấn. Nút nhấn này đặc biệt vì nó sẽ gửi các giá trị chúng ta nhập vào mẫu đơn đến chương trình CGI.

Thông tin chi tiết về các thẻ HTML có thể được xem thêm từ các tài liệu từ W3C.

Chương trình này cũng chỉ là in ra một mẫu đơn nhưng chúng ta đã có thể nhấn nút để gửi mẫu đơn đó đi.

Chúng ta sẽ sửa nó để in lại những gì đã nhận từ trình duyệt.

#!c:/python26/python

import cgi

print "Content-Type: text/html"
print

print """<html>
      <head>
              <title>CGI form processing</title>
      </head>
      <body>
              <fieldset>
                      <legend>Number guessing game</legend>
                      <form method="POST">
                              <label for="number">Enter a number</label>
                              <input type="text" name="number" value="" />
                              <input type="submit" />
                      </form>
              </fieldset>
"""

form = cgi.FieldStorage()
if form.has_key('number'):
      print """You have entered: %s""" % form.getfirst('number')

print """</body>
</html>
"""

Điều đầu tiên chúng ta nhận ra là sự sử dụng mô-đun cgi. Mô-đun này cho phép chúng ta tạo một đối tượng FieldStorage. Đối tượng FieldStorage chứa các bộ 2 (khóa, giá trị) chúng ta nhận được từ trình duyệt trong một cấu trúc như kiểu từ điển. Do đó chúng ta có thể dùng phương thức has_key để kiểm tra sự tồn tại của khóa tương ứng. Để lấy giá trị từ FieldStorage ta có thể dùng form['number'].value hoặc gọi các hàm như getvalue, getfirst, hay getlist. Các hàm này được đề cập đến một cách chi tiết trong bộ tài liệu sử dụng Python.

Nếu tinh ý, chúng ta sẽ thấy rằng khi dữ liệu nhập chứa các thẻ HTML hợp lệ thì kết quả xuất ra sẽ hiển thị cả những thẻ HTML này. Ví dụ khi ta nhập Nguyễn <b>Việt</b> Nam.

/static/web-programming/cgi/form2.png

Điều này có thể là đúng theo ý định, hoặc cũng thể nằm ngoài mong muốn. Chúng ta gọi đây là các lỗi kịch bản chéo trang (Cross Site Scripting, XSS). Cách khắc phục là trước khi hiển thị các chuỗi không nằm trong kiểm soát của chương trình (ví dụ như dữ liệu nhập, dữ liệu xuất từ hệ thống khác, v.v...), chúng ta sẽ cần chuyển mã các ký tự đặc biệt. Mô-đun cgi cung cấp hàm escape để làm việc này. Mã nguồn mới sẽ gói getfirst trong cgi.escape như sau:

#!c:/python26/python

import cgi

print "Content-Type: text/html"
print

print """<html>
      <head>
              <title>CGI form processing</title>
      </head>
      <body>
              <fieldset>
                      <legend>Number guessing game</legend>
                      <form method="POST">
                              <label for="number">Enter a number</label>
                              <input type="text" name="number" value="" />
                              <input type="submit" />
                      </form>
              </fieldset>
"""

form = cgi.FieldStorage()
if form.has_key('number'):
      print """You have entered: %s""" % cgi.escape( \
                      form.getfirst('number'), True)

print """</body>
</html>
"""

Giờ đây, chuỗi được xuất ra sẽ giống hệt với chuỗi nhập.

/static/web-programming/cgi/form3.png

Chúng ta cũng có thể thử với phương thức GET bằng cách nhập thẳng URL http://localhost/cgi-bin/fp.py?number=Nguyễn+<b>Việt</b>+Nam. Chúng ta cũng sẽ nhận được kết quả tương tự.

Vậy là qua hai phần chúng ta đã tìm hiểu về cách hoạt động của một chương trình CGI. Điểm mạnh của giao thức CGI là tính đơn giản và an toàn cao vì mỗi yêu cầu được một tiến trình riêng xử lý. Cũng chính vì mô hình đơn giản như vậy nên CGI gặp phải nhiều cản trở trong việc phát triển. Cản trở đầu tiên là chương trình CGI phải tự xử lý trạng thái giữa các yêu cầu kế tiếp nhau, hay còn gọi là phiên làm việc (session). Cản trở thứ hai là chương trình CGI sẽ phải được thiết kế đặc biệt nếu muốn sử dụng các biến có phạm vi toàn ứng dụng (application scope). Cuối cùng, chương trình CGI chạy chậm hơn và tốn tài nguyên hơn vì mỗi yêu cầu phải được xử lý riêng.

Trong các phần tới, chúng ta sẽ xem qua cách hoạt động của những công nghệ tiên tiến, giải quyết được các điểm yếu của giao thức CGI.