源码时代软测干货分享|带你了解cgi支持模块

IT-source IT-source     2022-12-15     556

关键词:

该模块定义了许多由Python编写的CGI脚本使用的实用程序。

 

21.2.1。介绍

HTTP服务器调用CGI脚本,通常用于处理通过HTML <FORM>或<ISINDEX>元素提交的用户输入。

 

通常,CGI脚本存在于服务器的特殊cgi-bin目录中。 HTTP服务器在脚本的shell环境中放置有关请求的各种信息(例如客户端的主机名,请求的URL,查询字符串和许多其他好东西),执行脚本,并发送脚本的输出回到客户端。

 

脚本的输入也连接到客户端,有时表单数据以这种方式读取;在其他时候,表单数据通过URL的部分字符串传递。此模块旨在处理不同的情况,并为Python脚本提供更简单的界面。它还提供了许多帮助调试脚本的实用程序,最新增加的是对表单中文件上载的支持(如果您的浏览器支持它)。

 

CGI脚本的输出应由两部分组成,用空行分隔。第一部分包含许多标题,告诉客户端正在跟踪哪种数据。生成最小标题部分的Python代码如下所示:

 

print("Content-Type: text/html") # HTML is following print() # blank line, end of headers

 

第二部分通常是HTML,它允许客户端软件显示带有标题,内联图像等的格式良好的文本。这里的Python代码可以打印一段简单的HTML:

print("<TITLE>CGI script output</TITLE>") print("<H1>This is my first CGI script</H1>") print("Hello, world!") 

 

21.2.2。使用cgi模块

首先编写import cgi。

 

编写新脚本时,请考虑添加以下行:

 

导入cgitb

cgitb.enable()

这将激活一个特殊的异常处理程序,如果发生任何错误,它将在Web浏览器中显示详细的报告。如果你不想向你的脚本用户展示程序的内容,你可以将报告保存到文件中,代码如下:

 

导入cgitb

cgitb.enable(display = 0,logdir =“/ path / to / logdir”)

 

在脚本开发期间使用此功能非常有用。 cgitb生成的报告提供的信息可以为您节省大量时间来查找错误。您可以随后在测试脚本后删除cgitb行,并确信它可以正常工作。

 

要获取提交的表单数据,请使用FieldStorage类。如果表单包含非ASCII字符,请使用encoding关键字参数设置为为文档定义的编码值。它通常包含在HTML文档的HEAD部分的META标记中,或者包含在Content-Type标题中。这将从标准输入或环境中读取表单内容(取决于根据CGI标准设置的各种环境变量的值)。由于它可能消耗标准输入,因此应仅实例化一次。

 

FieldStorage实例可以像Python字典一样编入索引。 它允许使用in运算符进行成员资格测试,并且还支持标准字典方法keys()和内置函数len()。 包含空字符串的表单字段将被忽略,并且不会出现在字典中; 要保留此类值,请在创建FieldStorage实例时为可选的keep_blank_values关键字参数提供true值。

 

例如,以下代码(假定已打印Content-Type标题和空行)检查字段名称和addr是否都设置为非空字符串:

 

form = cgi.FieldStorage() if "name" not in form or "addr" not in form: print("<H1>Error</H1>") print("Please fill in the name and addr fields.") return print("<p>name:", form["name"].value) print("<p>addr:", form["addr"].value) ...further form processing here...

 

这里通过form [key]访问的字段本身就是FieldStorage(或MiniFieldStorage,取决于表单编码)的实例。 实例的value属性产生字段的字符串值。 getvalue()方法直接返回此字符串值; 它还接受一个可选的第二个参数作为默认值,如果请求的密钥不存在则返回。

 

如果提交的表单数据包含多个具有相同名称的字段,则由form [key]检索的对象不是FieldStorage或MiniFieldStorage实例,而是此类实例的列表。 同样,在这种情况下,form.getvalue(key)将返回一个字符串列表。 如果您期望这种可能性(当您的HTML表单包含多个具有相同名称的字段时),请使用getlist()方法,该方法始终返回值列表(这样您就不需要特殊情况下的单项案例)。 例如,此代码连接任意数量的用户名字段,以逗号分隔:

 

value = form.getlist("username") usernames = ",".join(value)

 

如果字段表示上载的文件,则通过value属性或getvalue()方法访问该值会将内存中的整个文件作为字节读取。 这可能不是你想要的。 您可以通过测试filename属性或file属性来测试上载的文件。 然后,您可以在文件属性自动关闭之前读取数据,作为FieldStorage实例的垃圾收集的一部分(read()和readline()方法将返回字节):

 

fileitem = form["userfile"] if fileitem.file: # It's an uploaded file; count lines linecount = 0 while True: line = fileitem.file.readline() if not line: break linecount = linecount + 1

 

 

FieldStorage对象还支持在with语句中使用,该语句在完成后将自动关闭它们。

 

如果在获取上载文件的内容时遇到错误(例如,当用户通过单击“返回”或“取消”按钮中断表单提交时),该字段的对象的done属性将设置为值-1 。

 

文件上载草案标准可以从一个字段上传多个文件(使用递归的multipart / *编码)。发生这种情况时,该项目将是类似字典的FieldStorage项目。这可以通过测试其type属性来确定,该属性应该是multipart / form-data(或者可能是另一个匹配multipart / *的MIME类型)。在这种情况下,它可以像顶层表单对象一样递归迭代。

 

当表单以搊ld格式提交时(作为查询字符串或类型为application / x-www-form-urlencoded的单个数据部分),这些项实际上将是MiniFieldStorage类的实例。在这种情况下,list,file和filename属性始终为None。

 

通过POST提交的同时具有查询字符串的表单将包含FieldStorage和MiniFieldStorage项目。

 

在3.4版中更改:在创建FieldStorage实例的垃圾回收时,文件属性自动关闭。

 

在3.5版中更改:向FieldStorage类添加了对上下文管理协议的支持

 

 

21.2.3。 更高级别的界面

上一节介绍了如何使用FieldStorage类读取CGI表单数据。 本节描述了一个更高级别的接口,该接口已添加到此类中,以允许用户以更易读和更直观的方式执行此操作。 该接口不会使前面部分中描述的技术过时 - 例如,它们对于有效地处理文件上载仍然有用。

 

界面由两个简单的方法组成。 使用这些方法,您可以以通用方式处理表单数据,而无需担心是否只在一个名称下发布了一个或多个值。

 

在上一节中,您学习了在您希望用户在一个名称下发布多个值时随时编写以下代码:

 

item = form.getvalue("item") if isinstance(item, list): # The user is requesting more than one item. else: # The user is requesting only one item.

 

这种情况很常见,例如,当表单包含一组具有相同名称的多个复选框时:

<input type="checkbox" name="item" value="1" /> <input type="checkbox" name="item" value="2" />

 

但是,在大多数情况下,表单中只有一个具有特定名称的表单控件,然后您只需要一个与此名称关联的值。 所以你编写一个包含例如这段代码的脚本:

user = form.getvalue("user").upper()

 

代码的问题在于,您永远不应期望客户端会为您的脚本提供有效的输入。例如,如果一个好奇的用户将另一个user = foo对附加到查询字符串,那么脚本将崩溃,因为在这种情况下,getvalue(“user”)方法调用返回一个列表而不是一个字符串。在列表上调用upper()方法无效(因为列表没有此名称的方法)并导致AttributeError异常。

 

因此,读取表单数据值的适当方法是始终使用检查所获取的值是单个值还是值列表的代码。这很烦人,导致脚本可读性降低。

 

更方便的方法是使用此更高级别接口提供的方法getfirst()和getlist()。

 

FieldStorage.getfirst(name,default = None)

此方法始终只返回与表单字段名称关联的一个值。如果在此名称下发布了更多值,则该方法仅返回第一个值。请注意,收到这些值的顺序可能因浏览器而异,因此不应计算在内。 [1]如果不存在此类表单字段或值,则该方法返回由可选参数default指定的值。如果未指定,此参数默认为“无”。

 

FieldStorage.getlist(名称)

此方法始终返回与表单字段名称关联的值列表。如果name不存在此类表单字段或值,则该方法返回空列表。如果只存在一个这样的值,它返回一个由一个项组成的列表。

 

使用这些方法,您可以编写漂亮的紧凑代码:

import cgi form = cgi.FieldStorage() user = form.getfirst("user", "").upper() # This way it's safe. for item in form.getlist("item"): do_something(item) 

 

21.2.4。功能

如果您想要更多控制,或者如果您想在其他情况下使用此模块中实现的某些算法,这些非常有用。

 

cgi.parse(fp = None,environ = os.environ,keep_blank_values = False,strict_parsing = False)

在环境中或从文件中解析查询(文件默认为sys.stdin)。 keep_blank_values和strict_parsing参数传递给urllib.parse.parse_qs()不变。

 

cgi.parse_qs(qs,keep_blank_values = False,strict_parsing = False)

此模块中不推荐使用此功能。请改用urllib.parse.parse_qs()。它仅在向后兼容时保留在此处。

 

cgi.parse_qsl(qs,keep_blank_values = False,strict_parsing = False)

此模块中不推荐使用此功能。请改用urllib.parse.parse_qsl()。它仅在向后兼容时保留在此处。

 

cgi.parse_multipart(fp,pdict)

解析multipart / form-data类型的输入(用于文件上传)。参数是输入文件的fp和包含Content-Type标头中其他参数的字典的pdict。

 

返回字典就像urllib.parse.parse_qs()键是字段名称,每个值都是该字段的值列表。这很容易使用,但如果您希望上传兆字节not不是很好 在这种情况下,使用FieldStorage类而不是更灵活。

 

请注意,这不会解析嵌套的多部分 使用FieldStorage。

 

cgi.parse_header(串)

将MIME标头(例如Content-Type)解析为主值和参数字典。

 

cgi.test()

强大的测试CGI脚本,可用作主程序。写入最小的HTTP标头并格式化以HTML格式提供给脚本的所有信息。

 

cgi.print_environ()

以HTML格式化shell环境。

 

cgi.print_form(形式)的

以HTML格式化表单。

 

cgi.print_directory()

以HTML格式化当前目录。

 

cgi.print_environ_usage()

在HTML中打印有用(由CGI使用)环境变量的列表。

 

cgi.escape(s,quote = False)

将字符串s中的字符'&','<'和'>'转换为HTML安全序列。 如果需要在HTML中显示可能包含此类字符的文本,请使用此选项。 如果可选标记引用为true,则引号字符(“)也会被翻译;这有助于包含在由双引号分隔的HTML属性值中,如<a href="...">中所示。请注意单引号 从未翻译过。

 

从版本3.2开始不推荐使用:此函数不安全,因为默认情况下quote为false,因此不推荐使用。 请改用html.escape()。

 

 

21.2.5。关心安全

有一个重要的规则:如果你调用一个外部程序(通过os.system()或os.popen()函数或其他具有类似功能的函数),请确保你不要将从客户端收到的任意字符串传递给贝壳。这是一个众所周知的安全漏洞,Web上任何地方的聪明黑客都可以利用一个容易上当的CGI脚本来调用任意shell命令。甚至部分URL或字段名称也不可信,因为请求不必来自您的表单!

 

为了安全起见,如果必须将从表单获取的字符串传递给shell命令,则应确保该字符串仅包含字母数字字符,短划线,下划线和句点。

 

21.2.6。在Unix系统上安装CGI脚本

阅读HTTP服务器的文档,并与本地系统管理员联系,找到应安装CGI脚本的目录;通常这是在服务器树中的目录cgi-bin中。

 

确保您的脚本可由搊thers 读取并执行; Unix文件模式应该是0o755八进制(使用chmod 0755文件名)。确保脚本的第一行包含#!从第1列开始,后跟Python解释器的路径名,例如:

#!/usr/local/bin/python

 

确保Python解释器存在并且可由搊thers 执行。

 

确保脚本需要读取或写入的任何文件分别是可读或可写的搊thers 他们的模式应为0o644表示可读,0o666表示可写。这是因为出于安全原因,HTTP服务器以“用户”的形式执行您的脚本,没有任何特殊权限。它只能读取(写入,执行)每个人都可以读取(写入,执行)的文件。执行时的当前目录也不同(通常是服务器的cgi-bin目录),环境变量集也与登录时的不同。特别是,不要指望shell的搜索可执行文件的路径(PATH)或Python模块搜索路径(PYTHONPATH)设置为任何有趣的东西。

 

如果需要从不在Python的默认模块搜索路径上的目录中加载模块,则可以在导入其他模块之前更改脚本中的路径。例如:

import sys sys.path.insert(0, "/usr/home/joe/lib/python") sys.path.insert(0, "/usr/local/lib/python")

 

(这样,将首先搜索最后插入的目录!)

 

非Unix系统的说明会有所不同; 检查你的HTTP服务器的文档(它通常有一个关于CGI脚本的部分)。

 

21.2.7。测试您的CGI脚本

遗憾的是,当您从命令行尝试CGI脚本时,它通常不会运行,并且从服务器运行时,从命令行完美运行的脚本可能会神秘地失败。你应该仍然从命令行测试你的脚本有一个原因:如果它包含语法错误,Python解释器根本不会执行它,并且HTTP服务器很可能会向客户端发送一个神秘的错误。

 

假设您的脚本没有语法错误,但它不起作用,您别无选择,只能阅读下一节。

 

21.2.8。调试CGI脚本

首先,检查琐碎的安装错误 阅读上面有关安装CGI脚本的部分可以为您节省大量时间。如果您想知道是否已正确理解安装过程,请尝试将此模块文件(cgi.py)的副本安装为CGI脚本。当作为脚本调用时,该文件将以HTML格式转储其环境和表单内容。给它正确的模式等,并发送一个请求。如果它安装在标准的cgi-bin目录中,则应该可以通过在表单的浏览器中输入URL来向它发送请求:

http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home

 

如果这给出404类型的错误,则服务器找不到脚本 可能需要将其安装在不同的目录中。 如果它给出了另一个错误,那么在尝试进一步操作之前,您应该修复一个安装问题。 如果你得到一个格式良好的环境和表单内容列表(在这个例子中,字段应该列为揳ddr ,值为 tHome 和“非ame ,值为揓oBlow ”),cgi.py脚本 已正确安装。 如果对自己的脚本执行相同的过程,则现在应该可以对其进行调试。

 

下一步可能是从脚本中调用cgi模块的test()函数:将其主代码替换为单个语句

cgi.test() 这应该产生与安装cgi.py文件本身相同的结果。

 

当普通的Python脚本引发一个未处理的异常时(无论出于何种原因:模块名称中的拼写错误,可以打开的文件等),Python解释器会打印出一个很好的回溯并退出。 虽然Python解释器仍然会在CGI脚本引发异常时执行此操作,但很可能回溯将最终出现在HTTP服务器的一个日志文件中,或者完全丢弃。

 

幸运的是,一旦您设法让脚本执行某些代码,您就可以使用cgitb模块轻松地将回溯发送到Web浏览器。 如果你还没有这样做,只需添加以下行:

import cgitb cgitb.enable()

 

到你的脚本的顶部。 然后尝试再次运行它; 当问题发生时,您应该看到一个详细的报告,可能会显示崩溃的原因。

 

如果您怀疑导入cgitb模块时可能存在问题,则可以使用更强大的方法(仅使用内置模块):

import sys sys.stderr = sys.stdout print("Content-Type: text/plain") print() ...your code here...

 

这依赖于Python解释器来打印回溯。 输出的内容类型设置为纯文本,禁用所有HTML处理。 如果您的脚本有效,则客户端将显示原始HTML。 如果它引发异常,很可能在打印前两行之后,将显示回溯。 由于没有进行HTML解释,因此回溯将是可读的。

 

21.2.9。常见问题和解决方案

大多数HTTP服务器缓冲CGI脚本的输出,直到脚本完成。这意味着在脚本运行时无法在客户端显示器上显示进度报告。

检查上面的安装说明。

检查HTTP服务器的日志文件。 (在单独的窗口中使用tail -f logfile可能很有用!)

通过执行python script.py之类的操作,首先检查脚本是否存在语法错误。

如果您的脚本没有任何语法错误,请尝试添加import cgitb; cgitb.enable()到脚本的顶部。

调用外部程序时,请确保找到它们。通常,这意味着使用绝对路径名 PATH通常不会在CGI脚本中设置为非常有用的值。

在读取或写入外部文件时,请确保它们可以由运行CGI脚本的用户标识读取或写入:这通常是运行Web服务器的用户标识,或者是Web服务器的一些明确指定的用户标识。 suexec功能。

不要试图给CGI脚本一个set-uid模式。这对大多数系统都不起作用,也是一种安全责任。

源码时代java干货分享|带你了解原型模式_prototype

原型模式概述在java中创建一个对象通过new是非常繁琐的,需要创建检查权限,做一些数据准备等等,因此也会销毁系统性能,那么想要快快速简单的创建出对象,我们可以通过原型模式实现,以某个对象... 查看详情

源码时代软测干货分享|jmeter性能测试实战(上)(代码片段)

确认被测项目的性能指标1.1性能测试指标性能测试指标,是性能测试中重要的测试依据,相当于测试用例中的预期结果,由于性能测试属于非功能性测试,在规范的项目中有专门的非功能性需求,但是在互联网高速发展的今天,为了早一... 查看详情

源码时代软测干货分享|今天来说说测试转向敏捷路线

尽管跳上敏捷的潮流对企业来说很有诱惑力,但这并不总是那么容易,向敏捷的转变很可能伴随着的是测试方面的一系列挑战。为了使敏捷能够快速交付高质量的产品,测试必须比以往更早地开始介入。 今天,... 查看详情

源码时代java干货分享|带你认识zookeeper分布式服务框架

本质它是一个分布式服务框架,是ApacheHadoop的一个子项目提供一种集中式信息存储服务,用于解决分布式应用中的一些数据管理问题简单来说zookeeper=文件系统+监听通知机制。特点数据存在内存中类似文件系统的树... 查看详情

源码时代java干货分享|手把手教你用maven构建docker镜像

一、原理分析由于我们开发pc上面没有docker,所以没办法直接打包镜像,所以要调用远程主机docker进行。并且远程主机docker默认不支持远程访问,所以需要开启远程。然后我们在项目中以dockermaven插件方式进行打包镜... 查看详情

源码时代前端干货分享|javascript编程优化,超强入门必备技能!

一、按强类型风格定义变量大家要知道js是一种弱类型语言,定义变量的时候不要太随意,大多数初学者定义变量时,只是声明变量而没有赋值,这样就显得太随意了声明变量的时候,一定要指明变量的类型,告诉解析器,你要... 查看详情

源码时代前端干货分享|swoole开启后无法访问问题

首先说一下环境,由于swoole只能在linux环境中使用,开发者通常使用的windows系统,所以我的swoole是安装在Cygwin环境中的。Cygwin可以在windows系统中运行,提供一个类似linux命令行的环境。同时,也虚拟机不同ÿ... 查看详情

源码时代java干货分享|springsecurity快速上手秘籍(代码片段)

前言至今Java能够如此的火爆Spring做出了很大的贡献,它的出现让Java程序的编写更为简单灵活,而Spring如今也形成了自己的生态圈,今天咱们探讨的是Spring旗下的一个款认证工具:SpringSecurity,如今认证框架主流“sh... 查看详情

源码时代前端干货分享|new关键字底层原理-执行过程

构造对象关键字:new众所周知,js是用new来产生对象的,那么今天我们就来说说js里new产生对象的构造原理。//人类构造函数functionPeople()People.prototype.index=1;//在原型上绑定一个index属性,值为1varp1=newPeople();//... 查看详情

源码时代ui干货分享|axure基础教程

主界面介绍知识重点一、页面的切换热区:就是可做交互的区域。动态面板:多种状态,可以反复切换。第一步:鼠标选择热区,鼠标左键拖拽到画布中,调整到合适位置,合适大小第二步:在图... 查看详情

源码时代前端干货分享|从零动手封装一个通用的vue按钮组件

我们在使用目前最主流的前端框架vue在开发过程中,组件是一个非常重要的组成部分,可以这么说,所有的vue应用,都是由一个一个的小组件拼装而成的。正是由于vue组件如此重要,所以vue的生态中,也非常多的UI组件库,其中... 查看详情

结合rocketmq源码,带你了解并发编程的三大神器(代码片段)

摘要:本文结合RocketMQ源码,分享并发编程三大神器的相关知识点。本文分享自华为云社区《读RocketMQ源码,学习并发编程三大神器》,作者:勇哥java实战分享。这篇文章,笔者结合RocketMQ源码,分享并... 查看详情

源码时代java干货分享|手把手教你springboot配置ssl证书(代码片段)

图说明第一步首先去阿里云弄一个免费的SSL证书下载然后放到项目里面的resource路径下这里一定要注意是key-store和key-store-password我在配置时写出了key-password弄了很久没找到原因换成了nginx去配置,最近还是嫌弃服务启动太多改... 查看详情

源码时代前端干货分享|new关键字底层原理-执行过程

构造对象关键字:new众所周知,js是用new来产生对象的,那么今天我们就来说说js里new产生对象的构造原理。//人类构造函数functionPeople()People.prototype.index=1;//在原型上绑定一个index属性,值为1varp1=newPeople();//... 查看详情

干货分享腾讯出品android插件化开发指南+项目实战(附源码)

...术团队最新出品的《高级Android插件化强化实战》,附源码解析,如下文所示👇需要者可点击文末卡片领取~《高级Android插件化强化实战(附源码)》第一章插件化技术的前世今生插件化提要插件化发展历史​... 查看详情

「干货分享」模块化编程和maven配置实践一则

?封面说到模块化编程,对我个人而言首先起因于团队协作的需要,也就是组织架构结构特点来决定,而不是跟风求得自我认同,看看我们团队的组织结构:? 其中:基础平台部职责:1、AI实验室:语音,图像识别算法持续优... 查看详情

带你了解node.js包管理工具:包与npm(代码片段)

...xff0c;也使得第三方文件模块可以有序的编写和使用。本文分享自华为云社区《NodeJs深入浅出之旅:包与NPM》,作者:空城机。包与NPMNode组织了自身的核心模块,也使得第三方文件模块可以有序的编写和使用。但是... 查看详情

一文带你了解单元测试和基准测试干货(代码片段)

 在平时,通过代码实战部分你可以看到,在写每个功能的时候,都会编写测试代码。那是因为TDD(Test-DrivenDevelopment,测试驱动开发)中提倡先编写测试代码,然后再编写功能代码,每做一个修改... 查看详情