diff --git a/blog/Web的历史1️⃣-HTTP.md b/blog/Web的历史1️⃣-HTTP.md index 0bbad91..731c61e 100644 --- a/blog/Web的历史1️⃣-HTTP.md +++ b/blog/Web的历史1️⃣-HTTP.md @@ -1,19 +1,26 @@ # Web的历史1️⃣-HTTP -## 起源 +*注:这些文章本来是打算作为开发组后端培训文本的开篇而写的,但是我发现文章的内容过于复杂且有点离题,所以稍微修改了一下单独作为3篇独立的文章发布。* + +*本文章的许多图片都直接来自 [MDN](https://developer.mozilla.org),在这里先感谢图片的创作者。* + +## 网络的起源 人们最初发明网络的目的很简单:在不同的电脑之间传输文件。那个时候没有U盘,也没有蓝牙,计算机领域的前辈们必须从头设计一个高效的传输文件的办法。 假如我们的电脑上有一个文件`example.docx`,放在我们电脑的文件夹`documents`里面,我们如何使用最原始的方法把它传递给另外一台电脑呢? -首先为这台电脑分配一个IP地址,IP地址是一台电脑在互联网上唯一标识,例如我们电脑的的IP地址是`123.45.67.89`;我们在这台电脑上启动一个“HTTP服务程序”;在程序的设置里指定"根目录"是我们存放文件的文件夹`documents`。 +早期的程序员编写了一类叫做"HTTP服务器"的程序,程序的功能是选择电脑上的一个文件夹,将这个文件夹暴露在互联网上共所有人操作。这个文件夹叫做HTTP服务的“根目录”。 -这个HTTP服务程序的作用是把你电脑上一个指定的文件夹(我们称为“根目录”)暴露在互联网上,所有人都可以下载这个文件夹下的文件。 +要使用这个程序,我们还要先给电脑分配一个IP地址,IP地址是一台电脑在互联网上的唯一标识,假如我们电脑的的IP地址是`123.45.67.89`。我们在这台电脑上启动一个HTTP服务程序。在程序的设置里指定根目录是我们存放文件的文件夹`documents`。 -这个时候,打开另一台电脑,在浏览器地址栏中输入`http://123.45.67.89/example.docx`,这时候就会发生神奇的事情:浏览器会提示你下载`example.docx`,这样,文件就从一台电脑传输到了另一台电脑。 +前面说过,这个HTTP服务程序的作用是把你电脑上一个指定的文件夹(我们称为“根目录”)暴露在互联网上,所有人都可以下载这个文件夹下的文件。于是这个时候,我们打开随便另一台电脑(前提是有网),在浏览器地址栏中输入`http://123.45.67.89/example.docx`,这时候就会发生神奇的事情:浏览器会提示你下载`example.docx`,这样,文件就从一台电脑传输到了另一台电脑。 假如`documents`文件夹下面还有另外一个文件`hi.txt`,那么当你在浏览器地址栏中输入`http://123.45.67.89/hi.txt`时,你就会发现浏览器显示了`hi.txt`这份文件的内容,因为浏览器可以直接显示`txt`文件,而不能直接显示`docx`文件。 +![IIS根目录](/img/blog/iis-changeroot-websoft9.png) +*IIS是Windows官方的HTTP服务器,有着图形化的配置界面,其它大部分HTTP服务器都需要通过命令行和配置文件进行配置。* + ## HTTP协议 等等,刚才所讲的操作为什么能发生呢?当我在浏览器中输入网址并按下回车键的时候,这一切的背后到底发生了什么? @@ -22,11 +29,14 @@ HTTP协议,是`HyperText Transfer Protocol`的缩写,即"超文本传输协议"。是通过网络在电脑之间进行文件传输的默认协议。进行HTTP文件传输的双方,一个称之为”客户端“(Client),一个称之为"服务端"(Server)。 -我们刚才就是在我们的电脑上部署了HTTP服务端,而浏览器就是我们通信中的客户端.一次完整的HTTP协议由客户端请求(Request)和服务端响应(Response)组成;**在HTTP协议中,总是由客户端先发起一次HTTP请求,然后由服务端返回这次请求的响应,这样才是一次完整的HTTP通信。** +我们刚才就是在我们的电脑上部署了HTTP服务端,而浏览器就是我们通信中的客户端.一次完整的HTTP协议由客户端请求(Request)和服务端响应(Response)组成。**在HTTP协议中,总是由客户端先发起一次HTTP请求,然后由服务端返回这次请求的响应,这样才是一次完整的HTTP通信。** + +![HTTP请求示意图](/img/blog/basic_static_app_server.png) + HTTP协议不仅可以获取一个文件(就像我们所做的),也可以向服务端上传一个文件,或是修改服务端目录下某个文件的内容... -一次HTTP通信需要进行的操作(获取or上传等)由HTTP通信的“方法”指定,方法是请求头的一部分。 +一次HTTP通信要进行什么操作(获取or上传等),由HTTP通信的“方法”指定,方法是请求头的一部分。 下面我们介绍HTTP协议的具体内容: @@ -41,29 +51,35 @@ HTTP协议不仅可以获取一个文件(就像我们所做的),也可以向 - User-Agent:说明客户端是什么(是一个浏览器,还是命令行工具,还是爬虫?) - 自定义参数:除了上面的需要传输的标准内容,客户端还可以发送一些自定义的内容给服务端,这些内容总是以`Key=Value`的形式存在。 -比如`content-encoding=gzip`,就是要求服务端把数据用`gzip`压缩之后传输过来,方便节省带宽。参数是放在地址栏里传输的,在正常的URI之后以`?`开头,以`&`分割,例如,一次完整的HTTP请求是:`http://123.45.67.89/example.docx?content-encoding=gzip&greeting=hello` +举个例子,百度的搜索接口是`GET http://baidu.com/s`,要传递搜索内容到这个接口中,需要通过URL参数`wd`。如果你想用百度搜索东西,可以在地址栏里面输入`http://baidu.com/s?wd=你要搜的东西`,这就是URL参数的意义:向服务器传递一些自定义信息。 -参数的内容不是HTTP标准,也就是服务端如何理解参数完全靠程序员的设置,如果程序员设置了`gzip`有关代码,那我们的参数才有意义,否则这些参数完全不起作用。 +参数的内容不是HTTP标准,也就是服务端如何理解参数完全靠程序员写的代码。比如谷歌的用户搜索接口就是`GET http://google.com/search?q=你要搜的东西`。 #### HTTP方法 HTTP方法定义了这个请求具体要对指定的文件做什么,其中: -- `GET`:获取指定文件的内容; +- `GET`:获取指定文件的内容。 - `POST`:上传一个文件,内容放在请求体(下面会讲到) -- `PUT`:更新指定的文件,如果没有就创建一个 -- `PATCH`:修改指定的文件 -- `DELETE`:删除指指定的文件 -我们日常使用浏览器,比如在地址栏中输入`baidu.com`,其实就是在对这个地址做GET请求,浏览器会把你的输入内容自动补全成 `GET http://www.baidu.com/` ,我们一般使用浏览器,没法手动做出除了GET之外的请求,但是其他请求今天又被经常使用,这其实是前端脚本在工作,以后会解释. +其实这里还有一些方法的,我们以后讲。 + +我们日常使用浏览器,比如在地址栏中输入`baidu.com`,其实就是在对这个地址做GET请求,浏览器会把你的输入内容自动补全成 `GET http://www.baidu.com/` 这一HTTP请求 + +在浏览器地址栏里直接输入网址,默认就是发送GET请求。你可能会好奇,POST这些请求是怎么发出去的呢?这通常是由网页中的JavaScript代码在背后发送的。我们在之后的内容会讲到这一点。 + + +![请求头](/img/blog/http-request.svg) ##### 请求体 -对于某些方法,需要在请求时向服务器夹带一些东西(比如POST,PUT,PATCH需要你带上新文件的内容),请求体就是装载这些东西的. +对于某些方法,需要在请求时向服务器夹带一些东西(比如POST需要你带上新文件的内容),请求体就是装载这些东西的. 像GET请求就没有请求体,因为GET请求不需要夹带信息. -:::tip 提示 +:::tip[提示] -浏览器虽然正常情况下没法做出GET外的请求,但是浏览器在控制台里是可以自定义请求的,以`Firefox`为例,在F12的`Network`一栏中点`New Request`(有一个加号)就可以发送自定义请求;例如`curl`等HTTP命令行工具也可以发送请求,专业一点的例如`Postman`是一个专业的HTTP测试工具,可以满足很多复杂的要求 +浏览器虽然正常情况下没法做出GET外的请求,但是浏览器在控制台里是可以自定义请求的,以`Firefox`为例,在F12的`Network`一栏中点`New Request`(有一个加号)就可以发送自定义请求。`curl`等HTTP命令行工具也可以发送请求. + +实际上,HTTP协议中的“客户端”不仅仅是指浏览器,我们通常用的浏览器就是最常见的客户端,但其实像curl这样的命令行工具,或者手机APP,或者是浏览器里的JavaScript代码,当它们向服务器请求数据时,也扮演着客户端的角色。 ::: @@ -75,6 +91,8 @@ HTTP方法定义了这个请求具体要对指定的文件做什么,其中: 回应体就是包含了回应的主体内容了,如果是GET请求的话,那么就回应了所GET文件的内容,如果是其他请求的话,可能也会没有回应体,具体看使用的方法 +![回应体](/img/blog/http-response.svg) + #### 一个例子 `GET`方法从服务器获得一个资源,我们在浏览器的地址栏输入一个地址时,就是在对这个URI做`GET`请求,前面的例子也是通过`GET`方法来进行的。 @@ -82,31 +100,55 @@ HTTP方法定义了这个请求具体要对指定的文件做什么,其中: `POST`方法向服务器上传一个资源,例如使用某个客户端发送`POST http://example.org/sheet.xlsx`,在request body里面带上你的这个文件,那么`example.org`网站的根目录下就会多出一个叫做`sheet.xlsx`的文件 + 除了上面介绍的这些方法,还有`DELETE`,`PATCH`等方法,分别对应了删除,修改一个资源,你可以在你的电脑上通过`curl`等程序,或者通过浏览器控制台来进行常规的`GET`之外的方法请求 -:::tip 提示 +:::tip[提示] httpbin.org 这个网站可以让你试验HTTP协议的方法 ::: +:::info[HTTPS] + +HTTP在网络上是明文传输的,也就是说客户端和服务器之间的每个网络节点,每个人都可以看到。这显然不方便隐私,所以人们发明了HTTPS,也就是把HTTP的正文加密了,HTTPS可以说是当今互联网的基石,有了HTTPS,我们才能放心地在网上输入密码,用银行卡付款等。 + +::: + ### 网页与HTML -随着网络的发展,人们发现互联网的潜力远不止于传输文件。特别是浏览器的普及,人们希望能直接在网页上展示丰富的信息,而不只是把浏览器当作下载工具。前面我们说过了,浏览器可以直接展示`txt`文件,但是最大的问题是,`txt`文件是没有样式的。 +随着网络的发展,人们发现互联网的潜力远不止于传输文件。特别是浏览器的普及,人们希望能直接在网页上展示丰富的信息,而不只是把浏览器当作下载工具。前面我们说过了,浏览器可以直接展示txt文件,但是最大的问题是,txt文件是没有样式的。 比如:txt无法设置字体大小和颜色,无法加粗、倾斜文字,无法创建表格和列表,无法插入图片和链接。这些都是txt的局限。 -为了解决这个问题,人们发明了`HTML`(HyperText Markup Language),就是“超文本标记语言”,HTML的核心思想是:在普通文本中加入特殊的标记,告诉浏览器如何显示内容。 +为了解决这个问题,人们发明了HTML(HyperText Markup Language),就是“超文本标记语言”,HTML的核心思想是:在普通文本中加入特殊的标记,告诉浏览器如何显示内容。 -比如`你好~`就是指示浏览器显示`你好~`这段文字,并且以斜体的方式。你可以像打开`txt`文件一样打开`html`文件,只不过浏览器默认是加载渲染之后的界面而不是raw HTML +比如`你好~`就是指示浏览器以斜体的方式显示`你好~`这段文字。你可以用系统自带的笔记本像打开txt文件一样打开HTML文件,只不过浏览器默认是加载渲染之后的界面而不是原始的HTML。 除HTML之外,人们还发明了CSS与HTML搭配使用,CSS可以对样式做更复杂高级精细的控制,这里就不细说了 回到我们的主题,不管使用的是什么方法,操作的是什么文件,HTTP协议传输的对象都是一些固定静态的文件,其内容在服务器上是固定不变的(除非手动修改),这样的网页称为静态网页。 -静态网页中,所有用户看到的内容都相同,内容不会根据用户行为动态变化 -,服务器只负责传输文件,不进行复杂计算。 +静态网页中,所有用户看到的内容都相同,内容不会根据用户行为动态变化,服务器只负责传输文件,不进行复杂计算。 -我们的wiki就是静态网页,在服务端上都对应着html页面,只不过加了非常多的样式显得很高级。 +我们的wiki就是静态网页,在服务端上都对应着HTML页面,只不过加了非常多的样式显得很高级。 很多常见的网站(如企业官网、技术文档、个人博客等)都是静态网页,虽然看起来很精美,但本质上就是经过精心设计的HTML和CSS文件。 + +![网页的构成](/img/blog/fetching-a-page.svg) + +:::info + +这是三篇系列文章中的第**1**篇 + +点击以跳转: + +**HTTP**(你在看的文章) + +[动态网页](/blog/Web的历史2️⃣-动态网页) + +[Web应用](/blog/Web的历史3️⃣-Web应用) + +::: + + diff --git a/blog/Web的历史2️⃣-动态网页.md b/blog/Web的历史2️⃣-动态网页.md index cc58d60..5faede5 100644 --- a/blog/Web的历史2️⃣-动态网页.md +++ b/blog/Web的历史2️⃣-动态网页.md @@ -1,2 +1,286 @@ # Web的历史2️⃣-动态网页 + +上篇文章我们已经了解了静态网页是如何工作的,但是这样的网页是不能满足大家对互联网的需求的。举例子来说:你访问b站首页`bilibili.com`,每次刷新,首页上显示给你的视频都不一样,不同的人访问这个首页,显示的也不一样,按理说大家都是访问一个网址,背后应该都是同一个文件,为什么每个人都不一样呢?这种功能是如何实现的? + +淘宝上有数不清的商品在售卖,如果淘宝为每一个商品都在服务器目录下面创建一个html文件,好让大家通过访问`http://taobao.com/someproduct.html`来查看商品信息,那这个工作量就非常大了。而且,这样的网页,基本上没有交互的功能:我们希望用户可以点击按钮就能购买商品,商家在网页后台上操作就能上传商品。这种功能应该如何实现呢? + +暂时先不考虑这些高级的问题,让我们先从最基础的讲起: + +## 服务器端内嵌(SSI) + +如果你想向网站中插入动态内容,SSI是最简单,最直接的办法,比如我们的wiki有许多页面,但是每个页面都有一些共同的元素:页面头部的导航栏,左侧的列表,页脚等。如果为每个页面都复制一份相同的HTML的话,那就太麻烦了,有没有什么办法,可以使HTML一次编写,到处渲染呢? + +SSI(Server Side Includes)就是满足这种需求的一个HTML宏语言。它有点类似于C语言的`# include`宏: + +假设这是我们首页的HTML: + +```html +

wiki

+ +
+ +``` + +假如`navbar.html`的内容如下: + +```html + + +教程 +文档 +高级 +Github + + + +``` + +那么用户访问我们首页时就会看到: + +```html + +

wiki

+// highlight-start + +教程 +文档 +高级 +Github + +// highlight-end +
+ + +``` +如果导航栏的界面有变化,那么只需要修改`navbar.html`即可,不用修改网站中的每一个页面。 + +没错,SSI的功能就是简单地把制定的内容插入进HTML里。这对一些重复的元素(例如每个网页的页头,页脚,侧边栏)还有一些需要更新的内容很实用。 + +当然,SSI并没有解决动态网页的问题,它只是把需要手动更新的地方单独拿了出来,使维护静态网站更容易,所以程序员们又发明了CGI技术(其实SSI也可以嵌入CGI的)。 + +## CGI + **CGI(Common Gateway Interface)** 是第一个真正实现动态网页的技术,它允许Web服务器执行外部程序来生成网页内容。 + +CGI的工作原理是:当用户访问特定URL时,服务器不是返回静态文件,而是执行一个程序,并将程序的输出作为HTTP响应返回给用户。 + +(举个天气预报的例子): + +Web服务器通常会把能执行的程序(除开静态文件)放在一个叫cgi-bin的特殊目录里。假设我们服务器的这个文件夹里有一个查询天气的Python程序`weather.py`,当用户访问`http://example.org/cgi-bin/weather.py?city=中山&date=2025-06-25`时,我们的HTTP服务程序会自动**执行**放在路径中`/cgi-bin/weather.py`的这个Python脚本,并且将客户端的请求头和请求体传递给脚本; + +脚本解析请求头中`city=中山&date=2025-06-25`这个参数,在数据库中查询这个日期的天气,然后返回一个HTML给HTTP服务程序,再把这个HTML返回给客户端。 + +如果没有设置CGI,那么服务程序只会返回给客户端`TodayWeather.py`这个脚本文件的内容。 + +其实,CGI是一个接口格式,它定义了我们编写程序与HTTP服务程序之间如何交互。通常,HTTP服务程序给CGI程序的输入就是环境变量,输出就是标准输出。 + +CGI的巧妙之处在于,服务器不是用什么复杂的方式和脚本沟通,而是把请求信息(比如URL参数里的城市)变成程序很轻松就能读到的环境变量。而程序也不需要复杂的操作进行IO,它只需要把生成的HTML代码打印出来,服务器就会自动收集这些打印的内容,然后发回给用户的浏览器。 + + +下面是一个例子: + + +```bash title="/var/www/cgi-bin/system-info.sh" + +#!/bin/bash + +# HTTP响应头 +echo "Content-type: text/html" +echo "" + +# HTML内容 +echo '' +echo '系统信息' +echo '' +echo '

服务器系统信息

' +echo '

当前时间:'$(date)'

' +echo '

内存使用情况:

' +echo '
'
+free -h
+echo '
' +echo '' +echo '' + +``` + +每次用户访问这个页面,都会看到实时的系统信息,真正实现了动态内容。 + +虽然CGI现在很少见了,但它建立了一个重要概念:将URL请求映射到程序函数,而不是静态文件。这个思想成为了现代Web开发的基础。 + +## 嵌入式脚本 +随着动态网页需求的增长,纯CGI编程变得复杂。程序员们希望能够在HTML中直接编程动态代码,这样既保持了HTML的可读性,又能实现动态功能。 + + +这个就是嵌入式脚本,顾名思义就是把脚本和HTML混在一起,在HTML中嵌入脚本; + +但是这种脚本和今天的前端JavaScript不同,它是由后端解释执行的,在返回HTML响应之前,HTTP服务程序会检查这个HTML里面有没有可以执行的脚本内容,有的话就执行这些脚本,并且把脚本的输出嵌入到HTML里面。任何有效的HTML也是有效的这类脚本语言。 + +从CGI到嵌入式脚本的另外一个关键驱动力是性能。CGI每来一个请求,服务器就得创建一个新进程去运行CGI程序,完成后再销毁,开销很大。而嵌入式脚本通常则是直接作为服务器的一部分运行,效率远高于CGI。 + +### JSP + +举个例子吧,你可以轻松使用Java来创建动态网页,只需要把Java代码嵌入到HTML里面,使用`<% %>`包裹住代码: + +```java + + + +

当前时间:<%= new java.util.Date() %>

+ + + + +``` + +复杂一点的例子: + +```java + + + +

欢迎访问我们的网站

+

当前服务器时间:<%= new java.util.Date() %>

+

您是第 <%= session.getAttribute("visitCount") %> 位访客

+ + <%-- 这是JSP注释,不会出现在最终HTML中 --%> + <% + // 这里可以写复杂的Java逻辑 + String userName = request.getParameter("user"); + if (userName != null) { + out.println("

欢迎您," + userName + "!

"); + } + %> + + + +``` + +:::info[session和cookie] + +在这段JSP代码中有一个对象叫做`session`,这是什么呢?实际上,因为HTTP是无状态的协议,意味着两次请求之间是完全独立的,一次请求不应该依赖另一次请求。这显得有点不灵活,于是人们会在HTTP的请求体上夹带一些额外的参数,用于表明用户的身份信息,比如在用户登录网站之后,服务器会给客户端一个密钥,下一次客户端请求页面是带上这个密钥,服务器就知道这是某个用户的请求。在这种模式下,服务器需要为每个用户维护信息,比如最简单地需要维护密钥是对应哪个用户的,这些信息就叫做session。 + +::: + +类似于这样的脚本叫做JSP(JavaServer Pages),它在后端返回时被转换成Java Servlet代码来执行,本质上,JSP是Java Servlet的一种语法糖。至于JSP和Java Servlet都是什么,自行了解吧。 + +### PHP +比JSP更灵活的就是PHP,PHP就是一门纯正的脚本语言了,它的用法与JSP类似,使用`包裹代码`: + +```php + + + +

欢迎来到我的网站

+ 当前时间:$time

"; + ?> + + + +``` + +也可以这样写,这样就类似于CGI程序的写法了: + + +```php + +"; +echo "

欢迎来到我的网站

"; +$time = date('Y-m-d H:i:s'); +echo "

当前时间:$time

"; +echo ""; +?> + +``` + +### LAMP +这种动态网页的编写方法流行了很多年,形成了一个叫做"LAMP"的套路:Linux+Apache+MySQL+PHP;就是将电脑装上Linux系统,运行Apache这个HTTP服务端,使用PHP作为动态脚本语言,使用MySQL来存储和访问业务数据。 + +需要注意的是,这四个都是开源免费的软件,LAMP的兴起,是开源软件运动的标志之一。开源软件使得部署网站的成本极大地降低,推动了互联网的繁荣。如果你想建站,那时候互联网上到处都是"LAMP一件安装脚本"之类的东西,现在也能搜到不少。一个下午就能上线一个完备的网站。这些技术的出现,使得开网站不再局限于大企业才能办得到的事情,一时间互联网上到处都是个人或者小单位的网站,甚至后来出现了诸如Wordpress之类的方案,不会写代码也能开网站。繁荣的生态,网页上丰富的动态内容,形成了被我们称为“Web 2.0”的时代。 + +LAMP的一个典型反面是微软全家桶:Windows Server+IIS+SQL Server+ASP,这套技术方案需要给微软缴纳高额的授权费用,在当时基本上只限于追求稳定和售后服务的企业使用。我们的文章也没有怎么介绍这些技术。不过IIS对于个人用自己的电脑建站还是非常方便的。(当然国内没有公网IP那是另一回事了╮( ̄▽ ̄)╭) + +## MVC架构 +随着网页的不断发展,出现了复杂的业务逻辑,并且页面也越来越复杂;这时候,把页面和程序逻辑混在一起的嵌入式脚本在庞大的复杂代码情况下变得难以维护。 + +而且它们都有一个特点:依赖于具体的某个HTTP服务程序,PHP依赖于Apache的`mod_php`或Nginx的FastCGI支持,JSP依赖于Servlet容器例如Tomcat,这增加了开发与部署的耦合度,更使得项目难以管理。嵌入式脚本难以复用已有的代码,这些代码的测试也需要模拟HTTP环境,难以测试。 + +此时兴起了一种新的Web后端编程思想,它就是MVC(Model-View-Controller) + +简单来说,根据大量的开发经验累积,人们发现一个动态网页的后端通常需要做到这3件事情: +- Model:使用面向对象的方法为业务建模,把数据对应到编程语言中的对象,把对数据的操作对应到对象的方法。负责对业务数据进行实际的操作。 +- View:输入数据,负责把数据变成用户可以直观看懂的HTML。 +- Controller:负责协调,调用上面两个部分。 + +![MVC](/img/blog/model-view-controller-light-blue.png) + +例如,当我们在报修系统中想要查询一个片区的全部报修时,首先我们访问`http://wwbx.zsxyww.com/QueryTickets.php?zone=朝晖&status=pending` + +然后服务器根目录下的`QueryTickets.php`程序就会接受到我们的请求(在MVC时期的PHP程序已经不像嵌入式脚本那时混写HTML和PHP,整个文件就是以` +随着技术的不断发展,尤其是移动设备的普及和移动互联网的发展,使得动态网页对于日益复杂的需求力不从心。 + +具体地来说,业界开始需求Web的“应用化”,也就是要把网页变成一个应用程序:在以前,我们介绍的动态网页技术可以很好地支持博客,论坛,新闻网站,企业官网等;而现在,我们需要在浏览器上写文件(腾讯文档),聊天(Discord),直播,点外卖,玩游戏(实际上,很多手机APP都是浏览器套壳,本质上就是向你展示一个网页;包括微信小程序,也是一个只可以使用微信内置浏览器打开的网站) + +那些开发APP的程序员也很乐意把自己的项目搬到浏览器里,因为用HTML+CSS写GUI非常方便,而且Web😇具有跨平台的特性:你需要付出很多努力才能确保一个传统APP能在手机,电脑,MacOS,Windows,Linux上运行,但是你写网页只需要很轻松地写,不用考虑兼容,浏览器会确保你的网页在这些平台上都能运行。 + +包括我们的报修系统,也算是一个Web应用。Web应用相比传统的高级动态网页有如下特征: +- 页面少:动态网页可能要有成千上百个页面,所以必须使用模板动态生成。而应用程序可能只有几个或者几十个界面。 +- 高交互:动态网页的主要目的是供人观看,用户的交互(例如点击链接,按钮等)比较少且简单。而Web应用中,用户的交互非常频繁且复杂。前后端通信更加频繁。 +- 部分更新:动态网页在不同页面之间也有不变的内容,比如页头页脚之类的,但是这些内容只占网页的少部分;在应用程序中,页面的绝大部分元素都没有怎么变化,只有少部分数据会发生变化,这时候,每更新一次数据就请求一次新页面就十分浪费了。 + +Web应用程序的这些需求都是我们之前介绍的动态网页技术所不好满足的,于是Web开发进入了一个新时期: + +## AJAX与前后端分离 + +### JavaScript +之前我们一直在介绍服务器的技术,而一直忽略了浏览器的发展。实际上,浏览器也慢慢变得复杂,也在慢慢进行技术演进。 + +在1995年,也就是差不多后端处于SSI和CGI的时代,网景公司的员工发明了一种编程语言,叫做"JavaScript",这是对当时新兴的Java Applet的回应。Java Applet可以让你在浏览器运行Java代码,和它同一生态位的一个技术大家应该很熟悉:Flash,不过它们后来都被淘汰了。 + +JavaScript作为Java Applet的竞争者,是专门设计出来在浏览器内运行的脚本语言。连名字都是在蹭Java的热度:)网景浏览器(也就是火狐浏览器的前身)为JavaScript提供了一个强大的能力:动态地改变当前页面HTML和CSS的内容。 + +另外,浏览器还为JavaScript提供了发起HTTP请求的接口,不只是GET请求。这本是非常强大的功能,不过,由于JavaScript早期语法混乱,再加上当时人们对于前端能力需求并不是太大,所以直到2010年左右,前端和JavaScript才被重视起来。也就是我们接下来的内容: + +### AJAX +传统的动态网页有一个不灵活的特性:每次只能返回一个完整的页面。也就是说,要么不更新网页,要么就全部更新,没办法更新现有网页的一部分。 + +比如我们有一个在文章下面评论的功能:用户需要在文本框里输入评论,然后点击"发布评论"的按钮。如果创建成功,那么我们应该让用户看到"评论成功"的提示并且在评论区显示用户最新评论的内容;如果失败也要提示有关的信息。 + +传统的动态网页只能这么做:返回一个完整的网页,这个网页和用户之前的网页的大部分内容没有区别,只是多了提示的信息和新的评论。但实际上,页面的有效信息只有那一部分。这极大地浪费了网络带宽和服务器性能(因为要重新渲染)。 + +AJAX(Asynchronous JavaScript and XML)就是解决这一痛点的技术,既然JavaScript可以动态地修改页面内容,发送HTTP请求,那么就直接让JavaScript程序发送带有评论内容的POST请求到服务器,然后服务器返回纯数据(通常使用JSON或XML的格式,把数据用格式包装起来有一个术语,叫做"序列化")而不是HTML,送给JavaScript程序而不是浏览器,JavaScript根据返回的操作HTML或CSS,实现网页部分更新的效果。 + +AJAX的核心在于"异步",浏览器发送请求后,不会等待后端返回数据,而是允许用户继续浏览。即是指在发评论时,页面不会整个刷新一下的特性。 + +### 一个AJAX报名系统的例子 + +下面举一个例子,来详细说明AJAX的工作原理:用户需要通过一个网页进行某项活动的报名,填写个人信息,然后点击网页上的提交按钮,服务端会返回报名的结果,成功还是失败,与传统的方法不同,服务器返回信息时不是重新返回一个HTML,而是让浏览器直接在原有的界面上(比如提交按钮的下面)显示报名的状态。 + +用户通过浏览器输入地址,使用`GET`方法发起对`http://example.org/submit`的请求,服务器程序获得请求后,在路由表中查得`/submit`这个路径匹配静态文件`/static/submit.html`,于是将文件发送给浏览器; + +浏览器得到文件后开始解析渲染这个文件,发现这个html在头部又要求浏览器向服务器获取`submit.js`文件和`submit.css`文件,于是浏览器不需要用户处理自己又发送了两条`GET`的HTTP Request,在文件返回后,浏览器自动执行返回的脚本并应用CSS样式。脚本的内容是监听html文件中提交按钮的“点击”事件,当按钮被点击时,执行提交报名表函数。 + +返回的html文件包括了提示框,要求用户输入姓名,手机,空闲时间等信息,在这些输入栏的最下面有一个提交按钮,当点击这个按钮时,脚本中的指定函数就会被激活,这个函数将读取用户从上面输入进来的内容,做一些简单的认证(比如说手机号的格式等),如果检验失败则让用户重新填写信息。 + +如果检验成功,则将这些信息组织成一个JSON文件(虽然AJAX中的X就是XML,但是现在其实最常用JSON),向服务器发送一个`POST`请求,地址是`http://example.org/api/submit`,在请求体中包含刚才的JSON文件。 + +服务器在接收这个请求后,在路由模块中查得这个URL匹配`HandleTicket()`函数,于是这个请求的上下文全部转交给`HandleTicket()`这个函数,函数会创建一个`Ticket`类的实例,将数据做后端检验后赋给实例的成员,如果检验成功则调用方法`Ticket.Create()`录入数据库,并向客户端(在这里指JavaScript脚本,而不是用户)发送回应成功的状态码(200); + +检验的内容除了冗余检查格式是否合法之外,还要执行业务逻辑方面的检查: +- 一个人只准报名一次,所以如果数据库中存在“姓名”字段相同的记录的话,检验则不通过(这个只是个例子,所以不考虑重名的情况) +- 如果我们活动的时间的范围没有包含在空闲时间的范围内,检验则不通过,因为用户的时间和我们活动的时间冲突 +- 如果数据库中的记录(表示报名成功的用户)大于或等于我们设定的数字的话,检验则不通过,因为人够了 + +如果检验没有成功,则回应失败的消息,除了依据失败的类型设置对应的错误码外,还要返回一个JSON文件来简述错误信息。 + +报名成功的消息的示例: + +```JSON title="StatusCode:201 Created" + + + +{ + "status":"success", + "submit":{ + "name":"小明", + "freeAt":"2023-11-25_15:00~19:00", + "phone":12345678900 + }, + "createdAt":"2023-11-23_9:23:22" + "message":"恭喜!你已成功报名我们的活动" +} + +``` + +报名失败的消息的示例: + +```JSON title="StatusCode:400 Bad Request" + +{ + "status":"fail", + "submit":{ + "name":"小明", + "freeAt":"2023-11-25_9:00~15:00", + "phone":12345678900 + }, + "createdAt":"2023-11-23_9:23:22" + "message":"抱歉,你的空闲时间与我们的活动时间有冲突" +} + +``` + + + +在JavaScript脚本接收到回应后,根据返回的内容,操作html文件的内容,在提交按钮的下面一行插入一个文本块: + +报名成功时: + +```HTML + +
+

恭喜!你已成功报名我们的活动

+

请检查你的信息:

+

姓名:小明

+

空闲时间:2023-11-25 15:00至19:00

+

手机号:12345678900

+

服务端提交创建时间:2023-11-23 9:23:22

+
+ +``` + +报名失败时: + +```HTML + +
+

报名失败!抱歉,你的空闲时间与我们的活动时间有冲突

+

请检查你的信息:

+

姓名:小明

+

空闲时间:2023-11-25 9:00至15:00

+

手机号:12345678900

+

服务端提交创建时间:2023-11-23 9:23:22

+
+ +``` + +使用AJAX技术,用户会发现,在写完信息点击提交按钮后,页面没有变化,过了1秒,按钮下面出现了提示信息。这给用户的体验非常像传统的桌面APP。 + +如果不使用AJAX技术,那么用户会发现在按下按钮后,网页会白屏刷新一段时间,过了几秒,返回了一个新页面,提示了报名状态信息。这样的应用虽然不是不能用,但是用户体验总是不如AJAX来的无缝,自然。性能也不好。 + +### API驱动与前后端分离 +在上面那个例子里,浏览器并没有返回HTML,而是返回纯数据,交给前端渲染。既然浏览器自己就能根据数据渲染HTML,那么后端也就没必要返回成品HTML了。 + +可以发现,这实际上就是把MVC架构中的"Views"部分推给了前端,后端只需要通过HTTP传递数据给前端JavaScript,前端JavaScript再渲染成HTML。后端返回纯数据,由前端渲染的模式称之为**前后端分离**; + +而这样返回数据的URL也不能称之为"网页"了,因为它从给人看变成了给JavaScript程序看,这样的URL实际上变成了前端程序和后端程序这两个程序之间交互的接口,所以这种URL我们称之为**HTTP API**,一个主要是API而不是HTML网页的网站,我们称之为"API驱动"的网站。 + + +#### REST API + +不知道你还记得前面说的“虚拟路由”吗?这被广泛地运用在如今的API设计下,比如说我要获取某篇文章下面的全部评论,评论获取API可能是这样的:`GET http://example.org/api/getComments?article_id=123123`。 + +这种设计模式把一个URL看作一个业务窗口,窗口的作用是办理一个业务,在这个例子里`getComments`办理的业务就是“获取评论区”,非常的直观。 + +这种面向业务的API设计对于小规模项目非常方便,如果你的API接口只有几十个的话那使用这种设计也无妨,不过如果你的业务多起来的的话,这种设计是非常难以管理的。 + +现代的API设计采用面向资源的设计模式,把一个URL看作一个资源,例如我们重写上面的获取评论API:`GET http://example.org/article/123123/comments` + +文章的评论是一项资源,一个URL就是获取这个资源,文章本身也是一项资源,如果我们要查看文章内容的话,我们可以这样写:`GET http://example.org/article/123123`,如果我们要上传评论的话,可以写`POST http://example.org/article/123123/comments`,在请求体里面带上评论。获取和上传评论,URL是一样的,只是方法不一样,这就利用了HTTP方法:例如,要删除评论区下的第15条评论的话,可以这样写:`DELETE http://example.org/article/123123/comments/15` + + + +如果我们要获取评论区下某个用户发过的其他评论的话,我们可以这么写:`GET http://example.org/user/456456/comments`,核心思想是,一个URL是一个资源,方法是对资源的操作。 + +你看,这有点像传统的文件系统路由,但是这是完全在后端程序里实现的虚拟的“文件资源系统”,在URL后对应的并不是文件,而是和数据库打交道的Model板块函数。这种方式,就是所谓的REST,以这种方式设计的API,就叫REST API。 + +GitHub的API设计可以说是REST风格的典范,你可以抽空看看。 + + + + + +## 单页应用(SPA) +API驱动架构发展到极致就是所谓的"单页应用",顾名思义,服务器只会在用户打开网页时加载一段html文件和一些JavaScript脚本(也就是一个页面),后面的全部内容都是通过JavaScript动态更新的。JavaScript和服务器的后端接口交互主要就是采用AJAX。现代网站,或者说Web应用,通常流行这种单页应用架构。 + +单页应用的坏处就是打破了为静态网页文件设计的“收藏”功能和前进后退,也不利于SEO和无障碍,因为用户在网站上只打开一个网页,网页的内容根据用户操作由浏览器脚本动态更新,而生成的html也大多没有做语义化优化,难以被理解和解析。当然这些问题有解决方案,下面会讲到。 + +SPA的例子就是邮箱应用(比如Gmail),通过浏览器访问你的邮箱,通过邮箱的主页面点某个邮件,你并没有进入一个新的页面,而是在原有的页面上,内容变成了邮件查看的界面,如果在主页面上点击发件的话,同样也没有一个发件页面,而是直接呈现发件的UI。 + +### 框架 +作为"应用",SPA是很复杂的,我们开发桌面应用也不是从底层一路造轮子,而是用Qt,GTK这种框架,同样Web应用也有框架,其中最主要的两个就是`Vue.js`和`React.js`,这些框架提供了Web应用需要的种种功能,我们介绍其中主要的几个方面: + +### UI组件 +SPA框架通常把整个页面的UI分成许多的"组件",当页面变得复杂时,手动管理DOM会变得非常繁琐且容易出错。组件则抽象了复杂的DOM,使其变成有组织,易维护,可复用的一个个单元。 + +更重要的是,主流框架提供了"声明式UI编程"的方法,这使得你像是在写UI的配置文件,而不是操纵这些UI的程序;你告诉框架"做什么",而不是"怎么做",框架会自动解析你的声明,随着数据的变化自动更新UI组件,你要做的就是告诉框架在某种数据状态下哪个组件需要是怎么样的。用行话讲这叫做"数据驱动视图"。 + +这样,就可以让开发者可以像搭积木一样构建用户界面,并且当数据变化时,框架会自动更新界面,极大地提高了开发效率。 + +### 虚拟DOM + +尽管JavaScript可以动态修改DOM,但频繁地直接操作真实DOM是非常耗费性能的。每次DOM操作都需要浏览器重新渲染一遍全部内容。在复杂或数据频繁变化的界面中,这会导致页面卡顿,用户体验下降。 + +为了解决这个问题,许多SPA框架引入了虚拟DOM的概念。虚拟DOM是一个缓冲区:当数据发生变化时,框架不会直接修改真实DOM,而是先在内存中构建一个新的虚拟DOM树,等到积累到一定的改动,再一次性应用全部的DOM操作。 + +这种机制的目的是将真实的DOM操作次数降到最低。这显著优化了渲染性能,尤其是在数据频繁变化的复杂界面中,虚拟DOM能够提供更好的性能。另外虚拟DOM也有利于管理状态,增加清晰度: + + + +### 状态管理 +基于Web前端技术栈的单页应用和传统桌面应用程序有一个很大的不同:渲染Web应用界面所基于的数据有着异步获取,分散来源的特点。在流行的REST风格的API设计下,服务器不负责记录状态,所以整理数据,维持状态的重任就全部交由前端负责。 + +偏偏前端技术栈又不是为开发这种复杂应用而诞生的:HTML最初就是一个标记格式的简单机制,JavaScript最初被用来写简单的脚本,为了保持兼容性,现代框架必须在这种基础上开发,而不是重构。 + +作为前端开发核心的DOM对象,其本身就是一个巨大的全局状态,所以前端开发天生没有选择像传统桌面开发那样把状态放在各个对象中进行操作,而是维护一个全局的状态,任何操作都是对这个全局的对象进行操作。 + +这种不良的设计风格,加上RESTful API和HTTP协议的无状态特性,导致前端技术栈天生不适合开发复杂的应用程序:当应用逻辑变得复杂时,通过直接操作DOM来更新视图,就极易导致代码混乱,状态难以追踪,很容易把代码写成一坨浆糊。 + +正如前面提到的,我们又不能重构浏览器的底层技术栈,所以必须在这一屎山上构建我们的项目。由于在SPA下不再刷新页面,前端需要自己维护一个与UI保持同步的数据状态。这就要考虑如何解耦模块和数据一致性的问题了。 + +这本来可以通过OOP+设计模式解决,不过我们说过,DOM的存在使得程序员要迎合这一模式,将状态以全局的方式保存,由异步数据来异步操作DOM也容易引起数据竞争。这就极大限制了OOP的发挥,那还有什么办法呢? + +于是函数式编程(FP)这个学院派的范式就进入了前端程序员的视野。FP对于前端的这一痛点提供了极佳的解决方案。现在我们不修改原状态,而是创建一个新的状态替代原状态(不可变数据);我们的函数,现在除了输出数据外不进行任何类似于`console.log()`的其它操作(纯函数)。等等诸如此类的编程思想基本上成为了现代前端开发的基石。使得SPA更容易规范开发和调试。 + +手写FP框架自然不太好,本着不重复造轮子的精神,前端提供了许多践行着FP范式的状态管理库,帮助前端程序员进行复杂程序的状态管理;Vue和React的状态管理框架分别叫Vuex/Pinia(在Vue3)和Redux。状态管理是现代Web应用前端开发的重要部分。 +:::info + +这个部分的内容可能有点难以理解,不过理解这一部分对于成为一个优秀的前端程序员是非常重要的。你也可以在这之后了解关于FP范式的主要思想,这对开发现代和优秀的程序是很重要的,因为FP是编程界的下一个技术潮流,比如Rust,其设计就大量参考了FP思想,甚至Rust早期就是用OCaml开发的😃 + +::: + +### 前端路由和首屏优化 +前面提到SPA打破了浏览器UX和搜索引擎对网站的检测,这些框架提供了API来改变地址栏中的内容,对于不同的页面可以设置不同的URL,来使得逻辑更加清晰,收藏栏也可以正常地使用了。 + +SPA因为需要初始化大量JavaScript程序,对于网络连接或者设备性能不好的用户,他们会看到长时间的白屏,首屏优化就是在一开始的html中加入一些信息,比如给用户看一个`加载中...`的动画,让用户耐心等待。或者是在`head`栏里放一些信息,使得网站可以被搜索引擎更好地搜到。 + +### 构建系统 +浏览器其实并不能直接理解前端框架(Vue,React等)写的代码。因为它们并不是标准JavaScript,它们含有大量自定义的语法。因此,前端开发必须要把使用了框架的代码**编译**成浏览器可以理解的HTML,CSS,JavaScript。请注意,类似JQuery这样的库是不需要编译的,因为它们并没有超出原生的JavaScript功能和语法,而只是一个类库。你只需要在你的HTML里包含它们就可以。 + +构建系统是现代前端开发的重要一环,除了编译高级JavaScript代码外,构建系统通常还会做一些打包,压缩等杂活;这是现代前端开发必不可少的一环。常用的构建系统有Vite和Webpack + +--- + +JavaScript并不是天生开发复杂应用的语言,所以SPA框架提供了那么多的机制,来把这个为简单脚本设计的语言变成一个足够开发可靠Web应用的语言,尽管一层一层的嵌套拖慢了性能,但是Web的性能瓶颈通常不在于计算而是I/O,再加上互联网所谓"敏捷开发"的风气,导致了前端基本几天就要推出新技术,眼花缭乱层出不穷,但是这些技术底层都是差不多的。重要的是不要被各种宣传迷惑了认知,而要认清它们的本质。 + +## JavaScript全栈 +### Node.js +前面提到,JavaScript是专门设计运行在浏览器中的语言,也就是说,JavaScript的运行时就是浏览器,不过JavaScript也有浏览器之外的运行时,最出名的叫做`Node.js`; + +简单来说,Node.js 让JavaScript走出了浏览器,可以直接在服务器上运行了。这意味着,开发者可以用同一种语言(JavaScript)来编写前端(用户在浏览器里看到的界面)和后端(服务器上处理数据的逻辑),这就是所谓的‘JavaScript全栈’开发。常见的JavaScript后端框架有Express.js Koa.js Nest.js。 +### Web开发之外 +Node.js能做的也不只是Web开发,传统编程语言可以做的Node.js都可以,在Web外最重要的领域就是桌面开发了,通常使用Electron框架,可以让你使用HTML,CSS来编写GUI界面,用JavaScript来操作界面。 + +因为依赖问题,通常每个Electron App 都打包了自己的Node.js运行时,每安装一个App,就相当于安装了一个浏览器内核。这严重浪费了电脑的内存和硬盘空间,但好处就是方便了开发。会写Web就能做桌面开发,降低了学习成本,也更方便跨平台。 + +## 最新技术 +### WebSocket +HTTP已经很好了,但是协议本身有一个缺点:一个HTTP连接分为请求和响应,只能由客户端发起请求,服务端返回响应,如果客户端没有先发一点什么给服务端的话,服务端是没办法先给客户端发信息的,这就给一些更加复杂的要求增添了障碍:比如通过浏览器和别人聊天,需要接受来自服务器发给你的其他人的聊天的信息;或者是在浏览器上玩游戏,需要和服务器交换数据。这可以通过轮询来解决,但是这样还是不方便,效率也较低。 + +WebSocket是一个全新的协议,支持客户端和服务器的全双工通信,即客户端和服务器都可以主动地发消息,而且可以同时进行,而且是采用较小的数据帧格式,降低了网络开销,提高了数据传输速度。WebSocket在建立连接后保持长时间有效,不需要为每次消息发送重新建立连接,因此效率更高。 + +为了兼容HTTP协议,WebSocket也是监听80和443端口(HTTPS),握手采用了HTTP协议,通过升级协议来建立连接,如果可以升级,服务器会返回101状态码,升级连接. + + +### WebAssembly +WebAssembly是最近新出现的技术,他允许开发者将C/C++ , Rust等原本的一些编译型语言编译成浏览器可以执行的字节码,使得在浏览器中也可以执行这些程序,目前也有一些使用WebAssembly的应用,可以去看看,最大的好处就是不会JavaScript也能开发前端,也可以复用以前为桌面写的代码。这还是一个非常新兴的领域,值得技术投资😄 + +## 总结与资源 +Web的历史总共经历了三个阶段:首先为了传输基本文件而发明了HTTP,为了方便地预览文档而发明了HTML和CSS,这是Web的静态网页的时代;为了给静态网页增加动态内容,批量生成许多的网页,人们发明了动态网页技术,形成了后端程序的概念,这是动态网页的时代;为了让客户端浏览器更加强大,给后端减负,人们发明了JavaScript,又搭建了复杂的Web应用程序,形成了前端程序的概念,这是Web的应用程序时代。 + +虽然技术变得越来越复杂,但是发展的脉络是可以抓住的:Web由于其特性,被人们承载了越来越高的期望:从操作文件,到浏览信息,到成熟的应用程序平台。最重要的是知道Web的诸多名词,概念,技术并不是突然出现的,而是经历了半个世纪的历史沉淀,它们也不是为了创新而创新,每个技术能够出现并受到欢迎,一定是因为解决了某个痛点才有了价值。 + +### 编年史 +按照时间顺序总结对Web意义重大的技术: + +
+编年史(AI写的,可能有点小错误) +

早期 (1966-1990)

萌芽与成长 (1991-2004)

繁荣与变革 (2005至今)

+
+ +### 资源 +*(MDN是一个非常好的网站,是开发FireFox的Mozilla公司写的,基本上是前端开发的圣经了,对于后端人,这个网站也有许多很好的内容)* +1. [关于HTTP协议的详细解释](https://developer.mozilla.org/zh-CN/docs/Web/HTTP) +2. [Web开发教程](https://developer.mozilla.org/zh-CN/docs/Learn_web_development) *HTML,CSS,JavaScript都有,需要学什么就去看什么* +3. [后端开发教程](https://developer.mozilla.org/zh-CN/docs/Learn_web_development/Extensions/Server-side) + +:::info + +这是三篇系列文章中的第**3**篇 + +点击以跳转: + +[HTTP](/blog/Web的历史1️⃣-HTTP) + +[动态网页](/blog/Web的历史2️⃣-动态网页) + +**Web应用**(你在看的文章) + +::: + diff --git a/docusaurus.config.js b/docusaurus.config.js index d034e2d..93a7afd 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -161,6 +161,7 @@ const config = { prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, + additonalLanguages: ["Java", "PHP", "Bash"], }, docs: { sidebar: { diff --git a/static/img/blog/basic_static_app_server.png b/static/img/blog/basic_static_app_server.png new file mode 100644 index 0000000..e5d3594 Binary files /dev/null and b/static/img/blog/basic_static_app_server.png differ diff --git a/static/img/blog/fetching-a-page.svg b/static/img/blog/fetching-a-page.svg new file mode 100644 index 0000000..78176bd --- /dev/null +++ b/static/img/blog/fetching-a-page.svg @@ -0,0 +1,231 @@ + + + + + + + +
+
+
+ image +
+
+
+
+ image +
+ + + +
+
+
+ Video +
+
+
+
+ Video +
+ + + +
+
+
+ Advertisement +
+
+
+
+ Advertisement +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ Web document +
+
+
+
+
+ Web document +
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+ GET index.html +
+
+
+
+
+
+ GET index.html + +
+ + + +
+
+
+
+ GET styles.css +
+
+
+
+
+
+ GET styles.css + +
+ + + +
+
+
+
+ GET header.png +
+
+
+
+
+
+ GET header.png + +
+ + + +
+
+
+
+ GET video.mp4 +
+
+
+
+
+
+ GET video.mp4 + +
+ + + +
+
+
+
+ GET advert.jpg +
+
+
+
+
+
+ GET advert.jpg + +
+ + + +
+
+
+ Web server +
+
+
+
+ Web server +
+ + + + + + + +
+
+
+ Video server +
+
+
+
+ Video server +
+ + + +
+
+
+ Ad server +
+
+
+
+ Ad server +
+
diff --git a/static/img/blog/http-request.svg b/static/img/blog/http-request.svg new file mode 100644 index 0000000..44a424a --- /dev/null +++ b/static/img/blog/http-request.svg @@ -0,0 +1,127 @@ + + + + + + + + + + +
+
+
+
Method
+
+
+
+
+ Method +
+ + + +
+
+
Path
+
+
+
+ Path +
+ + + +
+
+
Protocol version
+
+
+
+ Protocol version +
+ + + +
+
+
+
Headers
+
+
+
+
+ Headers +
+ + + + + + + + + + + +
+
+
+

+ GET +

+
+
+
+
+ GET +
+ + + +
+
+
+

/

+
+
+
+
+ / +
+ + + +
+
+
+

+ HTTP/1.1 +

+
+
+
+
+ HTTP/1.1 +
+ + + +
+
+
+
+ Host: developer.mozilla.org
+
+ Accept-Language: fr
+
+
+
+
+ Host: developer.mozilla.org... +
+ + + + +
\ No newline at end of file diff --git a/static/img/blog/http-response.svg b/static/img/blog/http-response.svg new file mode 100644 index 0000000..4fb310e --- /dev/null +++ b/static/img/blog/http-response.svg @@ -0,0 +1,121 @@ + + + + + + +
+
+
Status message
+
+
+
+ Status message +
+ + + +
+
+
Status code
+
+
+
+ Status code +
+ + + +
+
+
Protocol version
+
+
+
+ Protocol version +
+ + + +
+
+
+
Headers
+
+
+
+
+ Headers +
+ + + +
+
+
OK
+
+
+
+ OK +
+ + + +
+
+
200
+
+
+
+ 200 +
+ + + +
+
+
+

+ HTTP/1.1 +

+
+
+
+
+ HTTP/1.1 +
+ + + +
+
+
+
+ date: Tue, 18 Jun 2024 10:03:55 GMT
+
+ cache-control: public, max-age=3600
+
+ content-type: text/html
+
+
+
+
+ date: Tue, 18 Jun 2024 10:03:55 GMT... +
+ + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/static/img/blog/iis-changeroot-websoft9.png b/static/img/blog/iis-changeroot-websoft9.png new file mode 100644 index 0000000..3823ee3 Binary files /dev/null and b/static/img/blog/iis-changeroot-websoft9.png differ diff --git a/static/img/blog/model-view-controller-light-blue.png b/static/img/blog/model-view-controller-light-blue.png new file mode 100644 index 0000000..2243ef8 Binary files /dev/null and b/static/img/blog/model-view-controller-light-blue.png differ diff --git a/static/img/blog/web_application_with_html_and_steps.png b/static/img/blog/web_application_with_html_and_steps.png new file mode 100644 index 0000000..94a59df Binary files /dev/null and b/static/img/blog/web_application_with_html_and_steps.png differ