3. 动态生成的页面
现在我们已经了解访问一个最简单的静态页面的原理。那么想一下,这个页面是怎么开发出来的呢? HTML文档都是文本材料,完全可以在记事本里面徒手打出来。那么如果新闻网站有一万篇新闻,我们需要准备10000个html文档吗?或者新浪微博有1亿个用户,我们需要准备1亿个用户主页文档吗?答案显然是否定的。
我们可以想象,可能有一个网页的模板,服务器根据用户的请求,将用户想看的新闻填到网页模板里;又或者,当你查看某个大V的微博主页时,服务器会动态地把这个大V发过的微博、网友评论,都装到一个微博网页的模板里。
如果可以理解这一点,那么我们就能想到,在服务器端,是有一种程序,专门负责动态地生成web文档,可以称之为应用服务器、也可以称它为后端程序。
理论上,大多数编程语言都可以用来开发动态网页,但实际上有些语言特别擅长做这件事情,如PHP、JSP,上手快、开发效率高,非常时候开发轻量级的网页和小网站。目前最流行的网站开发语言,就有PHP、JAVA、PYTHON等。
这里有2个概念再强调一下。
首先:互联网中的大部分网页,例如新闻、微博、论坛帖子等(即使你以为他们有文件名叫**.html),在网站服务器上并不是以具体的文件形式保存的,也就是说,他们可能并不存在。而是在服务器端,由程序根据用户的请求,动态地生成的。
其次:虽然后端程序能够动态地组织各种信息,但是对于一个具体的http请求来说,后端程序生成的,并由服务器端发给用户的,仍旧是一个静态的html文档。对于浏览器来说,它其实无法判断html文档是静态文档还是动态生成的文档,看起来都是静态文档。 可以认为,后端程序与用户是隔离的。
举个例子,PHP是非常灵活的后端语言,你可以在html文件里直接插入php代码,也可以用php输出一个所有html代码。
Step1. 假设有一个hello.html的网页模板,内嵌了php代码,在服务器端被执行前,他是这样的。
<body>
<p>这是页面中的静态部分</p>
<?php
echo "<p>今天是 " . date("Y/m/d") . "</p>";
?>
</body>
Step2. 用户发起http请求,要访问hello.html。web服务器找到这个文件,但是发现其中有不认识的php代码,于是调用php进程来处理。php是一种解释性语言,它逐行运行php代码:
- 输出”
今天是“...,date(”Y/m/d“)是一个函数,运行后得到日期字符串。所以这一行PHP代码在此处(嵌入PHP代码的地方)打印了一行html代码
<p>今天是2015/11/21</p>
Step3. PHP运行以后,得到的是一个全是HTML静态标签的文档,是这个样子的
<body>
<p>这是页面中的静态部分</p>
<p>今天是 2015/11/21</p>
</body>
最后再提一个概念,后端程序动态生成网页的,是负责处理逻辑的。那么网页的内容(文章、信息)是保存在哪里的呢?最常见的答案就是数据库。例如电子商务网站的产品信息、用户信息、交易信息都是存储在数据库中的。目前互联网站使用最广泛的就是Mysql(主要指中小型网站)。
有关Mysql使用的代码案例,后文会介绍。
4. 数据提交与页面刷新
上一小节,我们了解了动态生成网页的概念,那么现在又有一个问题。假设是新闻网页,后台程序怎么知道当前要显示哪一篇文章呢?假设是电商网站,怎么知道当前要展示的是哪一个商品呢?
答案很简单,肯定是用户告诉服务器的啦。那么具体是怎么实现的?
我们知道用户通过浏览器发给服务器的就是一个HTTP请求,那么问题就是用户想要发送的数据(称为“参数”吧)放在HTTP请求的哪个部分。
- 请求的URL中
- 请求的正文中
- 请求的其他头域中
1) Get请求
第一种情况,将用户请求的参数是放在URL里的,这就是我们常说的GET方法。具体的说,就是将用户想要发送给服务器的参数,以字符串形式,遵循某种格式,拼接在URL里,发送给服务器。
举个例子,打开知乎首页,在搜索栏输入"html",查询html有关的问题,则页面会刷新,刷新后,地址栏变成了https://www.zhihu.com/search?type=question&q=html
,其中https://www.zhihu.com/search
是网页地址,?
就是GET参数开始的标志,&
是参数之间的分隔符,type
和q
是参数名,question
和html
是参数值,表示查询的类别是问题,查询的关键字是"HTML"。
这是完整的HTTP请求,体会一下:
GET /search?type=question&q=http HTTP/1.1
Host: www.zhihu.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: https://www.zhihu.com/
2) Post请求
这种情况,用户参数放在HTTP请求的正文里,这就是常说的POST方法。具体的来说,我们直接看上一个案例:
POST /search HTTP/1.1
Host: www.zhihu.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: https://www.zhihu.com/
Content-Type: application/x-www-form-urlencoded
Content-Length: 43
type=question&q=http
当然,因为知乎网站用的并不是POST,所以上述代码是我虚构的。
3) 扩展HTTP头域
把用户参数放在其他头域中,或者增加自定义的头域。但是需要双方都能兼容,也就是事先约定好格式。这已经属于扩展HTTP协议的范畴了,不是常规的方法。
所以截至目前,我们掌握的两种传递用户参数的方法,GET和POST。在很多场景下,两者都可以使用。GET和POST的区别和各自特点,这个不是本文重点,请读者自行百度。
传递用户参数的一个典型场景,就是表单。我们来看一个例子:
假设有一个页面question.html
,页面中有一个表单:
如果你现在愿意看一下,它对应的HTTP代码是这样的:
<form name="input" action="/html/html_form_action.asp" method="get">
<p>Name: <input type="text" name="fname" /></p>
Male: <input type="radio" name="Sex" value="Male" checked="checked">
Female: <input type="radio" name="Sex" value="Female">
<br/><br/>
I like to eat: <br/>
Apple: <input type="checkbox" name="food" value="apple" checked="checked" /><br />
Banana: <input type="checkbox" name="food" value="banana" /><br />
Carrot: <input type="checkbox" name="food" value="carrot" />
<br/><br/>
<input type="submit" value="Submit" />
</form>
其中表单form的method参数定义为Get。填好表单,点击Submit,发出的http请求如下:
GET /html/html_form_action.asp?fname=iHerzog&Sex=Male&food=apple HTTP/1.1
Host: www.w3school.com.cn
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
把Method改成POST再试一下:
POST /html/html_form_action.asp HTTP/1.1
Host: www.w3school.com.cn
Connection: keep-alive
Content-Length: 34
Cache-Control: max-age=0
Origin: http://www.w3school.com.cn
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
fname=iHerzog&Sex=Male&food=apple&food=banana
区别大家自己体会吧。
这里有一个问题,在这种所谓的用户交互模式下,当你改变表单中某个组件的值时,譬如你填写名字、修改性别、选择爱好的时候,浏览器和服务器至今没有发生任何交互,只有当你点击submit的时候,浏览器才会把你的参数,也就是form表单中各组件的值,打包成一个http请求中发给服务器。而且,一旦发送出去,整个页面就会全部刷新,刷新为从服务器收到的http响应中包含的html页面了。
这称之为”HTTP同步请求“,这种模式在具体的网页开发中,给网页功能带来了很大的局限性。大家应该能体会到,现在我们的网页都不是这样的了,我们把一件商品放进购物车、完全不觉得整个页面都会刷新,特别是当我们刷微博、朋友圈时,往下拖一拖,新的内容就滚出来的。
这种模式就是”HTTP异步请求“,也就是不让整个网页刷新,而是可以局部页面响应用户请求,加载新的内容。这一点我们将在下下一章来介绍。