【Nginx36】Nginx学习:SSI静态文件服务器端包含模块

2023-10-30 17:09:24 浏览数 (1)

Nginx学习:SSI静态文件服务器端包含模块

这个模块让我想到了 2009 年刚刚工作的时候。最早我是做 .NET 的,而第一家公司其实是从 ASP 向 ASP.NET 转型中,因此,还是有不少的 ASP 做的页面。在那个时候,就用到了 SSI 。

这么一说,大家估计也猜到了,这个功能其实是很早的技术了。现在的年轻大佬们可能很多都不知道这个功能。它可以让静态文件,也就是 HTML 文件实现一些简单的文件包含、定义变量、条件判断之类的功能。

这个模块的名称是 ngx_http_ssi_module 模块,它是一个过滤器,用于处理通过它的响应中的 SSI(服务器端包含)命令。目前,支持的 SSI 命令列表不完整。

SSI 模块的指令都可以在 http、server、location 下进行配置。SSI 模块是默认添加的模块,直接就可以使用。我们先来看看它的配置指令。这些配置不是今天的重点,今天的是重点是演示一下如何使用 SSI 。

ssi

启用或禁用响应中 SSI 命令的处理。

代码语言:javascript复制
ssi on | off;

默认值是 off 。要使用 SSI 当然要把这个打开啦。

ssi_last_modified

允许在 SSI 处理期间保留原始响应中的“Last-Modified”标头字段,以促进响应缓存。

代码语言:javascript复制
ssi_last_modified on | off;

默认值是 off 。默认情况下,当响应的内容在处理过程中被修改时,标头字段会被删除,并且可能包含动态生成的元素或部分,这些元素或部分会独立于原始响应而更改。

ssi_min_file_chunk

设置存储在磁盘上的响应部分的最小大小,从这里开始使用 sendfile 发送它们是有意义的。

代码语言:javascript复制
ssi_min_file_chunk size;

默认值是 1k 。

ssi_silent_errors

如果启用,则在 SSI 处理期间发生错误时抑制“[an error occurred while processing the directive]”字符串的输出。

代码语言:javascript复制
ssi_silent_errors on | off;

默认值是 off 。

ssi_types

除了“text/html”之外,还可以处理具有指定 MIME 类型的响应中的 SSI 命令。

代码语言:javascript复制
ssi_types mime-type ...;

默认值是 text/html 。特殊值“*”匹配任何 MIME 类型 (0.8.29)。

ssi_value_length

设置 SSI 命令中参数值的最大长度。

代码语言:javascript复制
ssi_value_length length;

默认值是 256 。

变量

  • $date_local 本地时区的当前时间。格式由带有 timefmt 参数的 config 命令设置。
  • $date_gmt 格林威治标准时间的当前时间。格式由带有 timefmt 参数的 config 命令设置。

SSI 语法

对于上面配置指令和变量的内容咱们就不多说了,直接配置一个服务器来学习 SSI 的使用吧。

代码语言:javascript复制
server{
  listen 8036;
  root html;

  location /ssi/ {
    ssi on;
  }
  
  location ~ .php$ {
    root html;
    fastcgi_pass unix:/var/sock/php-fpm/www.sock;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }
}

非常简单,就是监听了 8036 端口,然后定义了一个 /ssi/ 目录,然后再打开 ssi 功能。因为我们还会用到 PHP ,所以也加上了一个 PHP 的 FastCGI 配置。然后我们去 html 目录下创建一个 ssi 目录,在这个目录下面创建一个 index.html 文件。

代码语言:javascript复制
<!--# include file="header.html" -->
<!--# include file="/ssi/header.php?title=testssi" -->

<!--# set var="name" value="zyblog" -->
<!--# set var="age" value="37" -->

<!--# echo var="name" -->
<!--# echo var="age" -->
<!--# echo var="id" default="123456" -->


<!--# if expr="$age = 37" -->
37
<!--# elif expr="$age != 40" -->
young
<!--# else -->
old
<!--# endif -->

<!--# block name="one" -->
this is block one.<br/>
<!--# endblock -->
<!--# include virtual="/ssi/abc.html" stub="one" -->
<!--# include file="/ssi/123.html" stub="one" -->

看出来这个 SSI 的语法了吧。

代码语言:javascript复制
<!--# command parameter1=value1 parameter2=value2 ... -->

它直接使用 HTML 中的注释,但是在注释中添加了一个 # 符号作为开始符号。接着就是命令以及命令相关的参数 。上面代码中,我们使用 include 命令加载文件,使用 set 定义变量,使用 echo 输出变量。使用 if 命令进行逻辑判断,最后的 block 命令是定义一个块,如果 include 加载的文件不存在时,就使用一个 stub 参数指定一个 block 显示 block 里面的内容。

接下来,准备最上面两个 include 需要加载的文件。

代码语言:javascript复制
<!-- header.html -->
this is header.html!<br/>

header.html 就是显示一句话。

代码语言:javascript复制
<?php
// header.php
$title = $_GET['title'];
?>
title is '<?php echo $title;?>'!<br/>

header.php 文件里面则是接收一个 title 参数 ,然后再把这个 title 参数打印出来。

好了,咱们访问一下这个页面试下吧。

代码语言:javascript复制
➜  ~ curl http://192.168.56.88:8036/ssi/
this is header.html!<br/>



title is 'testssi'!<br/>






zyblog
37
123456



37




this is block one.<br/>


this is block one.<br/>





中间的空行我故意没有去掉,从这里可以看出,SSI 的命令行以及 PHP 代码在解析完成之后是会变成空行的。最下面的两个使用 block 的 include ,在错误日志文件中可以看到相应的错误信息。

代码语言:javascript复制
2022/09/21 23:20:28 [error] 1513#0: *38 open() "/usr/local/nginx/html/ssi/abc.html" failed (2: No such file or directory), client: 192.168.56.1, server: , request: "GET /ssi/index.html HTTP/1.1", subrequest: "/ssi/abc.html", host: "192.168.56.88:8036", referrer: "http://xxx"
2022/09/21 23:20:28 [error] 1513#0: *38 open() "/usr/local/nginx/html/ssi/123.html" failed (2: No such file or directory), client: 192.168.56.1, server: , request: "GET /ssi/index.html HTTP/1.1", subrequest: "/ssi/123.html", host: "192.168.56.88:8036", referrer: "http://xxx"

上面例子中,if 判断貌似没啥用呀,毕竟我们的变量是写死的。然后 SSI 又不能动态接收参数,其实呀,使用 PHP 套上静态页面就可以接收参数了嘛。还是先准备一个 lcoation 来进行测试。

代码语言:javascript复制
location ^~ /ssiphp/ {
  alias html/ssi/;
  fastcgi_pass unix:/var/sock/php-fpm/www.sock;
  fastcgi_index  index.php;
  fastcgi_param  SCRIPT_FILENAME $request_filename;
  include        fastcgi_params;
  ssi on;
}

然后,准备一个 if.php 文件。

代码语言:javascript复制
<?php
$age = $_GET['age'];
?>
<!--# set var="age" value="<?php echo $age;?>" -->
<!--# if expr="$age = 37" -->
37
<!--# elif expr="$age != 40" -->
not 40
<!--# else -->
old or young? old!
<!--# endif -->

测试一下吧,看看 if 的效果怎么样。

代码语言:javascript复制
➜  ~ curl "http://192.168.56.88:8036/ssiphp/if.php?age=37"


37


➜  ~ curl "http://192.168.56.88:8036/ssiphp/if.php?age=49"


not 40


➜  ~ curl "http://192.168.56.88:8036/ssiphp/if.php?age=40"


old or young? old!

返回的结果和我们 if 条件的预期一样。不过需要注意的是,这里的 if 判断条件没有大于、小于,只有等于、不等于、空或非空判断,但判断值可以是正则表达式。

总结

有意思吧,哈哈,早期的我们就是靠这个,实现 ASP 开发中头文件和脚文件的拆分的。不过现在真的很少见到了,毕竟一是纯静态网站已经很少了,二是各种语言框架都已经自带这些功能了。即使是做文章站那种生成纯静态页面的,也是直接去生成整张页面,和这个嵌套也没啥关系。

因此,它的应用场景现在确实很有限了。大家了解一下就好,特别是各位年轻的大佬,如果没见过的话,自己试试,其实也挺好玩的。

参考文档:

http://nginx.org/en/docs/http/ngx_http_ssi_module.html

0 人点赞