-
停止运行此脚本吗 web浏览器运行速度慢_C++ Web 编程
2020-12-01 05:40:17公共网关接口(CGI),是一套标准,定义了信息是如何在 Web 服务器和客户端脚本之间进行交换的。CGI 规范目前是由 NCSA 维护的,NCSA 定义 CGI 如下:公共网关接口(CGI),是一种用于外部网关程序与信息服务器(如 ...什么是 CGI?
- 公共网关接口(CGI),是一套标准,定义了信息是如何在 Web 服务器和客户端脚本之间进行交换的。
- CGI 规范目前是由 NCSA 维护的,NCSA 定义 CGI 如下:
- 公共网关接口(CGI),是一种用于外部网关程序与信息服务器(如 HTTP 服务器)对接的接口标准。
- 目前的版本是 CGI/1.1,CGI/1.2 版本正在推进中。
Web 浏览
为了更好地了解 CGI 的概念,让我们点击一个超链接,浏览一个特定的网页或 URL,看看会发生什么。
- 您的浏览器联系上 HTTP Web 服务器,并请求 URL,即文件名。
- Web 服务器将解析 URL,并查找文件名。如果找到请求的文件,Web 服务器会把文件发送回浏览器,否则发送一条错误消息,表明您请求了一个错误的文件。
- Web 浏览器从 Web 服务器获取响应,并根据接收到的响应来显示文件或错误消息。
然而,以这种方式搭建起来的 HTTP 服务器,不管何时请求目录中的某个文件,HTTP 服务器发送回来的不是该文件,而是以程序形式执行,并把执行产生的输出发送回浏览器显示出来。
公共网关接口(CGI),是使得应用程序(称为 CGI 程序或 CGI 脚本)能够与 Web 服务器以及客户端进行交互的标准协议。这些 CGI 程序可以用 Python、PERL、Shell、C 或 C++ 等进行编写。
CGI 架构图
下图演示了 CGI 的架构:
Web 服务器配置
在您进行 CGI 编程之前,请确保您的 Web 服务器支持 CGI,并已配置成可以处理 CGI 程序。所有由 HTTP 服务器执行的 CGI 程序,都必须在预配置的目录中。该目录称为 CGI 目录,按照惯例命名为 /var/www/cgi-bin。虽然 CGI 文件是 C++ 可执行文件,但是按照惯例它的扩展名是 .cgi。
默认情况下,Apache Web 服务器会配置在 /var/www/cgi-bin 中运行 CGI 程序。如果您想指定其他目录来运行 CGI 脚本,您可以在 httpd.conf 文件中修改以下部分:
<Directory "/var/www/cgi-bin"> AllowOverride None Options ExecCGI Order allow,deny Allow from all </Directory> <Directory "/var/www/cgi-bin"> Options All </Directory>
在这里,我们假设已经配置好 Web 服务器并能成功运行,你可以运行任意的 CGI 程序,比如 Perl 或 Shell 等。
第一个 CGI 程序
请看下面的 C++ 程序:
实例
#include <iostream> using namespace std; int main () { cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>Hello World - 第一个 CGI 程序</title>n"; cout << "</head>n"; cout << "<body>n"; cout << "<h2>Hello World! 这是我的第一个 CGI 程序</h2>n"; cout << "</body>n"; cout << "</html>n"; return 0; }编译上面的代码,把可执行文件命名为 cplusplus.cgi,并把这个文件保存在 /var/www/cgi-bin 目录中。在运行 CGI 程序之前,请使用 chmod 755 cplusplus.cgi UNIX 命令来修改文件模式,确保文件可执行。访问可执行文件,您会看到下面的输出:
Hello World! 这是我的第一个 CGI 程序
上面的 C++ 程序是一个简单的程序,把它的输出写在 STDOUT 文件上,即显示在屏幕上。在这里,值得注意一点,第一行输出 Content-type:text/htmlrnrn。这一行发送回浏览器,并指定要显示在浏览器窗口上的内容类型。您必须理解 CGI 的基本概念,这样才能进一步使用 Python 编写更多复杂的 CGI 程序。C++ CGI 程序可以与任何其他外部的系统(如 RDBMS)进行交互。
HTTP 头信息
行 Content-type:text/htmlrnrn 是 HTTP 头信息的组成部分,它被发送到浏览器,以便更好地理解页面内容。HTTP 头信息的形式如下:
HTTP 字段名称: 字段内容 例如 Content-type: text/htmlrnrn
还有一些其他的重要的 HTTP 头信息,这些在您的 CGI 编程中都会经常被用到。
头信息描述Content-type:MIME 字符串,定义返回的文件格式。例如 Content-type:text/html。Expires: Date信息变成无效的日期。浏览器使用它来判断一个页面何时需要刷新。一个有效的日期字符串的格式应为 01 Jan 1998 12:00:00 GMT。Location: URL这个 URL 是指应该返回的 URL,而不是请求的 URL。你可以使用它来重定向一个请求到任意的文件。Last-modified: Date资源的最后修改日期。Content-length: N要返回的数据的长度,以字节为单位。浏览器使用这个值来表示一个文件的预计下载时间。Set-Cookie: String通过string设置 cookie。
CGI 环境变量
所有的 CGI 程序都可以访问下列的环境变量。这些变量在编写 CGI 程序时扮演了非常重要的角色。
变量名描述CONTENT_TYPE内容的数据类型。当客户端向服务器发送附加内容时使用。例如,文件上传等功能。CONTENT_LENGTH查询的信息长度。只对 POST 请求可用。HTTP_COOKIE以键 & 值对的形式返回设置的 cookies。HTTP_USER_AGENT用户代理请求标头字段,递交用户发起请求的有关信息,包含了浏览器的名称、版本和其他平台性的附加信息。PATH_INFOCGI 脚本的路径。QUERY_STRING通过 GET 方法发送请求时的 URL 编码信息,包含 URL 中问号后面的参数。REMOTE_ADDR发出请求的远程主机的 IP 地址。这在日志记录和认证时是非常有用的。REMOTE_HOST发出请求的主机的完全限定名称。如果此信息不可用,则可以用 REMOTE_ADDR 来获取 IP 地址。REQUEST_METHOD用于发出请求的方法。最常见的方法是 GET 和 POST。SCRIPT_FILENAMECGI 脚本的完整路径。SCRIPT_NAMECGI 脚本的名称。SERVER_NAME服务器的主机名或 IP 地址。SERVER_SOFTWARE服务器上运行的软件的名称和版本。
下面的 CGI 程序列出了所有的 CGI 变量。
实例
#include <iostream> #include <stdlib.h> #include <string> using namespace std; const string ENV[ 24 ] = { "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_USER_AGENT", "PATH", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN", "SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL", "SERVER_SIGNATURE","SERVER_SOFTWARE" }; int main () { cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>CGI 环境变量</title>n"; cout << "</head>n"; cout << "<body>n"; cout << "<table border = "0" cellspacing = "2">"; for ( int i = 0; i < 24; i++ ) { cout << "<tr><td>" << ENV[ i ] << "</td><td>"; // 尝试检索环境变量的值 char *value = getenv( ENV[ i ].c_str() ); if ( value != 0 ){ cout << value; }else{ cout << "环境变量不存在。"; } cout << "</td></tr>n"; } cout << "</table><n"; cout << "</body>n"; cout << "</html>n"; return 0; }C++ CGI 库
在真实的实例中,您需要通过 CGI 程序执行许多操作。这里有一个专为 C++ 程序而编写的 CGI 库,我们可以从 ftp://ftp.gnu.org/gnu/cgicc/ 上下载这个 CGI 库,并按照下面的步骤安装库:
$ tar xzf cgicc-X.X.X.tar.gz $ cd cgicc-X.X.X/ $ ./configure --prefix=/usr $ make $ make install
注意:libcgicc.so 和 libcgicc.a 库会被安装到/usr/lib目录下,需执行拷贝命令:
$ sudo cp /usr/lib/libcgicc.* /usr/lib64/才能使 CGI 程序自动找到 libcgicc.so 动态链接库。您可以点击 C++ CGI Lib Documentation,查看相关的库文档。
GET 和 POST 方法
您可能有遇到过这样的情况,当您需要从浏览器传递一些信息到 Web 服务器,最后再传到 CGI 程序。通常浏览器会使用两种方法把这个信息传到 Web 服务器,分别是 GET 和 POST 方法。
使用 GET 方法传递信息
GET 方法发送已编码的用户信息追加到页面请求中。页面和已编码信息通过 ? 字符分隔开,如下所示:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET 方法是默认的从浏览器向 Web 服务器传信息的方法,它会在浏览器的地址栏中生成一串很长的字符串。当您向服务器传密码或其他一些敏感信息时,不要使用 GET 方法。GET 方法有大小限制,在一个请求字符串中最多可以传 1024 个字符。
当使用 GET 方法时,是使用 QUERY_STRING http 头来传递信息,在 CGI 程序中可使用 QUERY_STRING 环境变量来访问。
您可以通过在 URL 后跟上简单连接的键值对,也可以通过使用 HTML <FORM> 标签的 GET 方法来传信息。
简单的 URL 实例:Get 方法
下面是一个简单的 URL,使用 GET 方法传递两个值给 hello_get.py 程序。
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
下面的实例生成 cpp_get.cgi CGI 程序,用于处理 Web 浏览器给出的输入。通过使用 C++ CGI 库,可以很容易地访问传递的信息:
实例
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc formData; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>使用 GET 和 POST 方法</title>n"; cout << "</head>n"; cout << "<body>n"; form_iterator fi = formData.getElement("first_name"); if( !fi->isEmpty() && fi != (*formData).end()) { cout << "名:" << **fi << endl; }else{ cout << "No text entered for first name" << endl; } cout << "<br/>n"; fi = formData.getElement("last_name"); if( !fi->isEmpty() &&fi != (*formData).end()) { cout << "姓:" << **fi << endl; }else{ cout << "No text entered for last name" << endl; } cout << "<br/>n"; cout << "</body>n"; cout << "</html>n"; return 0; }现在,编译上面的程序,如下所示:
$g++ -o cpp_get.cgi cpp_get.cpp -lcgicc
生成 cpp_get.cgi,并把它放在 CGI 目录中,并尝试使用下面的链接进行访问:
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
这会产生以下结果:
名:ZARA 姓:ALI
简单的表单实例:GET 方法
下面是一个简单的实例,使用 HTML 表单和提交按钮传递两个值。我们将使用相同的 CGI 脚本 cpp_get.cgi 来处理输入。
<form action="/cgi-bin/cpp_get.cgi" method="get"> 名:<input type="text" name="first_name"> <br /> 姓:<input type="text" name="last_name" /> <input type="submit" value="提交" /> </form>
下面是上述表单的实际输出,请输入名和姓,然后点击提交按钮查看结果。
使用 POST 方法传递信息
一个更可靠的向 CGI 程序传递信息的方法是 POST 方法。这种方法打包信息的方式与 GET 方法相同,不同的是,它不是把信息以文本字符串形式放在 URL 中的 ? 之后进行传递,而是把它以单独的消息形式进行传递。该消息是以标准输入的形式传给 CGI 脚本的。
我们同样使用 cpp_get.cgi 程序来处理 POST 方法。让我们以同样的例子,通过使用 HTML 表单和提交按钮来传递两个值,只不过这次我们使用的不是 GET 方法,而是 POST 方法,如下所示:
<form action="/cgi-bin/cpp_get.cgi" method="post"> 名:<input type="text" name="first_name"><br /> 姓:<input type="text" name="last_name" /> <input type="submit" value="提交" /> </form>
向 CGI 程序传递复选框数据
当需要选择多个选项时,我们使用复选框。
下面的 HTML 代码实例是一个带有两个复选框的表单:
<form action="/cgi-bin/cpp_checkbox.cgi" method="POST" target="_blank"> <input type="checkbox" name="maths" value="on" /> 数学 <input type="checkbox" name="physics" value="on" /> 物理 <input type="submit" value="选择学科" /> </form>
下面的 C++ 程序会生成 cpp_checkbox.cgi 脚本,用于处理 Web 浏览器通过复选框给出的输入。
实例
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc formData; bool maths_flag, physics_flag; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>向 CGI 程序传递复选框数据</title>n"; cout << "</head>n"; cout << "<body>n"; maths_flag = formData.queryCheckbox("maths"); if( maths_flag ) { cout << "Maths Flag: ON " << endl; }else{ cout << "Maths Flag: OFF " << endl; } cout << "<br/>n"; physics_flag = formData.queryCheckbox("physics"); if( physics_flag ) { cout << "Physics Flag: ON " << endl; }else{ cout << "Physics Flag: OFF " << endl; } cout << "<br/>n"; cout << "</body>n"; cout << "</html>n"; return 0; }向 CGI 程序传递单选按钮数据
当只需要选择一个选项时,我们使用单选按钮。
下面的 HTML 代码实例是一个带有两个单选按钮的表单:
<form action="/cgi-bin/cpp_radiobutton.cgi" method="post" target="_blank"> <input type="radio" name="subject" value="maths" checked="checked"/> 数学 <input type="radio" name="subject" value="physics" /> 物理 <input type="submit" value="选择学科" /> </form>
下面的 C++ 程序会生成 cpp_radiobutton.cgi 脚本,用于处理 Web 浏览器通过单选按钮给出的输入。
实例
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc formData; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>向 CGI 程序传递单选按钮数据</title>n"; cout << "</head>n"; cout << "<body>n"; form_iterator fi = formData.getElement("subject"); if( !fi->isEmpty() && fi != (*formData).end()) { cout << "Radio box selected: " << **fi << endl; } cout << "<br/>n"; cout << "</body>n"; cout << "</html>n"; return 0; }向 CGI 程序传递文本区域数据
当需要向 CGI 程序传递多行文本时,我们使用 TEXTAREA 元素。
下面的 HTML 代码实例是一个带有 TEXTAREA 框的表单:
<form action="/cgi-bin/cpp_textarea.cgi" method="post" target="_blank"> <textarea name="textcontent" cols="40" rows="4"> 请在这里输入文本... </textarea> <input type="submit" value="提交" /> </form>
下面的 C++ 程序会生成 cpp_textarea.cgi 脚本,用于处理 Web 浏览器通过文本区域给出的输入。
实例
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc formData; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>向 CGI 程序传递文本区域数据</title>n"; cout << "</head>n"; cout << "<body>n"; form_iterator fi = formData.getElement("textcontent"); if( !fi->isEmpty() && fi != (*formData).end()) { cout << "Text Content: " << **fi << endl; }else{ cout << "No text entered" << endl; } cout << "<br/>n"; cout << "</body>n"; cout << "</html>n"; return 0; }向 CGI 程序传递下拉框数据
当有多个选项可用,但只能选择一个或两个选项时,我们使用下拉框。
下面的 HTML 代码实例是一个带有下拉框的表单:
<form action="/cgi-bin/cpp_dropdown.cgi" method="post" target="_blank"> <select name="dropdown"> <option value="Maths" selected>数学</option> <option value="Physics">物理</option> </select> <input type="submit" value="提交"/> </form>
下面的 C++ 程序会生成 cpp_dropdown.cgi 脚本,用于处理 Web 浏览器通过下拉框给出的输入。
实例
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc formData; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>向 CGI 程序传递下拉框数据</title>n"; cout << "</head>n"; cout << "<body>n"; form_iterator fi = formData.getElement("dropdown"); if( !fi->isEmpty() && fi != (*formData).end()) { cout << "Value Selected: " << **fi << endl; } cout << "<br/>n"; cout << "</body>n"; cout << "</html>n"; return 0; }在 CGI 中使用 Cookies
HTTP 协议是一种无状态的协议。但对于一个商业网站,它需要在不同页面间保持会话信息。例如,一个用户在完成多个页面的步骤之后结束注册。但是,如何在所有网页中保持用户的会话信息。
在许多情况下,使用 cookies 是记忆和跟踪有关用户喜好、购买、佣金以及其他为追求更好的游客体验或网站统计所需信息的最有效的方法。
它是如何工作的
服务器以 cookie 的形式向访客的浏览器发送一些数据。如果浏览器接受了 cookie,则 cookie 会以纯文本记录的形式存储在访客的硬盘上。现在,当访客访问网站上的另一个页面时,会检索 cookie。一旦找到 cookie,服务器就知道存储了什么。
cookie 是一种纯文本的数据记录,带有 5 个可变长度的字段:
- Expires : cookie 的过期日期。如果此字段留空,cookie 会在访客退出浏览器时过期。
- Domain : 网站的域名。
- Path : 设置 cookie 的目录或网页的路径。如果您想从任意的目录或网页检索 cookie,此字段可以留空。
- Secure : 如果此字段包含单词 "secure",那么 cookie 只能通过安全服务器进行检索。如果此字段留空,则不存在该限制。
- Name=Value : cookie 以键值对的形式被设置和获取。
设置 Cookies
向浏览器发送 cookies 是非常简单的。这些 cookies 会在 Content-type 字段之前,与 HTTP 头一起被发送。假设您想设置 UserID 和 Password 为 cookies,设置 cookies 的步骤如下所示:
实例
#include <iostream> using namespace std; int main () { cout << "Set-Cookie:UserID=XYZ;rn"; cout << "Set-Cookie:Password=XYZ123;rn"; cout << "Set-Cookie:Domain=www.w3cschool.cc;rn"; cout << "Set-Cookie:Path=/perl;n"; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>CGI 中的 Cookies</title>n"; cout << "</head>n"; cout << "<body>n"; cout << "设置 cookies" << endl; cout << "<br/>n"; cout << "</body>n"; cout << "</html>n"; return 0; }从这个实例中,我们了解了如何设置 cookies。我们使用 Set-Cookie HTTP 头来设置 cookies。
在这里,有一些设置 cookies 的属性是可选的,比如 Expires、Domain 和 Path。值得注意的是,cookies 是在发送行 "Content-type:text/htmlrnrn 之前被设置的。
编译上面的程序,生成 setcookies.cgi,并尝试使用下面的链接设置 cookies。它会在您的计算机上设置四个 cookies:
/cgi-bin/setcookies.cgi
获取 Cookies
检索所有设置的 cookies 是非常简单的。cookies 被存储在 CGI 环境变量 HTTP_COOKIE 中,且它们的形式如下:
key1=value1;key2=value2;key3=value3....
下面的实例演示了如何获取 cookies。
实例
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc cgi; const_cookie_iterator cci; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>CGI 中的 Cookies</title>n"; cout << "</head>n"; cout << "<body>n"; cout << "<table border = "0" cellspacing = "2">"; // 获取环境变量 const CgiEnvironment& env = cgi.getEnvironment(); for( cci = env.getCookieList().begin(); cci != env.getCookieList().end(); ++cci ) { cout << "<tr><td>" << cci->getName() << "</td><td>"; cout << cci->getValue(); cout << "</td></tr>n"; } cout << "</table><n"; cout << "<br/>n"; cout << "</body>n"; cout << "</html>n"; return 0; }现在,编译上面的程序,生成 getcookies.cgi,并尝试使用下面的链接获取您的计算机上所有可用的 cookies:
/cgi-bin/getcookies.cgi
这会产生一个列表,显示了上一节中设置的四个 cookies 以及您的计算机上所有其他的 cookies:
UserID XYZ Password XYZ123 Domain www.w3cschool.cc Path /perl
文件上传实例
为了上传一个文件,HTML 表单必须把 enctype 属性设置为 multipart/form-data。带有文件类型的 input 标签会创建一个 "Browse" 按钮。
<html> <body> <form enctype="multipart/form-data" action="/cgi-bin/cpp_uploadfile.cgi" method="post"> <p>文件:<input type="file" name="userfile" /></p> <p><input type="submit" value="上传" /></p> </form> </body> </html>
这段代码的结果是下面的表单:
文件:
注意:上面的实例已经故意禁用了保存上传的文件在我们的服务器上。您可以在自己的服务器上尝试上面的代码。
下面是用于处理文件上传的脚本 cpp_uploadfile.cpp:
实例
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc cgi; cout << "Content-type:text/htmlrnrn"; cout << "<html>n"; cout << "<head>n"; cout << "<title>CGI 中的文件上传</title>n"; cout << "</head>n"; cout << "<body>n"; // 获取要被上传的文件列表 const_file_iterator file = cgi.getFile("userfile"); if(file != cgi.getFiles().end()) { // 在 cout 中发送数据类型 cout << HTTPContentHeader(file->getDataType()); // 在 cout 中写入内容 file->writeToStream(cout); } cout << "<文件上传成功>n"; cout << "</body>n"; cout << "</html>n"; return 0; }上面的实例是在 cout 流中写入内容,但您可以打开文件流,并把上传的文件内容保存在目标位置的某个文件中。
-
Web开发要考虑用户禁用浏览器的JS吗?
2013-03-26 11:04:51是,客户端是可以禁用JS的,禁用后使用JS实现的各种功能自然就全部无效,到底这种情况考虑不考虑?要考虑到什么程度? 先按浏览器用户的角度思考一下:当自己的浏览器里点一个按钮发现无效的时候,他会做些什么?...用JS实现了一个功能,而上级的评价是:放弃这种实现方式吧,因为js是可以被用户禁用的,那样他们就觉得是你的系统出问题,而造成不良的后果
是,客户端是可以禁用JS的,禁用后使用JS实现的各种功能自然就全部无效,到底这种情况考虑不考虑?要考虑到什么程度?
先按浏览器用户的角度思考一下:当自己的浏览器里点一个按钮发现无效的时候,他会做些什么?“这网站做的太烂了,赶紧关了”是这样吗?当然肯定有些人是这样。但我觉得更多的人会觉得是自己浏览器的问题。作为一个IT男,我接过很多人的求助,说自己的浏览器看网站不正常,让我帮忙找找问题修复一下
所以,在Web开发过程中,用户禁用JS而致使功能无效化的情况,可以不考虑。JS的诞生就是为了提高用户体验的,比如用JS实现一个全选的功能,这些逻辑完全没有必要让服务器做处理,提交给服务器并返回也会降低用户体验。而如果用户禁用了JS,全选功能无效了,说明人家就不需要你给予的这种高用户体验,你干吗还自己犯贱用服务器代码实现一个客户端全选功能?
用户禁用JS的情况几乎是可以不考虑的,所以很多网站才会广泛使用JS和ajax来提高用户体验。但并不能完全不考虑
在我看来,用户禁用JS只有一种情况,那就是:恶意提交表单数据,绕过JS验证。
所以,综上所述,只需要在提交表单的时候在服务器验证一下数据是否存在恶意代码和字符,其他客户端JS无效的情况都可以不考虑。甚至连按钮的提交也可以用js来实现
-
docker-webssh:基于Web的ssh客户端https:webssh.huashengdun.org-源码
2021-02-21 08:36:23一个简单的Web SSH客户端。 用Python编写。 特征 支持SSH密码身份验证,包括空密码。 支持SSH公钥身份验证,包括DSA RSA ECDSA Ed25519密钥。 支持加密密钥。 支持两因素身份验证(基于时间的一次性密码)。 ... -
hinclude:Web的声明式客户端包含-源码
2021-03-05 16:08:36hinclude.js ... HInclude是Web的声明性客户端包含; 它使您可以使用浏览器轻松地构成网页-使您的页面更具模块化,更可缓存且更易于维护。 有关文档和示例,请参见。 如果您对使用Web组件感兴趣,请参阅 。 -
刷新页面会中断web socket吗_月薪20K+的Web自动化测试工程师都会这项技能!小话浏览器缓存...
2020-12-03 10:07:10在开始今天的测试项目之前,我们先来了解一下Web应用程序!应用程序有两种模式,C/S和B/S。...Web应用程序一般是B/S模式,一个Web应用程序是由完成特定任务的各种Web组件(web components)构成的并通过W...在开始今天的测试项目之前,我们先来了解一下Web应用程序!
应用程序有两种模式,C/S和B/S。C/S模式,即Client/Server(客户端/服务端)模式,这类程序可独立运行。B/S模式,即Browser/Server(浏览器/服务端)模式,这类程序需借助浏览器来运行。
Web应用程序一般是B/S模式,一个Web应用程序是由完成特定任务的各种Web组件(web components)构成的并通过Web将服务展示给外界。在实际应用中,Web应用程序是由多个Servlet、JSP页面、HTML文件以及图像文件等组成。
最近在参加的一个项目,主要是web相关的产品,经常出现开发更完包之后,验证一些功能出现异常,但是一到别人电脑上又不复现,每次都让清理浏览器缓存。测试的时候可以这么清,到了客户那,客户愿意一直清吗?故尝试了解背后原因。
现象:怎么刷新都不行,必须清理浏览器才能查看提交的数据。
1.分析过程
1)问题发现后,按照刚才的操作仍然可以复现,有必现步骤的
2)观察日志打印,——这一步暂时因为某些原因没有做
3)删除配置后,发现数据库中的数据也成功删除了,但是重新添加时,仍存在校验提示配置已存在。——这一步也没做
4)数据库操作没有问题,于是合理怀疑页面的配置校验读取了缓存,而没有实时的读取数据库信息。
5)清除浏览器缓存,重启浏览器,再次进行添加,添加成功,证实了4中的猜测。
问题原因
页面在新添加配置进行保存时会进行校验,但是校验读取了缓存。导致了删除配置再添加时会失败。
修改方式
最后的修改方式,是在每次请求中添加了时间戳。这样,就不会由于每次请求相同,会读取相同的缓存。既保障了缓存的优势(查询反馈速度快),又避免了缓存造成的读取错误的原因
2.问题定位的思路
1)大多数情况,问题的原因是后端代码没有处理好,所以问题出现时,首先应该去看日志;
2)确定问题在前端或者后端日志不能定位的情况下,需要配合使用F12工具或者抓包工具辅助定位;
3)问题特别难定位时,可以使用假设-预测-试验-分析的方法推测问题的原因。
3.测试建议
1)在测试用例设计时,对存在唯一校验的功能应反复操作。像这个缺陷中的类似场景,可以添删除后重新添加相同的数据验证校验时读取的是缓存还是实时读取的后台数据。
2)如果存在本系统嵌入了其他系统的页面的情况,在本系统中打开其他系统的页面时,需要考虑清除缓存后进行操作。
3)对列表记录进行修改或者编辑后,列表没有马上刷新,需要手动刷新页面才会更新列表。对于此类问题,可以建议开发在提交修改或者编辑的数据后,再发一个请求获取数据来及时刷新列表数据,提高用户体验。
4)与缓存或是cookie、session有关的一类问题,测试过程中不能简单的清缓存就可以了,遇到这类问题时除了要多考虑问题的影响还要再多深究原因。
-
离线缓存占内存吗_彻底弄懂浏览器缓存策略
2021-01-10 16:36:14浏览器缓存策略对于前端开发同学来说不陌生,大家都有一定的了解,但...Web 缓存介绍Web 缓存是指一个 Web 资源(如 html 页面,图片,js,数据等)存在于 Web 服务器和客户端(浏览器)之间的副本。缓存会根据进来...浏览器缓存策略对于前端开发同学来说不陌生,大家都有一定的了解,但如果没有系统的归纳总结,可能三言两语很难说明白,甚至说错,尤其在面试过程中感触颇深,很多候选人对这类基础知识竟然都是一知半解,说出几个概念就没了,所以重新归纳总结下,温故而知新。
Web 缓存介绍
- Web 缓存是指一个 Web 资源(如 html 页面,图片,js,数据等)存在于 Web 服务器和客户端(浏览器)之间的副本。
- 缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的 URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。
Web 缓存的好处
- 减少网络延迟,加快页面打开速度
- 减少网络带宽消耗
- 降低服务器压力
- ...
HTTP 的缓存机制
简化的流程如下
根据什么规则缓存
- 新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下条件,浏览器会认为它是有效的,足够新的:
- 含有完整的过期时间控制头信息(HTTP 协议报头),并且仍在有效期内;
- 浏览器已经使用过这个缓存副本,并且在一个会话中已经检查过新鲜度;
- 校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签 Etag(Entity Tag),它可以用来作为浏览器再次请求过程的校验标识。如果发现校验标识不匹配,说明资源已经被修改或过期,浏览器需求重新获取资源内容。
HTTP 缓存的两个阶段
浏览器缓存一般分为两类:强缓存(也称本地缓存)和协商缓存(也称弱缓存)。
本地缓存阶段
浏览器发送请求前,会先去缓存里查看是否命中强缓存,如果命中,则直接从缓存中读取资源,不会发送请求到服务器。否则,进入下一步。
协商缓存阶段
当强缓存没有命中时,浏览器一定会向服务器发起请求。服务器会根据 Request Header 中的一些字段来判断是否命中协商缓存。如果命中,服务器会返回 304 响应,但是不会携带任何响应实体,只是告诉浏览器可以直接从浏览器缓存中获取这个资源。如果本地缓存和协商缓存都没有命中,则从直接从服务器加载资源。
启用&关闭缓存
按照本地缓存阶段和协商缓存阶段分类:
- 使用 HTML Meta 标签 Web 开发者可以在 HTML 页面的节点中加入标签,如下:
上述代码的作用是告诉浏览器当前页面不被缓存,事实上这种禁用缓存的形式用处很有限:
a. 仅有 IE 才能识别这段 meta 标签含义,其它主流浏览器仅识别“Cache-Control: no-store”的 meta 标签。
b. 在 IE 中识别到该 meta 标签含义,并不一定会在请求字段加上 Pragma,但的确会让当前页面每次都发新请求(仅限页面,页面上的资源则不受影响)。
- 使用缓存有关的 HTTP 消息报头 这里需要了解 HTTP 的基础知识。一个 URI 的完整 HTTP 协议交互过程是由 HTTP 请求和 HTTP 响应组成的。有关 HTTP 详细内容可参考《Hypertext Transfer Protocol — HTTP/1.1》、《HTTP 权威指南》等。
在 HTTP 请求和响应的消息报头中,常见的与缓存有关的消息报头有:
上图中只是常用的消息报头,下面来看下不同字段之间的关系和区别:
- Cache-Control 与 Expires
- Cache-Control:HTTP1.1 提出的特性,为了弥补 Expires 缺陷加入的,提供了更精确细致的缓存功能。详细了解详细看几个常见的指令:_ max-age:功能和 Expires 类似,但是后面跟一个以“秒”为单位的相对时间,来供浏览器计算过期时间。_ no-cache:提供了过期验证机制。
(在 Chrome 的 devtools 中勾选 Disable cache 选项,发送的请求会去掉 If-Modified-Since 这个 Header。同时设置 Cache-Control:no-cache Pragma:no-cache,每次请求均为 200)
- no-store:表示当前请求资源禁用缓存;
- public:表示缓存的版本可以被代理服务器或者其他中间服务器识别;
- private:表示只有用户自己的浏览器能够进行缓存,公共的代理服务器不允许缓存。
- Expires:HTTP1.0 的特性,标识该资源过期的时间点,它是一个绝对值,格林威治时间(Greenwich Mean Time, GMT),即在这个时间点之后,缓存的资源过期;优先级:Cache-Control 优先级高于 Expires,为了兼容,通常两个头部同时设置;浏览器默认行为:其实就算 Response Header 中沒有设置 Cache-Control 和 Expires,浏览器仍然会缓存某些资源,这是浏览器的默认行为,是为了提升性能进行的优化,每个浏览器的行为可能不一致,有些浏览器甚至没有这样的优化。
- Last-Modified 与 ETag
- Last-Modified(Response Header)与 If-Modified-Since(Request Header)是一对报文头,属于 http 1.0。
If-Modified-Since 是一个请求首部字段,并且只能用在 GET 或者 HEAD 请求中。Last-Modified 是一个响应首部字段,包含服务器认定的资源作出修改的日期及时间。当带着 If-Modified-Since 头访问服务器请求资源时,服务器会检查 Last-Modified,如果 Last-Modified 的时间早于或等于 If-Modified-Since 则会返回一个不带主体的 304 响应,否则将重新返回资源。
(注意:在 Chrome 的 devtools 中勾选 Disable cache 选项后,发送的请求会去掉 If-Modified-Since 这个 Header。)- ETag 与 If-None-Match 是一对报文头,属于 http 1.1。
ETag 是一个响应首部字段,它是根据实体内容生成的一段 hash 字符串,标识资源的状态,由服务端产生。If-None-Match 是一个条件式的请求首部。如果请求资源时在请求首部加上这个字段,值为之前服务器端返回的资源上的 ETag,则当且仅当服务器上没有任何资源的 ETag 属性值与这个首部中列出的时候,服务器才会返回带有所请求资源实体的 200 响应,否则服务器会返回不带实体的 304 响应。
- ETag 能解决什么问题?
a.Last-Modified 标注的最后修改只能精确到秒级,如果某些文件在 1 秒钟以内,被修改多次的话,它将不能准确标注文件的新鲜度;b. 某些文件也许会周期性的更改,但是他的内容并不改变(仅仅改变的修改时间),但 Last-Modified 却改变了,导致文件没法使用缓存;c. 有可能存在服务器没有准确获取文件修改时间,或者与代理服务器时间不一致等情形。
- 优先级:ETag 优先级比 Last-Modified 高,同时存在时会以 ETag 为准。
缓存位置
浏览器可以在内存、硬盘中开辟一个空间用于保存请求资源副本。我们经常调试时在 DevTools Network 里看到 Memory Cache(內存缓存)和 Disk Cache(硬盘缓存),指的就是缓存所在的位置。请求一个资源时,会按照优先级(Service Worker -> Memory Cache -> Disk Cache -> Push Cache)依次查找缓存,如果命中则使用缓存,否则发起请求。这里先介绍 Memory Cache 和 Disk Cache。
200 from memory cache
表示不访问服务器,直接从内存中读取缓存。因为缓存的资源保存在内存中,所以读取速度较快,但是关闭进程后,缓存资源也会随之销毁,一般来说,系统不会给内存分配较大的容量,因此内存缓存一般用于存储较小文件。同时内存缓存在有时效性要求的场景下也很有用(比如浏览器的隐私模式)。
200 from disk cache
表示不访问服务器,直接从硬盘中读取缓存。与内存相比,硬盘的读取速度相对较慢,但硬盘缓存持续的时间更长,关闭进程之后,缓存的资源仍然存在。由于硬盘的容量较大,因此一般用于存储大文件。
下图可清晰看出差别:
200 from prefetch cache
在 preload 或 prefetch 的资源加载时,两者也是均存储在 http cache,当资源加载完成后,如果资源是可以被缓存的,那么其被存储在 http cache 中等待后续使用;如果资源不可被缓存,那么其在被使用前均存储在 memory cache。
CDN Cache
以腾讯 CDN 为例:X-Cache-Lookup:Hit From MemCache 表示命中 CDN 节点的内存;X-Cache-Lookup:Hit From Disktank 表示命中 CDN 节点的磁盘;X-Cache-Lookup:Hit From Upstream 表示没有命中 CDN。
整体流程
从上图能感受到整个流程,比如常见两种刷新场景:
- 当 F5 刷新网页时,跳过强缓存,但是会检查协商缓存;
- 当 Ctrl + F5 强制刷新页面时,直接从服务器加载,跳过强缓存和协商缓存
其他 Web 缓存策略
IndexDB
IndexedDB 就是浏览器提供的本地数据库,能够在客户端存储可观数量的结构化数据,并且在这些数据上使用索引进行高性能检索的 API。
异步 API 方法调用完后会立即返回,而不会阻塞调用线程。要异步访问数据库,要调用 window 对象 indexedDB 属性的 open() 方法。该方法返回一个 IDBRequest 对象 (IDBOpenDBRequest);异步操作通过在 IDBRequest 对象上触发事件来和调用程序进行通信。
常用异步 API 如下:
在 16 年曾基于 IndexDB 做过一整套缓存策略,有不错的优化效果:
Service Worker
SW 从 2014 年提出的草案到现在已经发展很成熟了,基于 SW 做离线缓存,让用户能够进行离线体验,消息推送体验,离线缓存能力涉及到 Cache 和 CacheStorage 的概念,篇幅有限,不展开了。
LocalStorage
localStorage 属性允许你访问一个 Document 源(origin)的对象 Storage 用于存储当前源的数据,除非用户人为清除(调用 localStorage api 或则清除浏览器数据), 否则存储在 localStorage 的数据将被长期保留。
SessionStorage
sessionStorage 属性允许你访问一个 session Storage 对象,用于存储当前会话的数据,存储在 sessionStorage 里面的数据在页面会话结束时会被清除。页面会话在浏览器打开期间一直保持,并且重新加载或恢复页面仍会保持原来的页面会话。
定义最优缓存策略
- 使用一致的网址:如果您在不同的网址上提供相同的内容,将会多次获取和存储该内容。注意:URL 区分大小写!
- 确定中继缓存可以缓存哪些资源:对所有用户的响应完全相同的资源很适合由 CDN 或其他中继缓存进行缓存;
- 确定每个资源的最优缓存周期:不同的资源可能有不同的更新要求。审查并确定每个资源适合的 max-age;
- 确定网站的最佳缓存层级:对 HTML 文档组合使用包含内容特征码的资源网址以及短时间或 no-cache 的生命周期,可以控制客户端获取更新的速度;
- 更新最小化:有些资源的更新比其他资源频繁。如果资源的特定部分(例如 JS 函数或一组 CSS 样式)会经常更新,应考虑将其代码作为单独的文件提供。这样,每次获取更新时,剩余内容(例如不会频繁更新的库代码)可以从缓存中获取,确保下载的内容量最少;
- 确保服务器配置或移除 ETag:因为 Etag 跟服务器配置有关,每台服务器的 Etag 都是不同的;
- 善用 HTML5 的缓存机制:合理设计启用 LocalStorage、SessionStorage、IndexDB、SW 等存储,会给页面性能带来明显提升;
- 结合 Native 的强大存储能力:善于利用客户端能力,定制合适的缓存机制,打造极致体验。
结语
通过了解浏览器各种缓存机制和存储能力特点,结合业务制定合适的缓存策略,善用缓存是基本功,可以用于时常审查负责的业务,可能就会发现个别业务并没有运用到位,共勉。
本文转载自腾讯技术工程公号,仅供学习用,侵删
-
现在有一个手机客户端应用,服务端用python+webpy,给客户端返回数据使用的是xml,怎么生成这个xml并发送啊?...
2012-08-24 19:37:40部署到服务器以后,在浏览器请求,返回注入数据的xml,[color=red][b]但是在手机客户端发送请求,返回的就是空的xml,就是说没有注入数据???????[/b][/color]这是为什么????客户端的同事说,服务器刷新了... -
浏览器访问web资源的过程
2013-06-18 08:01:48从浏览器输入网址到结果返回的具体过程图 一个完整的http请求包括一个消息行,若干消息头以及实体内容吗,如下图 Get是一种请求方式,book/java.html即访问该主机下的book应用下的java.html资源,... -
oracle数据库的客户端和本地是什么意思?
2020-05-11 16:50:471、安装的客户端是什么意思,安装好以后,再配置一下就可以访问公司的数据库吗?本地客户端只是一个界面? 2、安装的客户端查询的数据什么的,账户和密码是已经建好的,是不是可以自己插数据到公司的数据库呢? 3、... -
关闭浏览器cookie还在吗_java web Session会话技术(原理图解+功能+与Cookie的区别+基本使用)...
2021-01-25 05:39:05会话技术类似于生活中两个人聊天,你说一句我说一句,在web中体现为服务器端与客户端的交互一次会话中包含多次请求与响应,当服务器请求浏览器是会话建立,当一方断开时会话结束什么是SessionSession是服务器端会话... -
你了解什么是Web服务器吗?
2019-09-21 10:48:11Web服务器可以向Web浏览器等客户端提供文档,也可以放置网站文件,让全世界浏览,更可以放置数据文件,让全世界下载。 Web服务器,也称为“WWW服务器”( 英文全写:World Wide Web,翻译成中文:万维网或环球信息网... -
cookies可以导出到其他浏览器登录吗_Postmanpostman通过Cookies登录博客园
2020-12-06 20:22:55在做接口测试的时候都会遇到需要登录这个操作,有一些接口是在登录过后才能操作过程,前面写过如何通过requests...Cookie是由Web服务器保存在用户浏览器(客户端)上的小文本文件,它可以包含有关用户的信息。无论何时... -
你真的了解这些浏览器存储以及session吗
2017-12-13 07:02:56cookie数据会自动在web浏览器和web服务器之间传输,也就是说HTTP请求发送时,会把保存在该请求域名下的所有cookie值发送给web服务器,因此服务器端脚本是可以读、写存储在客户端的cookie的操作。 cookie的有效期: ... -
报表工具支持不需客户端安装插件的打印方式吗
2020-07-09 10:58:21当然,Web 报表在浏览器端展现后也可以用浏览器自带的打印来完成,但这个是针对整个页面的,不仅对应报表部分,所以报表内容以外的信息也会被打印且不能连续多页打印。 上面提到的打印方式中,除了借助浏览器本身的... -
瘦客户端时代会再度来临吗
2009-08-18 21:19:00最近有很多文章都在展望“云计算”时代的到来,个人电脑不再处在中心地位,仅仅是一个小小的客户端,数据、应用都在一服务器上进行,有人还说,操作系统将被浏览器替代。其实,如果抛开云计算的精确概念不谈,这种... -
ViewFinderJS:ViewFinder-NodeJS产品,使浏览器成为Web应用程序。 WTF RBI。 CBII。 远程浏览器隔离,可...
2021-02-22 18:51:05这是一个功能完整的,无客户端,源源双许可的远程浏览器隔离解决方案。 执照 这已发布到公共领域。 对于托管和托管版本,请在注册 关于 这是功能齐全的无客户端远程浏览器隔离产品(RBI),包括安全文档查看(CDR... -
ViewFinder:ViewFinder-NodeJS产品,使浏览器成为Web应用程序。 WTF RBI。 CBII。 远程浏览器隔离,可嵌入...
2021-01-28 19:13:12这是一个功能完整的,无客户端,源源双许可的远程浏览器隔离解决方案。 执照 这已发布到公共领域。 对于托管和托管版本,请在注册 关于 这是功能齐全的无客户端远程浏览器隔离产品(RBI),包括安全文档查看(CDR... -
WEB开发未来还有前途吗?
2019-03-12 18:06:46不知道其他人对WEB开发是怎样定义的,但我认为WEB开发包含一系列技术和环节,如HTTP协议,数据库,WEB...后端则主要是处理一些业务逻辑处理和数据存储等很多东西,处理后的结果或数据则返回给客户端(浏览器)。... -
浏览器向服务器请求页面的原理
2016-02-28 18:24:31我们经常用浏览器上网,但是大家想过没有在浏览器和服务器之间是...服务器端安装的IIS软件,在客户端是浏览器; 客户端: 当我们在浏览器输入网址并回车后,如果你是第一次访问这个网址例如www.itcast.cn,你 -
Adobe AIR:我们真的需要在桌面上运行浏览器应用吗?
2007-12-03 12:44:00Adobe AIR:我们真的需要在桌面上运行浏览器应用吗? 作者 Jon Rose译者 郭晓刚 发布于 2007年12月2日 下午7时14分 ...Adobe集成运行时(AIR)是一个让开发者运用Web技术构建桌面应用的平台。Dan... -
你真正了解web前端开发吗
2020-05-26 09:17:50完成客户端程序(也就是浏览器端)的开发,开发JavaScript以及Flash模块,同时结合后台开发技术模拟整体效果,进行丰富互联网的Web开发,致力于通过技术改善用户体验。 “前端开发工程师”从事【前台显示页面开发...