ลองหัดเล่น CGI ด้วย Lua Python และ Ruby

สมัยก่อนเราใช้ Web Server จัดการเว็บปกติจะเขียนเว็บก็ต้องใช้ HTML กับ JavaScript เป็นหลักถ้าต้องการให้มีการประมวลผลบางอย่างเราจะใช้ CGI (Common Gateway Interface) ในการเรียกโปรแกรมภายนอกมาประมวลผลเช่นภาษา Perl เป็นต้น
ในปัจจุบันเรามีทางเลือกในการเขียนเว็บแอปพลิเคชั่นมากขึ้นไม่ว่าจะเป็น Perl, PHP, ASP, Java, NodeJS, Lua, Python, Ruby, Go, Rust, C, C++ ฯลฯ บางตัวก็ไม่ต้องอาศัย Web Server สามารถเขียนให้ทำงานเป็น Web Server ในตัวเองได้เลย และหลายภาษาก็มีตัวอำนวยความสะดวกในการจัดการกับ HTTP request ให้ใช้งานได้ง่ายๆ แต่วันนี้เราจะมาลองเขียน CGI แบบบ้านๆ โดยไม่ใช้มอดูล คลาส หรือฟังก์ชั่นสำเร็จที่มีมาให้ ด้วยสามภาษาสคริปต์ได้แก่ Lua, Python และ Ruby โดยใช้ Apache เป็น Web Server กันครับ

ตั้งค่า Apache

เริ่มด้วยการแก้ไขค่าใน httpd.conf  ให้ใส่ 

Options +ExecCGI
 ใน Directory ที่ต้องการและใส่

AddHandler cgi-script .lua .py .rb
 เพื่อให้ apache รู้จักไฟล์สคริปต์ของเรา

ตั้งค่าไฟล์สคริปต์

กำหนด permission ให้กับไฟล์สคริปต์เป็น executable และใส่ shebang ที่ส่วนหัวของไฟล์ตามภาษาที่ใช้

รูปแบบเป็น #!พาธเต็มของตัวแปลภาษานั้น [พารามิเตอร์]
โดยทั่วไปใน Linux หรือ Mac จะเป็น
#!/usr/bin/env พารามิเตอร์เป็นชื่อตัวแปรภาษานั้นๆ  เช่น #!/usr/bin/env python3
หรือในกรณีที่ระบุพาธไปเลย
#!/usr/bin/ตัวแปรภาษานั้นๆ    เช่น #!/usr/bin/python3

ถ้าใน Windows ก็แล้วแต่ว่าติดตั้งตัวแปลภาษาไว้ที่ไหนเช่น
#!D:\python\python.exe

ตัวอย่างโปรแกรม

การแสดงผลจะใช้คำสั่งพิมพ์ข้อความธรรมดาแต่กำหนดต้นข้อความเป็น "Content-type:text/html" และเว้นบรรทัดก่อนข้อความ HTML เช่น

#!/usr/bin/python3.7
import os
import sys
html_str1 = """Content-type:text/html; charset=utf-8

<html> 
<head></head> 
<body>
"""
html_str2 = """
<b> Search Your Query:</b><br> 
<FORM action="pycgi.py" method = "GET"> 
<input type="text" name="q" size="20" maxlength="120"> 
<input type="submit" value="Search"><br> 
<input type="radio" name="l" value="Web" checked>Web 
<input type="radio" name="l" value="India">IND 
</FORM> 
<FORM action="pycgi.py" method = "POST"> 
<input type="text" name="q" size="20" maxlength="120"> 
<input type="submit" value="Search"><br> 
<input type="radio" name="l" value="Web" checked>Web 
<input type="radio" name="l" value="India">IND 
</FORM> 
</body> 
</html>
"""
print(html_str1)
ในการรับค่าเราจะอ่าน environment ของระบบโดยมีรูปแบบคล้ายกันดังนี้
Lua:

os.getenv("ชื่อตัวแปรของระบบ")
 เช่น
os.getenv("SERVER_NAME")
Python:

os.environ["ชื่อตัวแปรของระบบ"]
 เช่น
os.environ["REQUEST_METHOD"]
Ruby:

ENV["ชื่อตัวแปรของระบบ"]
 เช่น
ENV["AUTH_TYPE"]
การรับข้อมูลแบบ GET เราสามารถอ่านค่าได้จากตัวแปร "QUERY_STRING" แล้วใช้ pattern matching ดึงเอาค่าคู่ลำดับมาเก็บใน data structure ของแต่ละภาษาได้แก่ table (Lua), dict (Python), hash (Ruby) โดยใน Python กับ Ruby เราใช้ Regular Expression ที่เป็นสากลในการจัดการรูปแบบแต่ใน Lua จะมีรูปแบบเฉพาะของตัวเองเช่น

local getString = os.getenv("QUERY_STRING")
local request = {}
for key, val in string.gmatch(getString.."&", "(.-)%=(.-)%&") do
  ....(อย่าลืมใช้ pattern matching ในการแปลงพวก escape character 
  ....ด้วยเพื่อป้องกันการ injection code ที่ไม่ปลอดภัย)
  ....
  ....
  request[key] = val --ยังไม่สมบูรณ์ต้องคิดเผื่อสำหรับกลุ่มย่อยเช่นพวก check box ด้วย
end
ส่วนการรับข้อมูลแบบ POST ต้องออกแรงเพิ่มอีกหน่อย ก่อนอื่นเราตรวจสอบตัวแปร "REQUEST_METHOD" ว่ามีค่าเป็น "POST" จากนั้นหาความยาวของ post data จากตัวแปร "CONTENT_LENGTH" แปลงค่าให้เป็นตัวเลขแล้วไปใส่ใน standard input ของแต่ละภาษาเพื่อรับค่าดังนี้
Lua:

local postString = io.read(tonumber(os.getenv("CONTENT_LENGTH")))
Python:

import sys
postString = sys.stdin.read(int(os.environ["CONTENT_LENGTH"]))
Ruby:

$stdin.binmode if defined? $stdin.binmode
postString = $stdin.read(ENV["CONTENT_LENGTH"].to_i)
หลังจากได้ postString มาซึ่งมีรูปแบบเหมือน getString ข้างบนก็แยกคู่ลำดับเก็บไว้เหมือนตอนรับค่า GET ซึ่งเราควรจะทำเป็นฟังก์ชั่นแยกออกมาเพื่อเรียกใช้งานได้ทั้ง POST และ GET

แค่นี้เราก็ทำ CGI อย่างง่ายๆง่อยๆ ได้แล้ว (จริงๆ ใช้เครื่องมือที่เขามีอยู่แล้วง่ายกว่าเยอะไม่รู้จะดิ้นรนทำไม) ก็ขอจบลงเท่านี้สำหรับการเรียนรู้และลองผิดลองถูกไปเรื่อยจนได้บทความนี้มาผิดพลาดตรงไหนก็ขออภัยและโปรดชี้แนะด้วยครับ ส่วนใครอยากไปพัฒนาต่อทำมอดูลส่วนตัวไว้ใช้งานแทนตัว default ที่มีอยู่ก็ตามสะดวกครับ ดูรายละเอียดเพิ่มเติมเกี่ยวกับ CGI ได้ที่ Common Gateway Interface RFC

***สวัสดีลาก่อนพบกันใหม่เมื่อว่าง***

ความคิดเห็น