前言
在 CGI 盛行時,網頁程式多以表單傳送資料;而 Ajax 普及後,已很少再使用 CGI。不過 CGI 並未限制 HTTP 方法,因此仍可回應 Ajax 請求。
本文示範以 CGI 程式回應 Ajax 呼叫的方式。
在 CGI 程式中偵測 HTTP 方法 (HTTP Method)
先確認 CGI 程式可取得 HTTP 方法。
program main;
uses
Dos;
const
newline = ANSIChar(#10);
var
method : string;
begin
method := GetENV('REQUEST_METHOD');
Write('Content-Type: text/plain; charset=utf-8' + newline + newline);
if '' = method then
Write('Unknown HTTP method')
else
Write('HTTP method: ' + method);
end.
編譯:
$ fpc -oindex.cgi detectHTTPMethod.pas
以瀏覽器存取:
HTTP method: GET
若要測試其他方法,可使用 HTTP client,例如:
$ http PUT http://localhost:8080/cgi-bin/index.cgi
不同 CGI / FastCGI 實作支援的 HTTP 方法可能不同,可自行測試。
以 CGI 程式搭配 Ajax 呼叫
Ajax 本質是發送 HTTP request 並接收回應。 CGI 只負責讀取 request 並輸出 response,因此同樣可以配合 Ajax 使用。
以下為簡化後的前端程式:
function showMessage(message) { /* 1 */
let msg = document.getElementById('message'); /* 2 */
msg.innerHTML = /* 3 */
'<div class="alert alert-warning" role="alert">' /* 4 */
+ message /* 5 */
+ '</div>'; /* 6 */
} /* 7 */
function clearMessage() { /* 8 */
let msg = document.getElementById('message'); /* 9 */
msg.innerHTML = ''; /* 10 */
} /* 11 */
function showInfo(data) { /* 12 */
let info = document.getElementById('info'); /* 13 */
info.innerHTML = /* 14 */
'<div class="alert alert-info" role="alert">' /* 15 */
+ data /* 16 */
+ '</div>'; /* 17 */
} /* 18 */
function clearInfo() { /* 19 */
let info = document.getElementById('info'); /* 20 */
info.innerHTML = ''; /* 21 */
} /* 22 */
let btn = document.getElementById('btnInch2CM'); /* 23 */
btn.addEventListener('click', function () { /* 24 */
let value = document.getElementById('inch2cm').value; /* 26 */
if ('' === value) { /* 27 */
clearInfo() /* 28 */
showMessage('No data'); /* 29 */
} else { /* 30 */
clearMessage(); /* 31 */
document.getElementById('inch2cm').value = ''; /* 33 */
superagent /* 34 */
.put('/cgi-bin/inch2cm.cgi') /* 35 */
.send({ data: value }) /* 36 */
.then(function (res) { /* 37 */
showInfo(res.body.data); /* 38 */
}) /* 39 */
.catch(function (err) { /* 40 */
if (err.response) { /* 41 */
showMessage(err.response.message); /* 42 */
} else { /* 43 */
showMessage(err); /* 44 */
} /* 45 */
}); /* 46 */
} /* 47 */
}); /* 48 */
此範例以 PUT 方法送出 JSON:
{
"data": 100
}
CGI 程式
以下 CGI 程式負責接收資料、驗證並回傳 JSON:
program main; (* 1 *)
{$mode objfpc} (* 2 *)
uses (* 3 *)
dos, fpjson, jsonparser, sysUtils, termio; (* 4 *)
function inchToCM(input: real): real; (* 5 *)
begin (* 6 *)
inchToCM := input * 2.54; (* 7 *)
end; (* 8 *)
const (* 9 *)
newline = ANSIChar(#10); (* 10 *)
var (* 11 *)
method : string; (* 12 *)
data : TJSONData; (* 13 *)
json : TJSONObject; (* 14 *)
c : char; (* 15 *)
s : ansistring; (* 16 *)
value : real; (* 17 *)
out : real; (* 18 *)
begin (* 19 *)
method := GetEnv('REQUEST_METHOD'); (* 20 *)
if 'PUT' <> method then (* 22 *)
begin (* 23 *)
Write('HTTP/1.1 405 Method Not Allowed' + newline); (* 24 *)
Write('Content-Type: application/json' + newline + newline); (* 25 *)
json := TJSONObject.Create(); (* 26 *)
json.Add('message', 'Method Not Allowed'); (* 27 *)
Write(json.AsJSON); (* 28 *)
Halt(0); (* 29 *)
end; (* 30 *)
s := ''; (* 31 *)
while not eof(input) do (* 32 *)
begin (* 33 *)
read(c); (* 34 *)
s := s + c; (* 35 *)
end; (* 36 *)
try (* 38 *)
data := GetJSON(s); (* 39 *)
json := TJSONObject(data); (* 40 *)
except (* 41 *)
Write('HTTP/1.1 422 Unprocessable Entity' + newline); (* 42 *)
Write('Content-Type: application/json' + newline + newline); (* 43 *)
json := TJSONObject.Create(); (* 44 *)
json.Add('message', 'Wrong data format'); (* 45 *)
Write(json.AsJSON); (* 46 *)
Halt(0); (* 47 *)
end; (* 48 *)
try (* 50 *)
s := json.Get('data'); (* 51 *)
except (* 52 *)
Write('HTTP/1.1 422 Unprocessable Entity' + newline); (* 53 *)
Write('Content-Type: application/json' + newline + newline); (* 54 *)
json := TJSONObject.Create(); (* 55 *)
json.Add('message', 'No data'); (* 56 *)
Write(json.AsJSON); (* 57 *)
Halt(0); (* 58 *)
end; (* 59 *)
if '' = s then (* 61 *)
begin (* 62 *)
Write('HTTP/1.1 422 Unprocessable Entity' + newline); (* 63 *)
Write('Content-Type: application/json' + newline + newline); (* 64 *)
json := TJSONObject.Create(); (* 65 *)
json.Add('message', 'Empty data'); (* 66 *)
Write(json.AsJSON); (* 67 *)
Halt(0); (* 68 *)
end; (* 69 *)
try (* 71 *)
value := StrToFloat(s); (* 72 *)
except (* 73 *)
Write('HTTP/1.1 422 Unprocessable Entity' + newline); (* 74 *)
Write('Content-Type: application/json' + newline + newline); (* 75 *)
json := TJSONObject.Create(); (* 76 *)
json.Add('message', 'Wrong data'); (* 77 *)
Write(json.AsJSON); (* 78 *)
Halt(0); (* 79 *)
end; (* 80 *)
out := inchToCM(value); (* 81 *)
Write('Content-Type: application/json' + newline + newline); (* 83 *)
json := TJSONObject.Create(); (* 84 *)
json.Add('data', format('%.3f', [out])); (* 85 *)
Write(json.AsJSON); (* 86 *)
end. (* 87 *)
結語
CGI 並未限制 HTTP 方法,也不限制回應格式。只要符合 HTTP request / response 的規則,即可回應 Ajax 呼叫。