2019-04-21 11:50:48 +08:00

54 lines
47 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div class="body" role="main"><div class="section" id="module-cgi"><h1><span class="yiyi-st" id="yiyi-10">21.2. <a class="reference internal" href="#module-cgi" title="cgi: Helpers for running Python scripts via the Common Gateway Interface."><code class="xref py py-mod docutils literal"><span class="pre">cgi</span></code></a> - 公共网关接口支持</span></h1><p><span class="yiyi-st" id="yiyi-11"><strong>源代码:</strong> <a class="reference external" href="https://hg.python.org/cpython/file/3.5/Lib/cgi.py">Lib / cgi.py</a></span></p><p><span class="yiyi-st" id="yiyi-12">通用网关接口CGI脚本的支持模块。</span></p><p><span class="yiyi-st" id="yiyi-13">这个模块定义了一些由Python编写的CGI脚本使用的实用程序。</span></p><div class="section" id="introduction"><h2><span class="yiyi-st" id="yiyi-14">21.2.1. </span><span class="yiyi-st" id="yiyi-15">引言</span></h2><p id="cgi-intro"><span class="yiyi-st" id="yiyi-16">CGI脚本由HTTP服务器调用通常用于处理通过HTML <code class="docutils literal"><span class="pre">&lt;FORM&gt;</span></code><code class="docutils literal"><span class="pre">&lt;ISINDEX&gt;</span></code>元素提交的用户输入。</span></p><p><span class="yiyi-st" id="yiyi-17">通常CGI脚本存放在服务器的特殊<code class="file docutils literal"><span class="pre">cgi-bin</span></code>目录中。</span><span class="yiyi-st" id="yiyi-18">HTTP服务器在脚本的shell环境中放置有关请求的所有信息例如客户端的主机名请求的URL查询字符串和许多其他好处执行脚本并将脚本的输出发送回客户。</span></p><p><span class="yiyi-st" id="yiyi-19">脚本的输入也连接到客户端有时表单数据以这种方式读取在其他时候表单数据通过URL的“查询字符串”部分传递。</span><span class="yiyi-st" id="yiyi-20">此模块旨在处理不同的情况并为Python脚本提供更简单的界面。</span><span class="yiyi-st" id="yiyi-21">它还提供了许多帮助调试脚本的实用程序,最新添加的是支持从表单(如果您的浏览器支持)上传文件。</span></p><p><span class="yiyi-st" id="yiyi-22">CGI脚本的输出应由两个部分组成由空行分隔。</span><span class="yiyi-st" id="yiyi-23">第一部分包含许多标题,告诉客户端跟踪哪种数据(告诉客户端接下来有什么数据)。</span><span class="yiyi-st" id="yiyi-24">用于生成最小头部分的Python代码如下所示</span></p><pre><code class="language-python"><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">"Content-Type: text/html"</span><span class="p">)</span> <span class="c1"># HTML is following</span>
<span class="nb">print</span><span class="p">()</span> <span class="c1"># blank line, end of headers</span>
</code></pre><p><span class="yiyi-st" id="yiyi-25">第二部分通常是HTML它允许客户端软件显示格式正确的文本包括标题内嵌图像等。</span><span class="yiyi-st" id="yiyi-26">这里是打印一个简单的HTML的Python代码</span></p><pre><code class="language-python"><span></span><span class="nb">print</span><span class="p">(</span><span class="s2">"&lt;TITLE&gt;CGI script output&lt;/TITLE&gt;"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"&lt;H1&gt;This is my first CGI script&lt;/H1&gt;"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Hello, world!"</span><span class="p">)</span>
</code></pre></div><div class="section" id="using-the-cgi-module"><h2><span class="yiyi-st" id="yiyi-27">21.2.2. </span><span class="yiyi-st" id="yiyi-28">使用cgi模块</span></h2><p><span class="yiyi-st" id="yiyi-29">首先写入<code class="docutils literal"><span class="pre">import</span> <span class="pre">cgi</span></code></span></p><p><span class="yiyi-st" id="yiyi-30">当您编写新脚本时,请考虑添加以下行:</span></p><pre><code class="language-python"><span></span><span class="kn">import</span> <span class="nn">cgitb</span>
<span class="n">cgitb</span><span class="o">.</span><span class="n">enable</span><span class="p">()</span>
</code></pre><p><span class="yiyi-st" id="yiyi-31">这将激活一个特殊的异常处理程序如果发生任何错误将在Web浏览器中显示详细的报告。</span><span class="yiyi-st" id="yiyi-32">如果您不想向脚本用户显示程序的内容,您可以将报告保存到文件,代码如下:</span></p><pre><code class="language-python"><span></span><span class="kn">import</span> <span class="nn">cgitb</span>
<span class="n">cgitb</span><span class="o">.</span><span class="n">enable</span><span class="p">(</span><span class="n">display</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">logdir</span><span class="o">=</span><span class="s2">"/path/to/logdir"</span><span class="p">)</span>
</code></pre><p><span class="yiyi-st" id="yiyi-33">在脚本开发期间使用此功能非常有帮助。</span><span class="yiyi-st" id="yiyi-34"><a class="reference internal" href="cgitb.html#module-cgitb" title="cgitb: Configurable traceback handler for CGI scripts."><code class="xref py py-mod docutils literal"><span class="pre">cgitb</span></code></a>生成的报告提供了可以节省大量时间来跟踪错误的信息。</span><span class="yiyi-st" id="yiyi-35">您可以随时在测试脚本并确信它可以正常工作后,删除<code class="docutils literal"><span class="pre">cgitb</span></code>行。</span></p><p><span class="yiyi-st" id="yiyi-36">要获取提交的表单数据,请使用<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>类。</span><span class="yiyi-st" id="yiyi-37">如果表单包含非ASCII字符请使用设置为为文档定义的编码值的<em>encoding</em>关键字参数。</span><span class="yiyi-st" id="yiyi-38">它通常包含在HTML文档HEAD部分的META标记中或者通过<em class="mailheader">Content-Type</em>标题)。</span><span class="yiyi-st" id="yiyi-39">这从标准输入或环境读取表单内容取决于根据CGI标准设置的各种环境变量的值</span><span class="yiyi-st" id="yiyi-40">由于它可能消耗标准输入,它应该只被实例化一次。</span></p><p><span class="yiyi-st" id="yiyi-41"><code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>实例可以像Python字典一样索引。</span><span class="yiyi-st" id="yiyi-42">It allows membership testing with the <a class="reference internal" href="../reference/expressions.html#in"><code class="xref std std-keyword docutils literal"><span class="pre">in</span></code></a> operator, and also supports the standard dictionary method <a class="reference internal" href="stdtypes.html#dict.keys" title="dict.keys"><code class="xref py py-meth docutils literal"><span class="pre">keys()</span></code></a> and the built-in function <a class="reference internal" href="functions.html#len" title="len"><code class="xref py py-func docutils literal"><span class="pre">len()</span></code></a>. </span><span class="yiyi-st" id="yiyi-43">包含空字符串的表单字段将被忽略,不会出现在字典中;要保留此类值,请在创建<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>实例时为可选的<em>keep_blank_values</em>关键字参数提供真实值。</span></p><p><span class="yiyi-st" id="yiyi-44">对于实例,以下代码(假设已经打印了<em class="mailheader">Content-Type</em>头和空白行)检查字段<code class="docutils literal"><span class="pre">name</span></code><code class="docutils literal"><span class="pre">addr</span></code></span></p><pre><code class="language-python"><span></span><span class="n">form</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">FieldStorage</span><span class="p">()</span>
<span class="k">if</span> <span class="s2">"name"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">form</span> <span class="ow">or</span> <span class="s2">"addr"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">form</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"&lt;H1&gt;Error&lt;/H1&gt;"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Please fill in the name and addr fields."</span><span class="p">)</span>
<span class="k">return</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"&lt;p&gt;name:"</span><span class="p">,</span> <span class="n">form</span><span class="p">[</span><span class="s2">"name"</span><span class="p">]</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"&lt;p&gt;addr:"</span><span class="p">,</span> <span class="n">form</span><span class="p">[</span><span class="s2">"addr"</span><span class="p">]</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="o">...</span><span class="n">further</span> <span class="n">form</span> <span class="n">processing</span> <span class="n">here</span><span class="o">...</span>
</code></pre><p><span class="yiyi-st" id="yiyi-45">这里通过<code class="docutils literal"><span class="pre">form[key]</span></code>访问的字段本身是<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>(或<code class="xref py py-class docutils literal"><span class="pre">MiniFieldStorage</span></code>,取决于表单编码)的实例。</span><span class="yiyi-st" id="yiyi-46">实例的<code class="xref py py-attr docutils literal"><span class="pre">value</span></code>属性生成字段的字符串值。</span><span class="yiyi-st" id="yiyi-47"><code class="xref py py-meth docutils literal"><span class="pre">getvalue()</span></code>方法直接返回此字符串值;它还接受可选的第二个参数作为默认值,如果请求的键不存在则返回。</span></p><p><span class="yiyi-st" id="yiyi-48">如果提交的表单数据包含多个具有相同名称的字段,则<code class="docutils literal"><span class="pre">form[key]</span></code>检索的对象不是<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code><code class="xref py py-class docutils literal"><span class="pre">MiniFieldStorage</span></code>实例,但是这种实例的列表。</span><span class="yiyi-st" id="yiyi-49">类似地,在这种情况下,<code class="docutils literal"><span class="pre">form.getvalue(key)</span></code>将返回字符串列表。</span><span class="yiyi-st" id="yiyi-50">如果您期望这种可能性当您的HTML表单包含多个具有相同名称的字段请使用<a class="reference internal" href="#cgi.FieldStorage.getlist" title="cgi.FieldStorage.getlist"><code class="xref py py-meth docutils literal"><span class="pre">getlist()</span></code></a>方法,它总是返回值列表(以便您不需要特殊 - case单项案例</span><span class="yiyi-st" id="yiyi-51">例如,此代码连接任意数量的用户名字段,用逗号分隔:</span></p><pre><code class="language-python"><span></span><span class="n">value</span> <span class="o">=</span> <span class="n">form</span><span class="o">.</span><span class="n">getlist</span><span class="p">(</span><span class="s2">"username"</span><span class="p">)</span>
<span class="n">usernames</span> <span class="o">=</span> <span class="s2">","</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
</code></pre><p><span class="yiyi-st" id="yiyi-52">如果字段表示上传的文件,通过<code class="xref py py-attr docutils literal"><span class="pre">value</span></code>属性或<code class="xref py py-meth docutils literal"><span class="pre">getvalue()</span></code>方法访问该值将以字节形式读取内存中的整个文件。</span><span class="yiyi-st" id="yiyi-53">这可能不是你想要的。</span><span class="yiyi-st" id="yiyi-54">您可以通过测试<code class="xref py py-attr docutils literal"><span class="pre">filename</span></code>属性或<code class="xref py py-attr docutils literal"><span class="pre">file</span></code>属性来测试上传的文件。</span><span class="yiyi-st" id="yiyi-55">你可以在 <code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code> 实例的垃圾回收器部分自动关闭前从 <code class="xref py py-attr docutils literal"><span class="pre">file</span></code> 属性读取数据( <a class="reference internal" href="io.html#io.RawIOBase.read" title="io.RawIOBase.read"><code class="xref py py-func docutils literal"><span class="pre">read()</span></code></a><a class="reference internal" href="io.html#io.IOBase.readline" title="io.IOBase.readline"><code class="xref py py-func docutils literal"><span class="pre">readline()</span></code></a> 方法返回的是字节):</span></p><pre><code class="language-python"><span></span><span class="n">fileitem</span> <span class="o">=</span> <span class="n">form</span><span class="p">[</span><span class="s2">"userfile"</span><span class="p">]</span>
<span class="k">if</span> <span class="n">fileitem</span><span class="o">.</span><span class="n">file</span><span class="p">:</span>
<span class="c1"># It's an uploaded file; count lines</span>
<span class="n">linecount</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="n">fileitem</span><span class="o">.</span><span class="n">file</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">line</span><span class="p">:</span> <span class="k">break</span>
<span class="n">linecount</span> <span class="o">=</span> <span class="n">linecount</span> <span class="o">+</span> <span class="mi">1</span>
</code></pre><p><span class="yiyi-st" id="yiyi-56"><code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>对象也支持在<a class="reference internal" href="../reference/compound_stmts.html#with"><code class="xref std std-keyword docutils literal"><span class="pre">with</span></code></a>语句中使用,它会在完成后自动关闭它们。</span></p><p><span class="yiyi-st" id="yiyi-57">如果在获取上传文件的内容时遇到错误(例如,当用户通过单击后退或取消按钮中断表单提交时),该字段的对象的<code class="xref py py-attr docutils literal"><span class="pre">done</span></code>属性将设置为值-1。</span></p><p><span class="yiyi-st" id="yiyi-58">文件上传草稿标准允许从一个字段上传多个文件(使用递归<em class="mimetype">multipart / *</em>编码)。</span><span class="yiyi-st" id="yiyi-59">发生这种情况时,该项目将是一个类似字典的<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>项目。</span><span class="yiyi-st" id="yiyi-60">这可以通过测试其<code class="xref py py-attr docutils literal"><span class="pre">type</span></code>属性来确定,该属性应为<em class="mimetype">multipart / form-data</em>或者另一个MIME类型匹配<em class="mimetype">multipart / *</em></span><span class="yiyi-st" id="yiyi-61">在这种情况下,它可以像顶层表单对象一样通过递归迭代。</span></p><p><span class="yiyi-st" id="yiyi-62">当表单以“旧”格式(作为查询字符串或作为<em class="mimetype">application / x-www-form-urlencoded</em>类型的单个数据部分提交时项目实际上将是class <code class="xref py py-class docutils literal"><span class="pre">MiniFieldStorage</span></code></span><span class="yiyi-st" id="yiyi-63">在这种情况下,<code class="xref py py-attr docutils literal"><span class="pre">list</span></code><code class="xref py py-attr docutils literal"><span class="pre">file</span></code><code class="xref py py-attr docutils literal"><span class="pre">filename</span></code>属性始终为<code class="docutils literal"><span class="pre">None</span></code></span></p><p><span class="yiyi-st" id="yiyi-64">通过POST提交并具有查询字符串的表单将同时包含<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code><code class="xref py py-class docutils literal"><span class="pre">MiniFieldStorage</span></code>项目。</span></p><div class="versionchanged"><p><span class="yiyi-st" id="yiyi-65"><span class="versionmodified">在版本3.4中更改:</span>在创建<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>实例的垃圾回收时,<code class="xref py py-attr docutils literal"><span class="pre">file</span></code>属性会自动关闭。</span></p></div><div class="versionchanged"><p><span class="yiyi-st" id="yiyi-66"><span class="versionmodified">在3.5版本中已更改:</span><code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>类添加了上下文管理协议支持。</span></p></div></div><div class="section" id="higher-level-interface"><h2><span class="yiyi-st" id="yiyi-67">21.2.3. </span><span class="yiyi-st" id="yiyi-68">更高层次的接口</span></h2><p><span class="yiyi-st" id="yiyi-69">上一节介绍如何使用<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>类读取CGI表单数据。</span><span class="yiyi-st" id="yiyi-70">本节介绍了一个更高级别的接口,它被添加到此类中,以允许以更可读和直观的方式完成它。</span><span class="yiyi-st" id="yiyi-71">该接口不会使前面部分中描述的技术过时 - 例如,它们仍然可以有效地处理文件上传。</span></p><p><span class="yiyi-st" id="yiyi-72">该接口由两个简单的方法组成。</span><span class="yiyi-st" id="yiyi-73">使用这些方法,您可以以通用方式处理表单数据,而无需担心是否只在一个名称下发布一个或多个值。</span></p><p><span class="yiyi-st" id="yiyi-74">在上一节中,您学习在任何时候希望用户在一个名称下发布多个值时编写以下代码:</span></p><pre><code class="language-python"><span></span><span class="n">item</span> <span class="o">=</span> <span class="n">form</span><span class="o">.</span><span class="n">getvalue</span><span class="p">(</span><span class="s2">"item"</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">item</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
<span class="c1"># The user is requesting more than one item.</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># The user is requesting only one item.</span>
</code></pre><p><span class="yiyi-st" id="yiyi-75">这种情况很常见,例如当表单包含一组具有相同名称的多个复选框时:</span></p><pre><code class="language-python"><span></span><span class="o">&lt;</span><span class="nb">input</span> <span class="nb">type</span><span class="o">=</span><span class="s2">"checkbox"</span> <span class="n">name</span><span class="o">=</span><span class="s2">"item"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"1"</span> <span class="o">/&gt;</span>
<span class="o">&lt;</span><span class="nb">input</span> <span class="nb">type</span><span class="o">=</span><span class="s2">"checkbox"</span> <span class="n">name</span><span class="o">=</span><span class="s2">"item"</span> <span class="n">value</span><span class="o">=</span><span class="s2">"2"</span> <span class="o">/&gt;</span>
</code></pre><p><span class="yiyi-st" id="yiyi-76">然而,在大多数情况下,在表单中只有一个具有特定名称的表单控件,然后您期望并且只需要一个与该名称关联的值。</span><span class="yiyi-st" id="yiyi-77">所以你写一个脚本包含例如这段代码:</span></p><pre><code class="language-python"><span></span><span class="n">user</span> <span class="o">=</span> <span class="n">form</span><span class="o">.</span><span class="n">getvalue</span><span class="p">(</span><span class="s2">"user"</span><span class="p">)</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
</code></pre><p><span class="yiyi-st" id="yiyi-78">代码的问题是,你不应该期望客户端将为您的脚本提供有效的输入。</span><span class="yiyi-st" id="yiyi-79">例如,如果好奇的用户将另一个<code class="docutils literal"><span class="pre">user=foo</span></code>对附加到查询字符串,则脚本会崩溃,因为在这种情况下,<code class="docutils literal"><span class="pre">getvalue("user")</span></code>调用返回一个列表而不是字符串。</span><span class="yiyi-st" id="yiyi-80">调用列表上的<a class="reference internal" href="stdtypes.html#str.upper" title="str.upper"><code class="xref py py-meth docutils literal"><span class="pre">upper()</span></code></a>方法无效(因为列表没有此名称的方法),并导致<a class="reference internal" href="exceptions.html#AttributeError" title="AttributeError"><code class="xref py py-exc docutils literal"><span class="pre">AttributeError</span></code></a>异常。</span></p><p><span class="yiyi-st" id="yiyi-81">因此,读取表单数据值的适当方式是始终使用检查所获得的值是单个值还是值列表的代码。</span><span class="yiyi-st" id="yiyi-82">这很烦人,导致可读性较差的脚本。</span></p><p><span class="yiyi-st" id="yiyi-83">更方便的方法是使用由此较高级别接口提供的方法<a class="reference internal" href="#cgi.FieldStorage.getfirst" title="cgi.FieldStorage.getfirst"><code class="xref py py-meth docutils literal"><span class="pre">getfirst()</span></code></a><a class="reference internal" href="#cgi.FieldStorage.getlist" title="cgi.FieldStorage.getlist"><code class="xref py py-meth docutils literal"><span class="pre">getlist()</span></code></a></span></p><dl class="method"><dt id="cgi.FieldStorage.getfirst"><span class="yiyi-st" id="yiyi-84"> <code class="descclassname">FieldStorage.</code><code class="descname">getfirst</code><span class="sig-paren">(</span><em>name</em>, <em>default=None</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-85">此方法始终只返回与表单字段<em>name</em>关联的一个值。</span><span class="yiyi-st" id="yiyi-86">该方法仅返回第一个值,以便在以此名称发布更多值的情况下。</span><span class="yiyi-st" id="yiyi-87">请注意,收到的值的顺序可能因浏览器而异,不应计入。</span><span class="yiyi-st" id="yiyi-88"><a class="footnote-reference" href="#id3" id="id2">[1]</a>如果没有这样的表单字段或值,则该方法返回由可选参数<em>默认</em>指定的值。</span><span class="yiyi-st" id="yiyi-89">如果未指定,此参数默认为<code class="docutils literal"><span class="pre">None</span></code></span></p></dd></dl><dl class="method"><dt id="cgi.FieldStorage.getlist"><span class="yiyi-st" id="yiyi-90"> <code class="descclassname">FieldStorage.</code><code class="descname">getlist</code><span class="sig-paren">(</span><em>name</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-91">此方法总是返回与表单字段<em>name</em>相关联的值的列表。</span><span class="yiyi-st" id="yiyi-92">如果<em>name</em>不存在此类表单字段或值,此方法将返回一个空列表。</span><span class="yiyi-st" id="yiyi-93">如果只有一个这样的值,它返回一个由一个项组成的列表。</span></p></dd></dl><p><span class="yiyi-st" id="yiyi-94">使用这些方法,你可以编写不错的紧凑代码:</span></p><pre><code class="language-python"><span></span><span class="kn">import</span> <span class="nn">cgi</span>
<span class="n">form</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">FieldStorage</span><span class="p">()</span>
<span class="n">user</span> <span class="o">=</span> <span class="n">form</span><span class="o">.</span><span class="n">getfirst</span><span class="p">(</span><span class="s2">"user"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> <span class="c1"># This way it's safe.</span>
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">form</span><span class="o">.</span><span class="n">getlist</span><span class="p">(</span><span class="s2">"item"</span><span class="p">):</span>
<span class="n">do_something</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</code></pre></div><div class="section" id="functions"><h2><span class="yiyi-st" id="yiyi-95">21.2.4. </span><span class="yiyi-st" id="yiyi-96">功能</span></h2><p><span class="yiyi-st" id="yiyi-97">如果你想要更多的控制,或者如果你想在其他情况下使用在这个模块中实现的一些算法,这些是有用的。</span></p><dl class="function"><dt id="cgi.parse"><span class="yiyi-st" id="yiyi-98"> <code class="descclassname">cgi.</code><code class="descname">parse</code><span class="sig-paren">(</span><em>fp=None</em>, <em>environ=os.environ</em>, <em>keep_blank_values=False</em>, <em>strict_parsing=False</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-99">在环境或文件中解析查询(文件默认为<code class="docutils literal"><span class="pre">sys.stdin</span></code>)。</span><span class="yiyi-st" id="yiyi-100"><em>keep_blank_values</em><em>strict_parsing</em>参数会不变地传递到<a class="reference internal" href="urllib.parse.html#urllib.parse.parse_qs" title="urllib.parse.parse_qs"><code class="xref py py-func docutils literal"><span class="pre">urllib.parse.parse_qs()</span></code></a></span></p></dd></dl><dl class="function"><dt id="cgi.parse_qs"><span class="yiyi-st" id="yiyi-101"> <code class="descclassname">cgi.</code><code class="descname">parse_qs</code><span class="sig-paren">(</span><em>qs</em>, <em>keep_blank_values=False</em>, <em>strict_parsing=False</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-102">此模块中已弃用此函数。</span><span class="yiyi-st" id="yiyi-103">请改用<a class="reference internal" href="urllib.parse.html#urllib.parse.parse_qs" title="urllib.parse.parse_qs"><code class="xref py py-func docutils literal"><span class="pre">urllib.parse.parse_qs()</span></code></a></span><span class="yiyi-st" id="yiyi-104">这里维护它只是为了向后兼容。</span></p></dd></dl><dl class="function"><dt id="cgi.parse_qsl"><span class="yiyi-st" id="yiyi-105"> <code class="descclassname">cgi.</code><code class="descname">parse_qsl</code><span class="sig-paren">(</span><em>qs</em>, <em>keep_blank_values=False</em>, <em>strict_parsing=False</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-106">此模块中已弃用此函数。</span><span class="yiyi-st" id="yiyi-107">请改用<a class="reference internal" href="urllib.parse.html#urllib.parse.parse_qsl" title="urllib.parse.parse_qsl"><code class="xref py py-func docutils literal"><span class="pre">urllib.parse.parse_qsl()</span></code></a></span><span class="yiyi-st" id="yiyi-108">这里维护它只是为了向后兼容。</span></p></dd></dl><dl class="function"><dt id="cgi.parse_multipart"><span class="yiyi-st" id="yiyi-109"> <code class="descclassname">cgi.</code><code class="descname">parse_multipart</code><span class="sig-paren">(</span><em>fp</em>, <em>pdict</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-110">解析类型<em class="mimetype">multipart / form-data</em>的输入(用于文件上传)。</span><span class="yiyi-st" id="yiyi-111">对于包含<em class="mailheader">Content-Type</em>头中的其他参数的字典,参数为输入文件的<em>fp</em><em>pdict</em></span></p><p><span class="yiyi-st" id="yiyi-112">返回一个字典就像<a class="reference internal" href="urllib.parse.html#urllib.parse.parse_qs" title="urllib.parse.parse_qs"><code class="xref py py-func docutils literal"><span class="pre">urllib.parse.parse_qs()</span></code></a>键是字段名,每个值都是该字段的值列表。</span><span class="yiyi-st" id="yiyi-113">这是很容易使用,但不是很好,如果你期望兆字节上传 - 在这种情况下,使用<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code>类,而不是更灵活。</span></p><p><span class="yiyi-st" id="yiyi-114">请注意,这不会解析嵌套的多部分零件 - 使用<code class="xref py py-class docutils literal"><span class="pre">FieldStorage</span></code></span></p></dd></dl><dl class="function"><dt id="cgi.parse_header"><span class="yiyi-st" id="yiyi-115"> <code class="descclassname">cgi.</code><code class="descname">parse_header</code><span class="sig-paren">(</span><em>string</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-116">将MIME标头例如<em class="mailheader">Content-Type</em>)解析为主值和参数字典。</span></p></dd></dl><dl class="function"><dt id="cgi.test"><span class="yiyi-st" id="yiyi-117"> <code class="descclassname">cgi.</code><code class="descname">test</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-118">稳健的测试CGI脚本可用作主程序。</span><span class="yiyi-st" id="yiyi-119">以HTML格式写入最少的HTTP标头并格式化提供给脚本的所有信息。</span></p></dd></dl><dl class="function"><dt id="cgi.print_environ"><span class="yiyi-st" id="yiyi-120"> <code class="descclassname">cgi.</code><code class="descname">print_environ</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-121">在HTML中格式化shell环境。</span></p></dd></dl><dl class="function"><dt id="cgi.print_form"><span class="yiyi-st" id="yiyi-122"> <code class="descclassname">cgi.</code><code class="descname">print_form</code><span class="sig-paren">(</span><em>form</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-123">在HTML中格式化表单。</span></p></dd></dl><dl class="function"><dt id="cgi.print_directory"><span class="yiyi-st" id="yiyi-124"> <code class="descclassname">cgi.</code><code class="descname">print_directory</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-125">使用HTML格式化当前目录。</span></p></dd></dl><dl class="function"><dt id="cgi.print_environ_usage"><span class="yiyi-st" id="yiyi-126"> <code class="descclassname">cgi.</code><code class="descname">print_environ_usage</code><span class="sig-paren">(</span><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-127">在HTML中打印有用由CGI使用环境变量的列表。</span></p></dd></dl><dl class="function"><dt id="cgi.escape"><span class="yiyi-st" id="yiyi-128"> <code class="descclassname">cgi.</code><code class="descname">escape</code><span class="sig-paren">(</span><em>s</em>, <em>quote=False</em><span class="sig-paren">)</span></span></dt><dd><p><span class="yiyi-st" id="yiyi-129">将字符串<em>s</em>中的字符<code class="docutils literal"><span class="pre">'&amp;'</span></code><code class="docutils literal"><span class="pre">'&lt;'</span></code><code class="docutils literal"><span class="pre">'&gt;'</span></code></span><span class="yiyi-st" id="yiyi-130">如果您需要显示可能在HTML中包含此类字符的文本请使用此选项。</span><span class="yiyi-st" id="yiyi-131">If the optional flag <em>quote</em> is true, the quotation mark character (<code class="docutils literal"><span class="pre">"</span></code>) is also translated; this helps for inclusion in an HTML attribute value delimited by double quotes, as in <code class="docutils literal"><span class="pre">&lt;a</span> <span class="pre">href="..."&gt;</span></code>. </span><span class="yiyi-st" id="yiyi-132">请注意,单引号从不翻译。</span></p><div class="deprecated"><p><span class="yiyi-st" id="yiyi-133"><span class="versionmodified">自版本3.2后已弃用:</span>此函数不安全,因为默认情况下<em>quote</em>为false因此已弃用。</span><span class="yiyi-st" id="yiyi-134">请改用<a class="reference internal" href="html.html#html.escape" title="html.escape"><code class="xref py py-func docutils literal"><span class="pre">html.escape()</span></code></a></span></p></div></dd></dl></div><div class="section" id="caring-about-security"><h2><span class="yiyi-st" id="yiyi-135">21.2.5. </span><span class="yiyi-st" id="yiyi-136">关心安全</span></h2><p id="index-1"><span class="yiyi-st" id="yiyi-137">有一个重要的规则:如果你调用一个外部程序(通过<a class="reference internal" href="os.html#os.system" title="os.system"><code class="xref py py-func docutils literal"><span class="pre">os.system()</span></code></a><a class="reference internal" href="os.html#os.popen" title="os.popen"><code class="xref py py-func docutils literal"><span class="pre">os.popen()</span></code></a>函数。</span><span class="yiyi-st" id="yiyi-138">None</span><span class="yiyi-st" id="yiyi-139">None</span><span class="yiyi-st" id="yiyi-140">即使部分URL或字段名称不可信任因为请求不必来自您的表单</span></p><p><span class="yiyi-st" id="yiyi-141">为了安全起见如果必须将从表单获取的字符串传递给shell命令则应确保字符串只包含字母数字字符破折号下划线和句点。</span></p></div><div class="section" id="installing-your-cgi-script-on-a-unix-system"><h2><span class="yiyi-st" id="yiyi-142">21.2.6. </span><span class="yiyi-st" id="yiyi-143">在Unix系统上安装CGI脚本</span></h2><p><span class="yiyi-st" id="yiyi-144">阅读您的HTTP服务器的文档并与您的本地系统管理员确定应该安装CGI脚本的目录通常这在服务器树中的目录<code class="file docutils literal"><span class="pre">cgi-bin</span></code>中。</span></p><p><span class="yiyi-st" id="yiyi-145">确保您的脚本是可读的和可执行的“其他”; Unix文件模式应为<code class="docutils literal"><span class="pre">0o755</span></code>八进制(使用<code class="docutils literal"><span class="pre">chmod</span> <span class="pre">0755</span> <span class="pre">filename</span></code>)。</span><span class="yiyi-st" id="yiyi-146">确保脚本的第一行包含<code class="docutils literal"><span class="pre">#!</span></code></span><span class="yiyi-st" id="yiyi-147">从第1列开始然后是Python解释器的路径名for实例</span></p><pre><code class="language-python"><span></span><span class="ch">#!/usr/local/bin/python</span>
</code></pre><p><span class="yiyi-st" id="yiyi-148">确保Python解释器存在并且可由“others”执行。</span></p><p><span class="yiyi-st" id="yiyi-149">确保脚本需要读取或写入的任何文件分别由“其他”读取或写入 - 它们的模式应为<code class="docutils literal"><span class="pre">0o644</span></code>可读,<code class="docutils literal"><span class="pre">0o666</span></code>可写。</span><span class="yiyi-st" id="yiyi-150">这是因为出于安全原因HTTP服务器以用户“nobody”执行脚本而没有任何特殊权限。</span><span class="yiyi-st" id="yiyi-151">它只能读(写,执行)每个人都可以读(写,执行)的文件。</span><span class="yiyi-st" id="yiyi-152">当前目录在执行时也是不同的它通常是服务器的cgi-bin目录和环境变量的集合也不同于你在登录时得到的。</span><span class="yiyi-st" id="yiyi-153">特别是不要依赖shell的可执行文件搜索路径<span class="target" id="index-2"></span> <code class="xref std std-envvar docutils literal"><span class="pre">PATH</span></code>或Python模块搜索路径<span class="target" id="index-3"></span> <a class="reference internal" href="../using/cmdline.html#envvar-PYTHONPATH"><code class="xref std std-envvar docutils literal"><span class="pre">PYTHONPATH</span></code></a></span></p><p><span class="yiyi-st" id="yiyi-154">如果需要从不在Python的默认模块搜索路径的目录加载模块则可以在导入其他模块之前更改脚本中的路径。</span><span class="yiyi-st" id="yiyi-155">例如:</span></p><pre><code class="language-python"><span></span><span class="kn">import</span> <span class="nn">sys</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s2">"/usr/home/joe/lib/python"</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s2">"/usr/local/lib/python"</span><span class="p">)</span>
</code></pre><p><span class="yiyi-st" id="yiyi-156">(这样,最后插入的目录将首先被搜索!)</span></p><p><span class="yiyi-st" id="yiyi-157">非Unix系统的说明将有所不同检查HTTP服务器的文档通常会有一个关于CGI脚本的部分</span></p></div><div class="section" id="testing-your-cgi-script"><h2><span class="yiyi-st" id="yiyi-158">21.2.7. </span><span class="yiyi-st" id="yiyi-159">测试你的CGI脚本</span></h2><p><span class="yiyi-st" id="yiyi-160">不幸的是当您从命令行尝试它时CGI脚本通常不会运行从服务器运行时从命令行完美运行的脚本可能会神秘失败。</span><span class="yiyi-st" id="yiyi-161">有一个原因你仍然应该从命令行测试你的脚本如果它包含语法错误Python解释器将不会执行它HTTP服务器将很可能发送一个神秘的错误给客户端。</span></p><p><span class="yiyi-st" id="yiyi-162">假设你的脚本没有语法错误,但它不工作,你别无选择,只能阅读下一节。</span></p></div><div class="section" id="debugging-cgi-scripts"><h2><span class="yiyi-st" id="yiyi-163">21.2.8. </span><span class="yiyi-st" id="yiyi-164">调试CGI脚本</span></h2><p id="index-4"><span class="yiyi-st" id="yiyi-165">首先,检查小的安装错误 - 仔细阅读上面的部分安装您的CGI脚本可以节省你很多时间。</span><span class="yiyi-st" id="yiyi-166">如果您不知道是否已正确理解安装过程,请尝试将此模块文件(<code class="file docutils literal"><span class="pre">cgi.py</span></code>的副本作为CGI脚本安装。</span><span class="yiyi-st" id="yiyi-167">None</span><span class="yiyi-st" id="yiyi-168">给它正确的模式等,并发送一个请求。</span><span class="yiyi-st" id="yiyi-169">如果它安装在标准的<code class="file docutils literal"><span class="pre">cgi-bin</span></code>目录中则应该可以通过在浏览器中输入以下格式的URL来发送请求</span></p><div class="highlight-none"><div class="highlight"><pre><span></span>http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&amp;addr=At+Home
</pre></div></div><p><span class="yiyi-st" id="yiyi-170">如果这给出类型404的错误则服务器找不到脚本 - 也许您需要将其安装在不同的目录中。</span><span class="yiyi-st" id="yiyi-171">如果它给出另一个错误,有一个安装问题,你应该解决,然后再尝试继续。</span><span class="yiyi-st" id="yiyi-172">如果你得到一个格式良好的环境和表单内容的列表在这个例子中字段应该被列为“addr”值为“At Home”值为“name”值为“Joe Blow”<code class="file docutils literal"><span class="pre">cgi.py</span></code>脚本已正确安装。</span><span class="yiyi-st" id="yiyi-173">如果你对自己的脚本采用相同的过程,你现在应该能够调试它。</span></p><p><span class="yiyi-st" id="yiyi-174">下一步可以从脚本中调用<a class="reference internal" href="#module-cgi" title="cgi: Helpers for running Python scripts via the Common Gateway Interface."><code class="xref py py-mod docutils literal"><span class="pre">cgi</span></code></a>模块的<a class="reference internal" href="test.html#module-test" title="test: Regression tests package containing the testing suite for Python."><code class="xref py py-func docutils literal"><span class="pre">test()</span></code></a>函数:用单个语句替换其主代码</span></p><pre><code class="language-python"><span></span><span class="n">cgi</span><span class="o">.</span><span class="n">test</span><span class="p">()</span>
</code></pre><p><span class="yiyi-st" id="yiyi-175">这应该产生与安装<code class="file docutils literal"><span class="pre">cgi.py</span></code>文件本身相同的结果。</span></p><p><span class="yiyi-st" id="yiyi-176">当一个普通的Python脚本引发一个未处理的异常无论什么原因在模块名称中的打字错误无法打开的文件等</span><span class="yiyi-st" id="yiyi-177">Python解释器打印出一个很好的traceback并退出。</span><span class="yiyi-st" id="yiyi-178">虽然Python解释器仍然会这样做当你的CGI脚本引发异常很可能跟踪将最终在一个HTTP服务器的日志文件或完全丢弃。</span></p><p><span class="yiyi-st" id="yiyi-179">幸运的是,一旦你设法让你的脚本执行<em>一些</em>代码,你可以很容易地使用<a class="reference internal" href="cgitb.html#module-cgitb" title="cgitb: Configurable traceback handler for CGI scripts."><code class="xref py py-mod docutils literal"><span class="pre">cgitb</span></code></a>模块发送回溯到Web浏览器。</span><span class="yiyi-st" id="yiyi-180">如果你还没有这样做,只需添加行:</span></p><pre><code class="language-python"><span></span><span class="kn">import</span> <span class="nn">cgitb</span>
<span class="n">cgitb</span><span class="o">.</span><span class="n">enable</span><span class="p">()</span>
</code></pre><p><span class="yiyi-st" id="yiyi-181">到您的脚本的顶部。</span><span class="yiyi-st" id="yiyi-182">然后尝试再次运行它;当出现问题时,您应该看到一个详细的报告,可能会明显导致崩溃的原因。</span></p><p><span class="yiyi-st" id="yiyi-183">如果您怀疑导入<a class="reference internal" href="cgitb.html#module-cgitb" title="cgitb: Configurable traceback handler for CGI scripts."><code class="xref py py-mod docutils literal"><span class="pre">cgitb</span></code></a>模块时可能会出现问题,您可以使用更稳健的方法(仅使用内建模块):</span></p><pre><code class="language-python"><span></span><span class="kn">import</span> <span class="nn">sys</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stderr</span> <span class="o">=</span> <span class="n">sys</span><span class="o">.</span><span class="n">stdout</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Content-Type: text/plain"</span><span class="p">)</span>
<span class="nb">print</span><span class="p">()</span>
<span class="o">...</span><span class="n">your</span> <span class="n">code</span> <span class="n">here</span><span class="o">...</span>
</code></pre><p><span class="yiyi-st" id="yiyi-184">这依赖于Python解释器来打印traceback。</span><span class="yiyi-st" id="yiyi-185">输出的内容类型设置为纯文本这将禁用所有HTML处理。</span><span class="yiyi-st" id="yiyi-186">如果您的脚本工作原始HTML将由您的客户端显示。</span><span class="yiyi-st" id="yiyi-187">如果引发异常,最有可能在打印前两行之后,将显示回溯。</span><span class="yiyi-st" id="yiyi-188">因为没有HTML解释traceback将是可读的。</span></p></div><div class="section" id="common-problems-and-solutions"><h2><span class="yiyi-st" id="yiyi-189">21.2.9. </span><span class="yiyi-st" id="yiyi-190">常见问题和解决方案</span></h2><ul class="simple"><li><span class="yiyi-st" id="yiyi-191">大多数HTTP服务器缓冲CGI脚本的输出直到脚本完成。</span><span class="yiyi-st" id="yiyi-192">这意味着在脚本运行时无法在客户端的显示器上显示进度报告。</span></li><li><span class="yiyi-st" id="yiyi-193">None</span></li><li><span class="yiyi-st" id="yiyi-194">检查HTTP服务器的日志文件。</span><span class="yiyi-st" id="yiyi-195">(在单独的窗口中可以使用<code class="docutils literal"><span class="pre"></span> <span class="pre">-f</span> <span class="pre">日志文件</span> </code></span></li><li><span class="yiyi-st" id="yiyi-196">始终通过执行类似<code class="docutils literal"><span class="pre">python</span> <span class="pre">script.py</span></code>的脚本来检查语法错误。</span></li><li><span class="yiyi-st" id="yiyi-197">如果脚本没有任何语法错误,请尝试将<code class="docutils literal"><span class="pre">import</span> <span class="pre">cgitb</span> <span class="pre">cgitb.enable()</span> 脚本的顶部。</code></span></li><li><span class="yiyi-st" id="yiyi-198">当调用外部程序时,请确保找到它们。</span><span class="yiyi-st" id="yiyi-199">通常,这意味着使用绝对路径名 - <span class="target" id="index-5"></span> <code class="xref std std-envvar docutils literal"><span class="pre">PATH</span></code>通常不会在CGI脚本中设置为非常有用的值。</span></li><li><span class="yiyi-st" id="yiyi-200">When reading or writing external files, make sure they can be read or written by the userid under which your CGI script will be running: this is typically the userid under which the web server is running, or some explicitly specified userid for a web servers <code class="docutils literal"><span class="pre">suexec</span></code> feature.</span></li><li><span class="yiyi-st" id="yiyi-201">不要试图给一个CGI脚本set-uid模式。</span><span class="yiyi-st" id="yiyi-202">None</span></li></ul><p class="rubric"><span class="yiyi-st" id="yiyi-203">脚注</span></p><table class="docutils footnote" frame="void" id="id3" rules="none"><tbody valign="top"><tr><td class="label"><span class="yiyi-st" id="yiyi-204"><a class="fn-backref" href="#id2">[1]</a></span></td><td><span class="yiyi-st" id="yiyi-205">请注意HTML规范的一些最新版本确实应该提供字段值的顺序但是知道是否从一个符合的浏览器或者甚至从一个浏览器接收到一个请求是冗长和容易出错的。</span></td></tr></tbody></table></div></div></div>