返回列表 发帖

CGI 程 式 设 计

大 纲 一 、 为 什 么 要 使 用 CGI ? 二 、 什 么 是 CGI ? 2-1 CGI 简 介 2-2 CGI 的 输 入 输 出 2-3 CGI 可 用 的 程 式 语 言 三 、 CGI 的 输 入 介 面 3-1 ISINDEX 3-2 FORM 3-2-1 FORM 标 签 3-2-2 INPUT 标 签 3-2-3 SELECT 标 签 3-2-4 TEXTAREA 标 签 四 、 CGI 如 何 运 作 ? 4-1 CGI 运 作 流 程 4-2 CGI 传 递 资 料 的 方 法 4-3 CGI 的 参 数 五 、 CGI 输 出 介 面 5-1 CGI 输 出 格 式 5-2 基 本 程 式 范 例 5-2-1 Perl 5-2-2 C for POST METHOD 5-2-3 C for GET METHOD -------------------------------------------------------------------------------- 一 、 为 什 么 要 使 用 CGI ? 随 著 全 球 资 讯 网 (World Wide Web, WWW) 的 兴 起 , 网 路 从 平 淡 的 文 字 世 界 进 入 了 影 音 声 光 的 新 纪 元 。 同 时 也 引 进 了 新 的 阅 读 方 法 , 一 种 有 别 於 传 统 循 序 式 的 阅 读 方 法 ━ HyperText 。 让 人 们 能 够 直 接 从 有 兴 趣 的 主 题 切 入 , 并 且 在 其 间 相 互 跳 跃 、 链 结 , 以 取 得 所 想 要 的 资 料 。 也 因 此 带 动 了 网 路 人 口 的 激 增 以 及 无 限 的 商 机 , 所 以 在 商 业 应 用 上 这 是 一 个 值 得 开 发 的 空 间 。 但 是 这 样 的 方 式 却 缺 少 了 一 个 商 业 应 用 上 一 个 重 要 的 特 性 , 那 就 是 ━ 互 动 性 。 而 CGI 的 出 现 正 是 为 了 弥 补 其 不 足 ! 有 了 CGI 功 能 的 加 入 可 以 使 得 WWW 的 环 境 不 仅 有 单 向 的 资 讯 浏 览 , 更 可 以 有 双 向 的 互 动 。 若 说 CGI 的 发 展 是 WWW 能 受 广 大 使 用 者 喜 爱 的 最 大 原 因 是 不 为 过 的 。 事 实 上 , CGI 的 应 用 相 当 的 广 泛 , 可 以 藉 著 与 其 它 系 统 的 结 合 而 增 强 WWW Server 的 功 能 。 例 如 ∶ 与 资 料 库 管 理 系 统 (DBMS) 结 合 , 便 是 一 种 最 常 见 , 而 且 是 最 具 扩 充 性 的 方 式 。 因 为 经 由 不 同 的 资 料 库 , 可 以 使 WWW Server 提 供 或 者 是 记 录 多 样 化 的 资 讯 。 同 时 , 也 可 利 用 CGI 制 作 登 录 (Login) 系 统 , 将 具 有 机 密 性 的 资 料 , 以 密 码 的 方 式 加 以 保 护 。 不 但 省 去 了 系 统 过 於 开 放 的 困 扰 , 也 可 以 有 效 的 管 理 使 用 者 。 更 进 一 步 , 可 以 记 录 使 用 者 的 使 用 情 形 , 以 便 加 以 计 费 或 者 是 在 网 路 上 进 行 电 子 交 易 。 是 故 如 果 要 将 商 业 应 用 在 WWW 上 , CGI 势 必 是 不 可 或 缺 的 一 环 。 以 下 我 们 将 会 以 和 资 料 库 管 理 系 统 结 合 为 核 心 , 逐 步 介 绍 何 谓 CGI 、 CGI 如 何 输 入 资 料 、 CGI 如 何 运 作 以 及 CGI 怎 么 输 出 资 讯 回 给 使 用 者 , 并 且 在 最 後 附 上 几 个 CGI 应 用 的 范 例 , 让 您 了 解 并 且 实 作 CGI 。 此 外 , 本 章 的 实 作 以 及 范 例 使 用 的 作 业 系 统 为 MicroSoft NT , WWW Server 为 EMWAC HTTPS , 资 料 库 管 理 系 统 为 MicroSoft SQL Server , 程 式 语 言 为 Microsoft C 与 资 料 库 管 理 系 统 的 介 面 为 DB-Library 。 关 於 这 些 部 分 的 详 细 资 料 将 不 在 赘 述 , 如 果 您 对 其 它 的 CGI 系 统 有 兴 趣 或 是 想 要 更 深 入 了 解 本 章 实 作 的 非 CGI 部 分 , 请 自 行 参 考 相 关 资 料 。 二 、 什 么 是 CGI ? 2-1 CGI 简 介 CGI 是 Common Gateway Interface 的 简 称 。 其 主 要 的 功 能 是 在 WWW 环 境 下 , 藉 由 从 客 户 端 传 递 一 些 讯 息 给 WWW Server , 再 由 WWW Server 去 启 动 所 指 定 的 程 式 码 来 完 成 特 定 的 工 作 。 所 以 更 明 确 的 说 , CGI 仅 是 在 WWW Server 上 可 执 行 的 程 式 码 , 而 她 的 工 作 就 是 控 制 讯 息 要 求 而 且 产 生 并 传 回 所 需 的 文 件 。 使 用 CGI , 你 的 Server 可 以 读 取 并 显 示 在 客 户 端 无 法 读 取 的 格 式 (像 是 SQL DataBase) 。 而 且 可 以 像 闸 道 (Gateway) 一 样 , 在 伺 服 端 和 客 户 端 之 间 , 产 生 客 户 端 所 需 要 的 讯 息 。 基 本 上 , 在 此 种 主 从 式 (Client/Server) 的 环 境 之 下 , 其 IPC (InterProcess Communication) 的 协 定 是 利 用 讯 息 传 递 及 记 忆 体 分 享 (环 境 变 数) 的 方 式 来 完 成 。 CGI 有 其 特 定 的 写 法 及 规 格 , 必 须 遵 守 其 原 则 , 方 可 达 到 主 从 端 资 讯 交 流 的 目 的 。 2-2 CGI 的 输 入 输 出 CGI 可 以 使 用 在 很 多 方 面 , 大 部 份 是 控 制 ISINDEX 和 FORM 的 需 求 , 亦 即 ISINDEX 和 FORM 最 常 被 使 用 作 CGI 的 输 入 介 面 。 但 也 有 利 用 链 结 (LINK) 的 方 式 来 呼 叫 所 要 执 行 的 程 式 , 通 常 是 在 使 用 者 的 资 讯 已 固 定 , 或 是 为 达 成 某 种 单 一 目 的 而 使 用 。 而 使 用 链 结 方 式 其 程 式 执 行 所 需 的 参 数 是 以 GET METHOD 的 方 式 含 在 URL 中 传 入 , 亦 即 CGI 程 式 在 发 展 时 必 须 使 用 GET METHOD 的 方 式 来 读 取 参 数 。 在 第 四 节 , 我 们 将 会 详 细 介 绍 传 递 参 数 的 方 法 。 输 出 的 部 分 则 大 部 分 是 利 用 程 式 来 产 生 虚 拟 文 件 , 也 就 是 文 件 并 不 是 储 存 Server 的 储 存 媒 体 中 , 而 是 由 程 式 建 构 出 来 的 , 浏 览 程 式 结 束 之 後 文 件 即 不 再 存 在 。 大 部 分 的 文 件 格 式 是 超 本 文 标 示 语 言 (HTML) 或 是 文 字 档 。 也 有 利 用 程 式 链 结 到 另 一 份 文 件 , 或 是 只 记 录 使 用 者 输 入 资 讯 而 不 做 输 出 ! 下 面 是 常 见 输 入 输 出 的 例 子 ∶ 转 换 您 的 系 统 参 考 手 册 变 成 超 本 文 标 示 语 言 (HTML) 的 格 式 , 接 著 将 结 果 送 给 客 户 端 。 当 做 WAIS 和 ARCHIE 资 料 库 的 界 面 , 将 结 果 转 成 超 本 文 标 示 语 言 (HTML) 的 格 式 送 给 客 户 端 。 允 许 使 用 者 透 过 HTML FORM 和 附 加 的 CGI 解 码 来 回 给 你 的 Server 。 在 下 面 各 节 , 我 们 将 会 进 一 步 说 明 有 关 这 一 部 分 的 使 用 方 法 , 或 是 简 单 的 范 例 。 2-3 CGI 可 用 的 程 式 语 言 至 於 CGI 要 使 用 何 种 程 式 语 言 来 达 成 ? 从 前 面 的 说 明 可 以 知 道 , CGI 仅 是 在 WWW Server 上 可 执 行 的 程 式 码 。 是 故 , 只 要 是 您 的 WWW Server 以 及 WWW Server 所 在 的 作 业 系 统 可 执 行 的 程 式 语 言 皆 能 使 用 在 CGI 上 。 以 下 列 出 几 种 较 常 在 CGI 中 使 用 的 语 言 ∶ C/C++ PERL THE BOURNE SHELL THE C SHELL TCL Visual Basic AppleScript 这 些 程 式 在 完 成 之 後 要 放 置 於 何 处 ? 事 实 上 , 不 同 的 WWW Server 有 不 同 的 规 定 。 以 HTTPS 为 例 , 她 只 执 行 副 档 名 为 EXE 的 执 行 档 , 且 程 式 不 能 为 图 形 介 面 (GUI) 的 程 式 。 而 程 式 只 要 放 置 在 指 定 文 件 的 树 状 目 录 中 任 一 位 置 即 可 。 但 是 值 得 注 意 的 是 , 大 部 分 的 WWW Server 尤 其 是 UNIX 上 的 WWW Server , 都 将 CGI 程 式 放 置 在 一 共 同 的 指 定 目 录 之 下 。 所 以 在 放 置 CGI 程 式 时 , 请 先 参 考 您 的 WWW Server 手 册 。 三 、 CGI 的 输 入 介 面 在 一 个 完 整 的 系 统 中 , 输 入 介 面 是 直 接 和 使 用 者 接 触 的 第 一 线 。 所 以 输 入 介 面 , 在 整 个 系 统 中 扮 演 著 举 足 轻 重 的 地 位 , 输 入 介 面 的 好 坏 将 直 接 影 向 系 统 的 成 败 。 好 的 输 入 介 面 可 以 弥 补 系 统 设 计 上 的 某 些 缺 陷 , 但 是 要 如 何 设 计 出 一 个 好 的 输 入 介 面 呢 ? 所 谓 「 工 欲 善 其 事 , 必 先 利 其 器 」 , 要 设 计 出 好 的 输 入 介 面 , 第 一 步 应 了 解 手 边 究 竟 有 哪 些 修 缮 门 面 的 工 具 ! 在 这 一 节 中 , 我 们 将 为 您 介 绍 CGI 的 主 要 输 入 介 面 , 让 您 做 好 成 功 的 第 一 步 。 在 CGI 中 较 常 见 的 输 入 介 面 有 ISINDEX 和 FORM 两 种 , 详 细 内 容 分 述 如 下 ∶ 3-1 ISINDEX ISINDEX 是 一 个 特 殊 的 标 签 , 使 用 的 方 法 如 下 ∶ 其 中 URL 为 想 要 执 行 的 程 式 之 所 在 位 址 及 档 名 。 如 此 会 使 浏 览 程 式 产 生 一 个 输 入 的 视 窗 , 当 使 用 者 完 成 输 入 按 下 ENTER 键 後 , Server 会 执 行 参 数 ACTION 中 指 定 的 程 式 。 下 面 是 一 个 简 单 的 范 例 ∶ -------------------------------------------------------------------------------- 可以搜索该索引。请键入要搜索的关键字: -------------------------------------------------------------------------------- 常 见 的 用 法 是 与 WAIS Server 结 合 , 或 是 做 档 案 的 搜 寻 。 本 节 的 重 点 将 放 在 FORM 的 实 作 上 , 所 以 我 们 在 这 里 不 对 ISINDEX 做 详 细 的 介 绍 , 如 有 兴 趣 请 自 行 参 考 相 关 资 料 。 3-2 FORM FROM 在 CGI 中 是 最 常 被 使 用 的 输 入 介 面 , 虽 然 并 不 十 分 完 美 , 但 却 是 目 前 WWW 中 最 强 大 的 输 入 工 具 。 FORM 是 由 一 组 相 关 连 的 标 签 所 组 成 , 使 用 方 法 就 像 其 它 标 签 一 样 。 在 FORM 中 提 供 了 我 们 多 种 输 入 资 料 的 工 具 , 例 如 文 字 输 入 区 (Text) 、 下 拉 式 表 单 (Select Box) 、 按 钮 (Button) 、 选 择 钮 (Radio Button) 等 等 。 就 目 前 所 定 义 的 标 准 中 , FORM 的 标 签 可 分 为 三 大 类 ∶ 这 个 标 签 的 目 的 在 提 供 一 个 多 行 的 输 入 区 域 。 以 结 束 。 这 个 标 签 也 允 许 设 定 内 定 值 , 供 使 用 者 修 改 。 输 入 区 的 大 小 由 ROWS 与 COLS 这 两 个 参 数 决 定 。 例 如 ∶ ROWS=20 COLS=5 表 示 一 个 大 小 为 20 乘 5 的 输 入 区 。 请 参 看 下 列 范 例 ∶ --------------------------------------------------------------------------------

请 输 入 您 对 本 公 司 的 建 议 ∶

结 果 如 下 ∶ 请 输 入 您 对 本 公 司 的 建 议 ∶ 我 的 建 议 是 ∶ -------------------------------------------------------------------------------- 当 使 用 者 输 入 的 资 料 送 到 程 式 内 时 , 程 式 会 同 时 接 收 到 NAME 及 VALUE 的 值 。 但 当 所 使 用 的 FORM 中 有 RADIO 或 SELECT 等 多 选 择 的 资 料 输 入 型 态 时 , 程 式 则 会 接 收 到 同 一 个 NAME 的 值 会 对 应 到 多 个 VALUE 的 值 。 程 式 可 以 藉 著 这 样 的 特 性 , 分 辨 使 用 者 所 做 的 多 重 选 择 并 加 以 处 理 。 但 是 , SELECT 和 RADIO 的 传 回 值 并 不 相 同 。 在 RADIO 中 , 其 值 并 不 是 FORM 上 所 见 到 的 值 , 而 是 在 VALUE 中 所 设 定 的 值 ; 在 SELECT 中 , 则 是 以 OPTION 标 签 之 後 的 值 为 传 回 值 。 也 就 是 说 使 用 SELECT , 程 式 所 收 到 的 值 和 使 用 者 看 到 的 一 样 ; 使 用 RADIO 则 可 以 设 定 不 同 的 值 , 使 得 使 用 者 可 以 见 到 有 意 义 的 选 项 , 而 程 式 可 以 以 更 简 单 的 参 数 , 来 处 理 使 用 者 选 择 的 资 料 。 如 果 , 在 RADIO 中 没 有 设 定 VALUE 的 值 , 则 传 回 的 是 字 串 "on" 。 四 、 CGI 如 何 运 作 ? 4-1 CGI 运 作 流 程 首 先 在 客 户 端 , 客 户 端 按 下 FORM 上 的 SUBMIT 按 钮 或 是 按 下 链 结 (LINK) , 告 知 浏 览 程 式 (Browser)完 成 输 入 後 。 浏 览 程 式 (Browser)将 客 户 端 输 入 的 资 讯 传 回 WWW Server , Server 启 动 指 定 的 程 式 并 将 包 装 过 的 参 数 传 入 。 接 著 程 式 依 照 传 入 的 参 数 完 成 指 定 的 工 作 。 如 果 此 时 有 需 要 传 回 结 果 的 话 , 则 程 式 会 把 结 果 传 回 给 Server , Server 再 传 至 浏 览 程 式 (Browser), 完 成 整 个 运 作 的 流 程 。 下 图 是 CGI 运 作 流 程 的 示 意 图 ∶ 如 果 现 在 要 让 WWW Server 与 其 它 的 系 统 结 合 , 则 程 式 会 扮 演 中 介 的 角 色 , 将 接 收 到 的 参 数 转 换 成 所 要 结 合 系 统 能 识 别 的 形 式 , 并 处 理 其 传 回 的 资 讯 , 再 送 回 至 Server 让 使 用 者 看 到 。 下 图 是 结 合 其 它 系 统 之 後 CGI 运 作 流 程 的 示 意 图 ∶ 接 下 来 这 二 张 图 是 以 MicroSoft SQL Server 为 例 , 结 合 WWW Server 之 後 CGI 运 作 流 程 的 示 意 图 ∶ 4-2 CGI 传 递 资 料 的 方 法 在 CGI 整 个 传 递 资 料 的 过 程 中 , 为 了 要 从 Server 传 递 有 关 输 入 资 料 给 CGI 程 式 ,Server 将 不 同 的 资 料 转 换 为 各 种 环 境 变 数 以 供 CGI 程 式 使 用 。 这 些 环 境 变 数 是 在 Server 执 行 CGI 程 式 时 被 设 定 。 我 们 在 下 一 小 节 , 将 会 介 绍 各 环 境 变 数 , 在 这 里 我 们 将 重 点 放 在 , CGI 如 何 将 使 用 者 经 FORM 所 输 入 的 资 料 会 被 如 何 处 理 再 送 到 CGI 程 式 中 。 这 是 CGI 的 关 键 所 在 , 有 了 这 项 资 料 才 能 正 确 的 收 到 并 处 理 使 用 者 输 入 的 资 料 。 在 FORM 中 , 每 一 资 料 输 入 栏 中 必 须 设 定 NAME 之 参 数 。 当 资 料 被 送 出 时 , NAME 参 数 的 值 会 和 其 相 对 应 的 VALUE 参 数 之 值 , 被 转 换 为 "NAME=VALUE" 的 形 式 。 不 同 的 资 料 输 入 栏 , 中 间 会 以 "&" 作 分 隔 , 亦 即 会 形 成 "NAME1=VALUE1&NAME2=VALUE2" 的 形 式 。 如 果 各 参 数 值 中 间 有 空 白 的 话 会 以 "+" 代 替 , 有 特 殊 字 元 则 以 "%XX" 代 替 , 其 中 "XX" 为 特 殊 字 元 的 16 进 位 ASCII 码 。 这 些 特 殊 字 元 包 括 了 "&" 、 "=" 、 中 文 以 及 ASCII 码 为 128 以 上 的 字 元 。 资 料 经 过 这 样 的 包 装 後 , 如 果 METHOD 是 设 定 为 POST 的 话 , 资 料 会 以 STDIN 的 方 式 送 入 CGI 程 式 中 ; 如 果 METHOD 是 设 定 为 GET 的 话 , 资 料 会 被 送 入 QUERY_STRING 的 环 境 变 数 中 , 以 供 程 式 读 取 。 在 前 面 的 小 节 曾 说 过 , CGI 的 程 式 可 以 用 链 结 的 方 式 起 动 , 此 时 资 料 须 以 上 述 的 方 法 包 装 後 , 加 在 程 式 名 称 的 後 方 , 最 前 面 要 有 一 "?" 符 号 与 程 式 名 称 分 隔 。 以 下 是 一 简 单 的 范 例 ∶ 按这里 接 著 , 我 们 分 别 就 不 同 的 传 输 METHOD 做 出 的 小 范 例 , 来 比 较 不 同 的 METHOD , 在 读 取 环 境 变 数 时 有 何 不 同 ∶
栏位 #1:
栏位 #2:

 

以 上 是 以 METHOD POST 的 方 式 传 输 资 料 , 所 应 参 考 的 环 境 变 数 如 下 ∶ CONTENT_LENGTH = 25 CONTENT_TYPE = application/x-www-form-urlencoded REQUEST_METHOD = POST SCRIPT_NAME = /CGI-BIN/TEST 上 面 环 境 变 数 中 , 较 值 得 注 意 的 是 CONTENT_LENGTH , 这 个 环 境 变 数 代 表 的 是 输 入 资 料 的 长 度 , 以 供 CGI 程 式 参 考 。 其 计 算 的 方 式 是 以 包 装 过 後 的 实 际 长 度 为 其 值 , 所 以 上 例 的 输 入 资 料 为 "NAME1=AA+BB&NAME2=CC%3DDD" , 长 度 25 个 字 元 。 接 下 来 , 我 们 看 看 METHOD GET 的 范 例 ∶
栏位 #1:
栏位 #2:

 

以 上 是 以 METHOD GET 的 方 式 传 输 资 料 , 所 应 参 考 的 环 境 变 数 如 下 ∶ PATH_INFO = /CGI-BIN/TEST PATH_TRANSLATED = /document_root/CGI-BIN/TEST QUERY_STRING = NAME1=AA+BB&NAME2=CC%3DDD REQUEST_METHOD = GET 由 上 面 的 例 子 可 以 看 出 , 不 同 资 料 的 传 输 方 式 , 所 参 考 的 环 境 变 数 并 不 一 样 。 所 以 程 式 在 发 展 时 , 要 注 意 与 输 入 介 面 上 设 定 的 传 输 方 式 一 致 , 不 同 的 程 式 不 应 该 混 用 , 以 避 免 产 生 不 必 要 的 错 误 。 4-3 CGI 的 参 数 在 这 一 小 节 所 列 示 的 环 境 变 数 仅 供 参 考 , 并 不 见 得 会 适 用 於 每 一 个 WWW Server 。 所 以 , 您 在 使 用 这 些 环 境 变 数 时 , 请 先 阅 读 过 您 的 WWW Server 手 册 , 以 免 产 生 不 必 要 的 错 误 ! 下 列 环 境 变 数 不 是 用 在 特 别 需 求 上 , 而 是 用 在 所 有 的 需 求 上 ∶ · GATEWAY_INTERFACE 此 变 数 是 用 来 传 送 CGI 的 修 订 版 给 Server 。 格 式 ∶ CGI/revision · SERVER_NAME 此 变 数 是 用 来 传 送 Server 的 名 字 , Domain Name Server, 或 IP Address 。 · SERVER_SOFTWARE 此 变 数 是 用 来 传 送 Server 的 软 体 名 字 和 版 本 。 格 式 ∶ name/version 下 面 的 环 境 变 数 是 给 CGI 程 式 专 用 ∶ · AUTH_TYPE 如 果 Server 提 供 使 用 者 查 核 , 而 且 程 式 是 有 保 护 的 。 这 是 一 种 辨 认 使 用 者 身 份 方 法 的 一 种 协 定 。 · CONTENT_LENGTH 这 是 使 用 者 输 入 资 料 的 长 度 。 · CONTENT_TYPE 这 是 使 用 者 输 入 资 料 的 MIME 型 态 。 · PATH_INFO 此 变 数 是 用 来 传 送 来 自 客 户 端 额 外 的 路 径 资 讯 , 换 句 话 说 , 程 式 可 以 藉 著 真 实 的 路 径 , 加 上 路 径 尾 巴 的 额 外 资 讯 来 被 执 行 。 这 额 外 资 讯 传 给 PATH_INFO 。 如 果 它 的 URL 必 须 先 经 过 CGI 程 式 码 时 , 则 要 由 Server 解 码 。 · PATH_TRANSLATED 此 变 数 是 由 Server 提 供 PATH_INFO 的 翻 译 版 本 。 · QUERY_STRING 此 变 数 是 传 送 使 用 者 输 入 的 资 料 。 在 任 何 的 版 本 下 不 会 被 解 码 。 只 要 有 使 用 者 输 入 的 资 料 , 此 变 数 就 会 被 设 定 , 不 管 命 令 列 的 解 码 。 · REMOTE_ADDR 产 生 请 求 的 远 方 主 机 的 IP 位 址 。 · REMOTE_HOST 产 生 请 求 的 远 方 主 机 名 字 。 如 果 Server 没 有 此 资 讯 , 则 设 定 REMOTE_ADDR , 并 且 不 设 定 此 一 变 数 。 · REMOTE_IDENT 如 果 Server 提 供 RFC 931 的 认 证 , 则 这 个 变 数 会 从 远 端 的 Server 取 得 使 用 者 名 字 。 这 个 变 数 应 只 使 用 在 限 制 使 用 者 登 录 上 。 · REMOTE_USER 如 果 Server 提 供 使 用 者 查 核 , 而 且 程 式 是 有 保 护 的 , 此 变 数 是 用 来 传 送 想 要 通 过 认 证 的 使 用 者 名 字 。 · REQUEST_METHOD 此 变 数 是 用 来 传 送 使 用 那 一 种 方 法 传 输 资 料 , 以 超 本 文 传 输 协 定 (HTTP) 而 言 是 "GET" 、 "POST" 以 及 "HEAD" 三 种 方 法 。 · SCRIPT_NAME 被 执 行 的 程 式 实 质 路 径 即 代 表 自 己 的 URL 。 · SERVER_PORT 此 变 数 是 用 来 传 送 那 一 个 埠 是 用 来 传 输 要 求 。 · SERVER_PROTOCOL 此 变 数 是 用 来 传 送 PROTCOL 的 名 字 和 版 本 。 格 式 ∶ protocol/revision 除 此 之 外 , 如 果 有 一 行 以 上 的 档 头 (HEADER) 从 客 户 端 接 收 到 , 则 会 被 WWW Server 在 档 头 名 称 前 面 加 上 "HTTP_" 的 开 头 之 後 被 当 成 环 境 变 数 。 在 档 头 中 如 果 有 任 何 "-" 字 元 , 将 会 被 转 成 "_" 字 元 。 另 外 Server 会 除 去 任 何 已 处 理 的 开 头 , 像 是 Authorization 、 content-type 、 content-length 。 如 果 放 入 这 些 档 头 会 超 过 系 统 环 境 限 制 , Server 会 选 择 去 删 除 全 部 或 一 些 开 头 。 五 、 CGI 的 输 出 介 面 资 料 输 出 是 CGI 流 程 的 最 後 一 部 分 , 由 於 送 出 的 资 料 不 只 一 种 型 态 。 为 了 使 WWW Server 能 够 辨 认 不 同 的 资 料 型 态 , 於 是 在 送 出 资 料 前 , 必 须 先 送 出 特 定 的 额 外 资 讯 以 做 为 辨 的 依 据 。 接 下 来 , 要 介 绍 的 就 是 这 些 额 外 资 讯 的 格 式 , 以 及 在 程 式 实 作 上 , 如 何 去 达 成 输 入 和 输 出 的 工 作 ! 5-1 CGI 输 出 格 式 当 程 式 完 成 工 作 後 , 如 果 要 输 出 资 讯 回 馈 给 使 用 者 , 则 程 式 必 须 将 结 果 以 资 料 流 的 方 式 传 回 给 Server 。 Server 负 责 把 资 料 流 以 超 本 文 传 输 协 定 (HTTP) 的 形 式 包 装 起 来 , 并 利 用 HTTP1 将 资 料 流 转 换 给 客 户 端 的 浏 览 程 式 (Browser)。 这 表 示 , Server 通 常 会 将 需 要 的 超 本 文 传 输 协 定 (HTTP) 档 头 (HEADER) 加 入 CGI 程 式 输 出 的 结 果 中 。 输 出 的 资 料 流 中 包 含 两 大 部 份 。 第 一 是 简 短 档 头 , 用 来 告 诉 Server 一 些 和 程 式 执 行 结 果 有 关 的 讯 息 。 第 二 就 是 程 式 执 行 完 的 结 果 , Server 并 不 会 解 译 或 更 改 这 一 部 份 的 资 料 , 也 就 是 客 户 端 的 浏 览 程 式 (Browser)会 完 全 接 收 到 这 一 部 份 的 资 料 。 这 两 个 部 份 之 间 要 有 一 个 以 上 的 空 白 行 作 区 隔 。 程 式 执 行 完 的 结 果 是 依 您 程 式 目 的 不 同 而 有 不 同 , 而 档 头 却 有 几 种 固 定 的 形 式 。 当 Server 接 到 无 法 认 定 的 档 头 时 会 原 封 不 动 地 送 给 客 户 端 的 浏 览 程 式 (Browser), 所 以 CGI 程 式 应 避 免 送 出 不 合 超 本 文 传 输 协 定 (HTTP) 标 准 的 档 头 , 以 免 发 生 错 误 的 动 作 。 档 头 跟 MIME 或 MAIL 的 开 头 相 似 。 它 包 含 数 行 , 每 一 行 代 表 一 个 项 目 以 及 其 参 数 , 项 目 及 参 数 以 ":" 作 区 隔 。 以 下 是 各 档 头 及 其 介 绍 ∶ Content-type: type/subtype 告 诉 Server 要 输 出 的 资 料 是 属 於 何 种 MIME 型 态 的 资 料 , type/subtype 的 值 必 须 是 MIME 承 认 的 型 态 之 一 。 举 例 来 说 , 如 果 要 输 出 的 是 超 本 文 标 示 语 言 (HTML) 文 件 则 type/subtype 应 改 为 text/html 。 如 果 要 输 出 的 是 JPEG 标 准 的 图 形 档 , 则 type/subtype 应 改 为 image/jpeg 。 有 关 MIME 的 资 料 请 自 行 参 考 相 关 资 料 。 以 下 有 一 简 单 的 范 例 以 供 参 考 ∶ --- 开 始 --- Content-type: text/html <== 内 容 的 MIME type Status: 200 OK <== 超 本 文 传 输 协 定 (HTTP) 状 态 (非 必 要) <== 档 头 和 内 容 的 分 隔 空 白 行 <== 内 容 开 始 范 例 文 件

范 例 文 件

[... etc.] --- 结 束 --- Status: 说 明 CGI 执 行 的 况 状 , 其 值 必 须 是 超 本 文 传 输 协 定 (HTTP) 所 认 可 的 。 如 果 CGI 程 式 传 回 的 结 果 中 , 并 未 包 含 此 一 档 头 的 话 , Server 会 认 定 CGI 已 成 功 的 完 成 了 工 作 , 并 且 Server 会 自 行 产 生 "200 OK" 的 Status 值 。 关 於 超 本 文 传 输 协 定 (HTTP) Status 值 的 设 定 , 请 自 行 参 考 相 关 资 料 。 URI: 在 两 个 角 括 号 中 间 的 值 可 以 是 完 整 的 URL , 或 是 相 对 位 址 指 向 某 一 档 案 , 则 这 一 档 案 的 内 容 将 会 取 代 CGI 程 式 输 出 的 结 果 。 如 果 值 是 区 域 性 的 档 案 , Server 会 将 档 案 内 容 直 接 送 到 客 户 端 。 如 果 是 完 整 的 URL 则 Server 传 回 "401 redirect" 的 讯 息 , 让 浏 览 程 式 (Browser)自 行 抓 取 指 定 的 档 案 。 Locantion: 与 URI 同 , 但 其 後 之 值 不 必 在 角 括 号 中 间 。 以 下 是 一 简 短 范 以 供 参 考 ∶ --- 开 始 --- Location: http://crc.yzit.edu.tw/intro.htm <== 指 定 之 URL <== 空 白 行 --- 结 束 --- 如 果 CGI 程 式 所 送 出 的 结 果 不 希 望 经 过 Server 的 包 装 , 而 希 望 直 接 送 到 客 户 端 的 浏 览 程 式 (Browser), 则 程 式 必 须 负 责 送 出 完 整 的 超 本 文 传 输 协 定 (HTTP) 讯 息 封 包 (Message Packaged) 。 当 Server 收 到 CGI 程 式 输 出 的 结 果 时 , 会 先 检 查 是 否 有 "HTTP/1.0" 的 讯 息 。 如 果 有 则 Server 会 认 为 以 下 的 讯 息 已 经 包 含 了 完 整 的 超 本 文 传 输 协 定 (HTTP) 回 应 , 并 将 其 直 接 送 至 客 户 端 的 浏 览 程 式 (Browser)。 以 下 是 一 简 短 的 范 例 ∶ --- 开 始 --- HTTP/1.0 200 OK <== 超 本 文 传 输 协 定 (HTTP) 档 头 开 始 Date: Tuesday, 31-May-94 19:04:30 GMT Server: WebSite 2.0 MIME-version: 1.0 Content-type: text/html Last-modified: Sunday, 15-May-94 02:12:32 GMT Content-length: 4109 <== 档 头 和 内 容 的 分 隔 空 白 行 范 例 文 件 [... etc.] --- 结 束 --- 5-2 基 本 程 式 范 例 在 程 式 的 实 作 上 , 第 一 步 是 要 先 读 取 环 境 变 数 REQUEST_METHOD 的 值 , 以 分 辨 资 料 传 输 的 方 式 。 接 著 是 依 不 同 传 输 方 式 , 接 收 使 用 输 入 资 料 ━ POST METHOD 以 STDIN 的 方 式 读 入 资 料 ; GET METHOD 则 从 环 境 变 数 QUERY_STRING 中 读 取 客 户 端 输 入 的 资 料 。 第 三 步 是 将 这 个 包 装 过 的 资 料 以 反 方 向 拆 解 开 , 分 别 置 入 不 同 的 变 数 中 。 在 处 理 过 这 些 变 数 和 完 成 所 要 的 工 作 之 後 , 输 出 资 料 回 馈 给 使 用 者 , 是 程 式 流 程 上 的 最 後 一 个 步 骤 。 输 出 的 资 依 上 一 小 节 所 介 绍 的 格 式 以 STDOUT 的 方 式 送 出 後 , 程 式 到 此 之 後 可 以 算 是 大 功 告 成 ! 下 面 是 CGI 程 式 流 程 的 简 单 示 意 图 ∶ 以 下 是 不 同 程 式 语 言 如 何 接 收 使 用 者 输 入 的 资 料 并 且 输 出 的 简 单 范 例 以 及 接 收 资 料 的 FORM ∶ -------------------------------------------------------------------------------- 输 入 栏 1: 输 入 栏 2:   -------------------------------------------------------------------------------- 以 上 的 FORM 并 未 指 定 其 METHOD 以 及 ACTION , 乃 因 以 下 的 范 例 包 含 两 种 METHOD 之 故 。 执 行 的 结 果 如 下 ∶ -------------------------------------------------------------------------------- 查 询 结 果 您 输 入 的 查 询 值 如 下 ∶ Input1 = ABCD Input2 = EFGH -------------------------------------------------------------------------------- 5-2-1 Perl 在 这 一 小 节 为 您 示 范 的 是 Perl 的 程 式 。 虽 然 , 在 本 章 的 重 点 是 以 C 语 言 为 主 干 , 但 Perl 的 程 式 由 於 处 理 档 案 时 较 C 语 言 简 洁 且 为 直 译 式 语 言 , 所 以 在 CGI 中 有 极 为 广 泛 的 应 用 。 是 故 , 我 们 增 加 了 Perl 的 范 例 , 让 您 可 以 对 如 何 用 Perl 来 写 CGI 有 初 步 的 认 识 ! 这 个 程 式 主 要 的 目 的 是 接 收 并 且 输 出 资 料 。 接 著 我 们 就 开 始 逐 步 介 绍 程 式 中 的 重 点 部 分 。 首 先 我 们 看 的 是 利 用 STDOUT 输 出 档 头 , 告 知 Server 何 种 内 容 将 会 送 出 ∶ print "Content-type: text/html\n\n"; 从 上 面 的 程 式 可 看 出 , 程 式 接 著 要 输 出 的 内 容 将 会 是 超 本 文 标 示 语 言 (HTML) 的 文 件 。 事 实 上 , 档 头 的 输 并 不 一 定 要 放 在 程 式 的 开 头 处 , 但 一 定 要 在 内 容 输 出 前 , 这 点 请 特 别 注 意 。 而 且 , 在 看 看 这 一 行 程 式 的 最 後 有 两 个 "\n" 符 号 , 为 的 是 要 再 加 上 一 行 空 白 , 以 便 和 接 著 输 出 的 内 容 做 区 隔 ! 输 出 档 头 之 後 是 辨 认 传 输 的 方 式 以 及 读 入 使 用 者 输 入 的 资 料 ∶ if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } elsif ($ENV{'REQUEST_METHOD'} eq "GET") { $buffer=$ENV{'QUERY_STRING'}; } 读 者 可 以 由 上 方 的 程 式 中 看 出 , 不 同 的 METHOD 读 取 使 用 者 输 入 资 料 的 方 式 并 不 相 同 , 请 勿 混 用 ! 完 成 了 资 料 读 取 的 动 作 之 後 , 接 下 来 则 是 重 头 戏 ━ 分 解 资 料 。 @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $A{$name} = $value; print "
  • $name = $value\n\n"; } 事 实 上 , 本 范 例 并 未 将 不 同 的 资 料 置 入 不 同 的 变 数 中 , 而 是 直 接 输 出 。 如 果 您 想 要 对 资 料 做 进 一 步 的 处 理 , 可 以 将 程 式 加 以 修 改 、 扩 充 。 请 注 意 , 在 输 出 变 数 之 前 已 先 输 出 部 分 的 超 本 文 标 示 语 言 (HTML) 文 件 的 内 容 。 print "

    查 询 结 果

    "; print "您 输 入 的 查 询 值 如 下 ∶

    \n"; print "

      \n"; 由 上 可 见 , 用 程 式 输 出 超 本 文 标 示 语 言 (HTML) 文 件 和 一 般 建 立 超 本 文 标 示 语 言 (HTML) 的 格 式 并 无 差 异 , 不 同 的 只 是 建 立 者 是 人 与 程 式 的 分 别 ! 以 下 是 完 整 的 程 式 列 表 ∶ -------------------------------------------------------------------------------- #!/usr/local/bin/perl $| = 1; print "Content-type: text/html\n\n"; if ($ENV{'REQUEST_METHOD'} eq "POST") { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); } elsif ($ENV{'REQUEST_METHOD'} eq "GET") { $buffer=$ENV{'QUERY_STRING'}; } print "

      查 询 结 果

      "; print "您 输 入 的 查 询 值 如 下 ∶

      \n"; print "

        \n"; @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $A{$name} = $value; print "
      • $name = $value\n\n"; } print "
      \n" -------------------------------------------------------------------------------- 上 面 的 程 式 是 一 个 「 麻 雀 虽 小 , 五 脏 俱 全 」 的 程 式 。 只 要 将 其 适 当 的 位 置 加 上 所 需 的 程 式 就 可 以 达 到 预 定 的 功 能 , 相 信 能 对 您 有 所 助 益 。 5-2-2 C for POST METHOD C 语 言 ━ 本 节 的 主 角 登 场 ! 这 是 一 个 使 用 C 语 言 以 接 收 POST METHOD 为 目 的 的 CGI BACKEND 程 式 。 就 如 同 上 面 的 范 例 , 虽 然 只 是 简 单 的 输 出 使 用 者 输 入 的 资 料 。 但 经 过 改 造 之 後 , 功 能 将 不 可 同 日 而 语 ! 现 在 就 让 我 们 开 始 逐 步 分 解 程 式 的 内 容 。 一 开 始 我 们 看 到 的 是 档 头 的 输 出 , 如 同 上 一 个 范 例 。 printf("Content-type: text/html%c%c",10,10); 同 样 的 , 我 们 也 可 以 见 到 在 这 一 行 程 式 的 尾 端 送 出 了 两 个 ASCII 码 为 10 的 字 元 , 来 产 生 与 输 出 内 容 分 隔 的 空 白 行 。 if(strcmp(getenv("REQUEST_METHOD"),"POST")) { printf("This script should be referenced with a METHOD of POST.\n"); exit(1); } if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) { printf("This script can only be used to decode form results. \n"); exit(1); } 这 段 程 式 是 辨 认 传 输 的 方 式 。 因 为 这 范 例 程 式 只 做 为 单 一 传 输 方 式 使 用 , 是 故 和 上 一 个 范 例 有 些 许 的 差 距 。 for(x=0;cl && (!feof(stdin));x++) { m=x; entries[x].val = fmakeword(stdin,'&',&cl); plustospace(entries[x].val); unescape_url(entries[x].val); entries[x].name = makeword(entries[x].val,'='); } 接 下 来 的 程 式 就 是 读 入 使 用 者 输 入 的 资 料 , 及 将 使 用 者 输 入 的 资 料 拆 解 开 。 注 意 ! 这 个 范 例 是 将 资 料 分 别 置 入 不 同 的 阵 列 元 素 中 , 与 上 一 个 例 子 并 不 相 同 。 printf("

      查 询 结 果

      "); printf("您 输 入 的 查 询 值 如 下 ∶

      %c",10); printf("

        %c",10); for(x=0; x <= m; x++) printf("
      • %s = %s%c",entries[x].name, entries[x].val,10); printf("
      %c",10); 到 了 这 里 就 是 程 式 的 最 後 一 部 分 ━ 输 出 回 的 资 讯 。 以 下 是 完 整 的 程 式 列 表 ∶ -------------------------------------------------------------------------------- #include #include #define MAX_ENTRIES 50 #define LF 10 #define CR 13 typedef struct { char *name; char *val; } entry; char *makeword(char *line, char stop); char *fmakeword(FILE *f, char stop, int *len); char x2c(char *what); void unescape_url(char *url); void plustospace(char *str); main(int argc, char *argv[]) { entry entries[MAX_ENTRIES]; register int x,m=0; int cl; printf("Content-type: text/html%c%c",10,10); if(strcmp(getenv("REQUEST_METHOD"),"POST")) { printf("This script should be referenced with a METHOD of POST.\n"); exit(1); } if(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) { printf("This script can only be used to decode form results. \n"); exit(1); } cl = atoi(getenv("CONTENT_LENGTH")); for(x=0;cl && (!feof(stdin));x++) { m=x; entries[x].val = fmakeword(stdin,'&',&cl); plustospace(entries[x].val); unescape_url(entries[x].val); entries[x].name = makeword(entries[x].val,'='); } printf("

      查 询 结 果

      "); printf("您 输 入 的 查 询 值 如 下 ∶

      %c",10); printf("

        %c",10); for(x=0; x <= m; x++) printf("
      • %s = %s%c",entries[x].name, entries[x].val,10); printf("
      %c",10); } char *makeword(char *line, char stop) { int x = 0,y; char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1)); for(x=0;((line[x]) && (line[x] != stop));x++) word[x] = line[x]; word[x] = '\0'; if(line[x]) ++x; y=0; while(line[y++] = line[x++]); return word; } char *fmakeword(FILE *f, char stop, int *cl) { int wsize; char *word; int ll; wsize = 102400; ll=0; word = (char *) malloc(sizeof(char) * (wsize + 1)); while(1) { word[ll] = (char)fgetc(f); if(ll==wsize) { word[ll+1] = '\0'; wsize+=102400; word = (char *)realloc(word,sizeof(char)*(wsize+1)); } --(*cl); if((word[ll] == stop) || (feof(f)) || (!(*cl))) { if(word[ll] != stop) ll++; word[ll] = '\0'; return word; } ++ll; } } char x2c(char *what) { register char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return(digit); } void unescape_url(char *url) { register int x,y; for(x=0,y=0;url[y];++x,++y) { if((url[x] = url[y]) == '%') { url[x] = x2c(&url[y+1]); y+=2; } } url[x] = '\0'; } void plustospace(char *str) { register int x; for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' '; } -------------------------------------------------------------------------------- 5-2-3 C for GET METHOD 本 节 的 最 後 一 个 范 例 是 以 C 语 言 做 一 个 接 收 GET METHOD 资 料 的 简 单 范 例 。 基 本 上 , 这 个 范 例 与 上 一 个 范 例 没 有 太 多 的 差 异 , 主 要 的 目 的 是 让 您 比 较 在 C 语 言 中 , 这 两 种 传 输 方 式 在 程 式 写 法 上 有 何 不 同 。 以 下 只 就 有 差 异 的 部 分 作 介 绍 。 if(strcmp(getenv("REQUEST_METHOD"),"GET")) { printf("This script should be referenced with a METHOD of GET.\n"); exit(1); } cl = getenv("QUERY_STRING"); if(cl == NULL) { printf("No query information to decode.\n"); exit(1); } 这 段 程 式 最 大 的 差 异 在 读 取 资 料 是 从 QUERY_STRING 取 得 , 而 非 从 STDIN 取 得 。 for(x=0;cl[0] != '\0';x++) { m=x; getword(entries[x].val,cl,'&'); plustospace(entries[x].val); unescape_url(entries[x].val); getword(entries[x].name,entries[x].val,'='); } 再 者 , 是 分 解 资 料 时 使 的 函 式 有 所 不 同 。 但 这 并 不 会 影 向 程 式 的 执 行 , 因 为 两 个 范 例 所 附 的 函 式 都 是 独 立 的 。 且 这 些 程 式 都 已 确 认 过 可 执 行 , 只 要 不 更 动 函 式 部 分 程 式 就 可 以 正 常 的 执 行 。 以 下 是 完 整 的 程 式 列 表 ∶ -------------------------------------------------------------------------------- #include #include typedef struct { char name[128]; char val[128]; } entry; void getword(char *word, char *line, char stop); char x2c(char *what); void unescape_url(char *url); void plustospace(char *str); main(int argc, char *argv[]) { entry entries[50]; register int x,m=0; char *cl; printf("Content-type: text/html%c%c",10,10); if(strcmp(getenv("REQUEST_METHOD"),"GET")) { printf("This script should be referenced with a METHOD of GET.\n"); exit(1); } cl = getenv("QUERY_STRING"); if(cl == NULL) { printf("No query information to decode.\n"); exit(1); } for(x=0;cl[0] != '\0';x++) { m=x; getword(entries[x].val,cl,'&'); plustospace(entries[x].val); unescape_url(entries[x].val); getword(entries[x].name,entries[x].val,'='); } printf("

      查 询 结 果

      "); printf("您 输 入 的 查 询 值 如 下 ∶

      %c",10); printf("

        %c",10); for(x=0; x <= m; x++) printf("
      • %s = %s%c",entries[x].name, entries[x].val,10); printf("
      %c",10); } void getword(char *word, char *line, char stop) { int x = 0,y; for(x=0;((line[x]) && (line[x] != stop));x++) word[x] = line[x]; word[x] = '\0'; if(line[x]) ++x; y=0; while(line[y++] = line[x++]); } char x2c(char *what) { register char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return(digit); } void unescape_url(char *url) { register int x,y; for(x=0,y=0;url[y];++x,++y) { if((url[x] = url[y]) == '%') { url[x] = x2c(&url[y+1]); y+=2; } } url[x] = '\0'; } void plustospace(char *str) { register int x; for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' '; }
  • 返回列表 回复 发帖