HTTP协议之资源(一)

Web资源是Web服务的基础,我们上网,归根到底就是为了获取资源;而整个互联网,就是为了将资源从一个地方传递到另一个地方。这里整理了资源类型,资源路径和动态资源网关这三个部分知识。

<!--more-->

从我这个半吊子的Web前端开发者的角度看,整个网页的浏览过程可以抽象为:

  • 开发者提前把图片啊HTML文档啊PHP代码啊啥的东西,一股脑全扔在服务器上。这些东西先暂时统称为资源
  • 访客可以通过服务器的IP地址或者网站的域名来访问我们的服务器了,至于IP和域名DNS解析这些暂时先别去管它,总之,服务器得一直开着,等待用户访问。
  • 用户输入服务器地址(浏览器向对应的服务器发出请求),然后看见页面(服务器就会响应请求并返回资源,由浏览器处理呈现资源),这个请求和响应的过程称为事务。(至于中间到底发生了什么,我们假装这是魔法的力量。)

现在,我们了解了两个重要的概念:资源和事务。我的理解是,整个HTTP协议就是围绕着这两个部分展开的接下来就先看看资源的部分,而至于事务相关的部分,后面会陆陆续续弄清楚的。

1. 资源类型

互联网上存在各式各样的资源,比如图片,文档等,图片又可以分为JPG,PNG等,文档也可以分为HTML,XML等等。为了区分这些通过Web传输的资源,HTTP为这些资源对象都打上了名为MIME类型的数据格式标签。当浏览器从服务器取回某个资源对象时,会检查该资源对象的MIME类型,从而得知浏览器自身能否处理这个对象。常见的MIME类型有text/html(HTML文本文档),text/plain(普通ASCII文本文档),image/(图片类型)等。

学习MIME的时候,在stackoverflow上发现了一个关于MIME的精彩回答,这个回答将MIME比作是windows系统上文件后缀名,用来帮助浏览器使用正确的姿势打开对应的网络资源。

不同类型的资源具有不同的MIME标签,这是从资源的属性来看的。如果从资源的生成方式来看待,可以将Web资源分为静态资源和动态资源。 所谓的静态资源,指的是提前准备好并保存在服务器上的资源,比如图片,文本文件,HTML文档等,最简单的Web资源就是Web服务器文件系统中的这些静态文件;所谓动态资源,指的是服务器根据需要访客的具体需求,动态生成内容的某些软件程序(比如利用PHP访问数据库,然后根据返回数据生成的网页就可以看作是动态资源)。

现在已经了解了资源的分类,接下来看一看如何指定浏览器去获取相应的资源。

2. 统一资源标识符

我们不可能仅仅通过某个资源的类型就能够请求到具体的资源对象(互联网上资源的数量远大于资源的类型数量)。实际上,每个Web服务器资源对象都有一个叫做统一资源标识符(URI)的名字,用于在世界范围内惟一标识并定位信息资源。我们在浏览器的地址栏输入的那一长串东西,用术语来讲就是:通过指定资源的统一资源标识符,向服务器请求相应资源。

而URI又分为了两种形式:

  • 统一资源定位符URL
  • 统一资源名URN

2.1. URL

我们最常见也最常用的的也就是URL了,统一资源定位符描述了某台特定服务器上的某资源的特定位置,URL可以明确说明从一个精确,固定的位置获取资源。回想一下,服务器的IP地址在世界范围内是唯一的,只要再指明该服务器上对应资源的位置,我们就可以获取到这个具体的资源对象了。来一个常见的URL:

http://www.shymean.com/uploads/test.jpg

可以把上面这个URL分为三个部分:

  • http://被称为方案,标识访问资源所用的协议类型
  • www.shymean.com表示特定服务器的因特网地址,这里是DNS域名,也可以使用ip地址表示,至于“DNS跟IP地址的解析”这件事咱们后面慢慢捣腾。
  • /uploads/test.jpg表示资源在这台服务器上的特定位置,这里不仅仅可以映射到简单的文件系统的目录,也可以映射到某些执行程序上,至于“为什么一个URL地址就可以调用服务器上的程序”这个问题,别着急,下一节就会谈到。

实际上,更通用更完整的URL语法由下面9部分构成

// 要死要死,怎么这么长(这是我从书上搬过来的,了解一下就好)
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>

一个URL最重要的部分是<scheme>方案,<host>主机和<path>路径。 其中:

  • 方案用于告诉解析URL的应用程序应该使用什么协议解析资源
  • 某些服务器要求访问者提供用户名和密码,才允许访问资源
  • 主机和端口号告诉应用程序请求资源所在的主机地址和服务端器监听的端口号
  • 路径用于提供资源位于服务器上的具体位置
  • 某些协议需要协议参数来才能访问资源
  • 某些资源,比如数据库服务,可以通过进行查询来缩小请求资源的范围
  • 某些资源,比如HTML文档,可以使用片段来引用整个资源文档中的某个部分(服务器还是会返回完整的资源对象,而片段是用于客户端端整理的)

关于片段,也就是我们常用的锚点,还有一点感慨。我最开始了解到的使用方法只是作为a标签的链接属性占位符,并不知道为何点击会返回顶部,后来发现原来是在localtion.href属性中,如果没有任何标记,触发链接文档就会返回顶部。现在发现原来整个片段标识符都只是整个URL的一部分,果然是十分有趣啊。

URL又分为了相对URL和绝对URL两种。

2.1.1. 绝对URL

绝对路径就是包含了主机名和路径的URL(由于某些浏览器存在自动扩展URL的机制,某些时候并不需要显式声明URL的方案)。

2.1.2. 相对URL

相对URL是不完整的,它省略了URL中的方案和主机名,只保留了某个相对路径。因此,为了获取相对URL资源,必须相对于基础URL进行解析。因此,解析相对URL资源,最重要的目标是找到基础URL:

  • ./相对于当前资源
  • ../相对于当前资源的上一级目录
  • /相对于服务器根目录

下面是在本地增加的一个虚拟域名,服务器环境是Apache,其中/路径指的就是DocumentRoot。一般地,如果指指定了主机名,而没有指定路径名,服务器会默认提供index.htmlindex.php之类的资源(这些默认资源可以在服务器配置文件中进行修改,这里就不深究了)。

<VirtualHost *:80>
    DocumentRoot "D:/wamp/www/shymean"
    ServerName www.shy.com
    DirectoryIndex index.php
    <Directory "D:/wamp/www/shymean">
        Options Indexes FollowSymLinks
        AllowOverride all
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

一般在后台编码中,都会使用一个__ROOT__常量来保存服务器程序的根目录路径,这样就方便日后迁移整个程序了。

2.2. URN

URN是作为特定内容的惟一名称使用,而与目前的资源所在地无关,这样做的好处是可以将资源到处搬移,从文件系统的某个文件夹移动到另一个文件夹,甚至从一台服务器移动到另一台服务器。然而,URN目前并未大规模使用(为整个万维网的所有Web资源都取一个与位置无关且惟一的名字是一个庞大的工程啊~)。因此,不恰当地说,甚至可以使用URL来统称URI。

2.3. 小结

OK,现在,我们学到了第一个非常重要的知识点:URL。工作中难免会遇见一些资源出现404错误,大部分情况下都是我们不小心把相对路径写错了(在刚开始学习Require.js的时候各种花式路径报错,泪奔)。只要正确理解了URL的定义,就可以避免这些小问题了。

最后,尽管资源的种类和生成方式多种多样,在客户端,浏览器为用户提供了一种统一的方式(URL)来访问服务器上的资源;在服务端,开发者也可以使用HTTP作为框架来访问(或者说提供)不同的资源。

3. 资源网关

如果你的好奇心比较强烈,应该会对“服务器是如何调用程序处理数据生成页面”这个问题感兴趣的。这个过程大概如下面描述的这样:

  • 客户端通过HTTP协议连接到应用程序服务器
  • 服务器通过某些方式将请求发送给运行在这台服务器上的某个程序(比如某段PHP代码)。
  • 该程序根据服务器传递的请求处理相关逻辑,并返回对应资源(请求不同,返回的资源也不同,这就是动态资源)
  • 服务器将应用程序生成的资源响应给客户端。

服务器将请求转发给应用程序的方式,书上叫做"网关应用编程接口"。这里有一个网关(Gateway)的概念,我们先可以简单的理解为网关就是将URL翻译成能够返回给客户端资源的某种翻译器,它抽象了一种能够到达资源的方法。(网络不好的时候看见的Bad Gateway,现在终于知道是个什么意思了)

3.1. 资源网关的含义

也许你还会问,服务器不是可以直接访问文件系统提供资源吗,为什么还要把请求转发给其他应用程序?回想我们前面提到的动态资源,当服务器面对复杂的资源请求,单个应用程序可能无法处理这些请求,因此将应用程序进行分工,有的用来提供数据(数据库),有的用来生成文档(PHP代码)等等。服务器向网关发送请求,然后从网关获取响应,(这个角度看网关更像是提供真正服务的服务器,哈哈)。

第一个流行的应用程序编程接口是通用网关接口CGI(Common Gateway Interface),服务器可以用它来装载程序以响应特定URL的HTTP请求。 服务器负责将URL请求传递到应用程序,至于应用程序如何处理特定的URL,则需要我们自己去决定了,这就是我们常说的后台路由:将特定的URL映射到指定的方法执行。之前写的那个简陋的MVC框架,是通过拆分URL中的特定符号来实现的路由选择。这里我们不需要知道后台去怎么实现路由,只需要明白:URL不仅仅代表某个静态资源的路径标识,也可以选择一个后端程序约定的URL作为服务器动态资源的的标识符!

上面这种可以提供资源的网关,称为资源网关。需要注意的是,网关只是一个抽象的概念,并不单单只是能够提供资源,此外还有用于服务器和浏览器连接的协议网关和一些其他的东西,由于这章的主角是资源,因此关于其他的网关类型,咱们后面再提。

3.2. 路由选择

下面是我写的一个很简陋的路由选择类,起因是受不了ThinkPHP那种全是斜杠的URL形式,所以参考Laravel实现的自定义路由。应用程序可以根据服务器提供的URL,执行相应的代码,最后返回数据。好吧,我理解的大体流程就是这个样子,

// Route.class.php 路由类
class Route {
    public $group = '';
    public $ctrl = '';
    public $action = '';

    // 默认
    static public $routes = array(
        '/' => 'Home/Index@index'
    );
    // 解析
    public function getRoute(){
        $url = $_SERVER['REQUEST_URI'];
        if ($url != '/'){
            $url = trim($url, '/');
        }
        $route = self::$routes[$url];

        // 如果是闭包,则执行
        if(is_callable($route)){
            exit($route());
        }

        // 如果是MVC路由,则加载相应控制器
        if (is_string($route)){
            $server = explode('@',$route);
            return array(
                'controller'=> $server[0],
                'action' =>$server[1]
            );
        }
    }
    // 绑定
    static public function bind($url,$action){
        // todo
        // 上面这个todo是要干嘛的我也忘记了
        if (is_array($url)){
            foreach($url as $u){
                self::$routes[$u] = $action;
            }
        }else {
            self::$routes[$url] = $action;
        }

    }
}

// route.php 绑定路由
Route::bind(['/','index'], function(){
    return include(DIST.'index.html');
});
Route::bind('blog/detail', '\App\Home\Controller\BlogController@articleDetail');

4. 小结

关于资源的问题先写到这里,现在主要弄明白了URL和资源网关路由选择这两点,至于服务器内部到底是如何通过网关调用PHP程序的,这个暂时还没有了解到,一直用的WAMP继承环境,里面的配置啥的都没整明白呢,只知道“反正请求来了,服务器就会执行PHP代码”~~,毕竟是在学习HTTP,还是回到正题吧。