新浪SAE 如何处理泛域名的问题
今天看到论坛社区总有人问这个问题,特意整理了一下。
针对 qmei.sinaapp.com以及qlove.sinaapp.com这个例子,如何做到点击链接后,跳转到其他网站。
如图
这样子就可以实现这个功能了。
另附录上 config.yaml文件常见配置
使用指南
设置
例子:
name: saetest
version: 1
编辑saetest/1/config.yaml ,增加 handle 段,如:
name: saetest
version: 1
handle:
- rewrite: if(!is_dir() && !is_file()) goto "index.php?%{QUERY_STRING}"
编辑完成后,通过 SVN代码部署 工具提交即可生效。或者你也可以通过 在线代码编辑器 修改config.yaml
如果通过SVN 部署,只需要在默认版本所在目录下,如,您的应用名为 devapp ,默认版本是 3 ,那么请在 devapp/3/ 下创建 config.yaml 文件,按下以下语法编写,然后通过 svn commit 部署完成即可生效。
语法说明
AppConfig 的语法分两种,一种是简单的参数罗列方式,一种是灵活的表达式语法,不同的功能会用到不同的类型的语法。
参数方式
目录默认页面
- directoryindex: file_list
file_list 中各个文件名以空格分隔, directoryindex 在 yaml 文件中仅有一项
例子:
- directoryindex: aaa.php bbb.html
自定义错误页面
- errordoc: httpcode error_file
httpcode 是诸如 404 、 302 之类的 http 响应码, error_file 是服务器以 httpcode 响应请求时响应的文件。 errordoc 在 yaml 中可以配置多项。
- errordoc: 404 /path/404.html
- errordoc: 403 /path/403.html
表达式语法
其他功能需要用到表达式语法,其形式为:
if (expression) do_something
expression 有如下形式:
1) in_header["header_name"] op string_or_digit
2) out_header["header_name"] op string_or_digit
3) path op string
4) query_string op string
5) is_file()
6) is_dir()
关于以上形式说明如下:
1) in_header 是请求头, out_header 是响应头, header_name 是 header 的名字,具体的请求头和响应头参考 RFC 官方文档
2) op 是操作符,有 ~ (正则匹配) !~ (正则不匹配) == (相等,用于字符串和数字) != (不相等,用于字符串和数字) >, >=, <, <= (比较操作符仅用于整形数字)
3) string 是形如 "xxxx" 的字符串
4) string_or_digit 表示 string 或者 digit ,根据 op 的种类,后面跟 string 或者 digit
5) path 是系统宏,表示用户请求的 url 去掉主机部分和查询串后剩下的部分
6) query_string 是系统宏,表示查询串,一般是 url 中问号后面的内容
7) is_file() 和 is_dir 是系统函数,判断 path 是文件还是目录, !is_file() , !is_dir() 分别是其否定形式。
表达式语法用于以下功能:
压缩
- compress: if (single_express) compress
在 compress 中 single_express 表示单一的表达式,不能用 && 做复合, in_header , out_header , path 都可以出现在 single_express 中
例如:
- compress: if(out_header["Content-Length"] >= 10240) compress
- compress: if(in_header["Referer"] == "gphone") compress
- compress: if(path ~ "/big/") compress
URL重写
- rewrite: if (complex_express) goto target_url
在 rewrite 中, complex_express 可以用 && 连接,组成复合表达式。除 out_header (没办法根据响应 header 做重定向) 外都可以出现在 rewrite 的 if 中,并且 path 只能出现一个(如果有多个,只有最后一个生效,其它被忽略),当省略 path 时,表示任意请求。
target_url 表示重定向的目标 url ,在 target_url 可以以 $N 的形式表示 path 中匹配到的内容, %N 的形式表示最后一个 query_string 中匹配到的内容,因为 query_string 可以在 if 中出现多次,以 %{QUERY_STRING} 表示查询串。
例如:
- rewrite: if(query_string ~ "^(so)$" && path ~ "zhaochou$") goto "/url/%1"
- rewrite: if(is_dir( ) && path ~ "urldir/(.*)") goto "/url/$1"
- rewrite: if( !is_file() && !is_dir()) goto "index.php?%{QUERY_STRING}"
指定过期时间和头信息
- expire: if (single_express) time seconds
- mime: if (single_express) type content-type
seconds 是秒数, content-type 是表示文档类型的字符串。
例如:
- expire: if(in_header["referer"] ~ "sina") time 10
- mime: if(path ~ "\.pdf2$") type "application/pdf"
设置响应header Content-Type
如果 url 请求文件的扩展名是 pdf2 ,设置 Content-Type 为 application/pdf
- mime: if(path ~ "\.pdf2$") type "application/pdf"
只要请求 header referer 包含字符串 sina ,就设置 Content-Type 为 text/plain
- mime: if(in_header["referer"] ~ "sina") type "text/plain"
在 expire 和 mime 中 single_express 表示单一的表达式,不能用 && 复合, in_header , path 都可以出现在 single_express 中,并且 op 只能是 ~ 或者 == ,即只支持正则匹配和字符串比较。
基于主机的访问控制
禁止127.0.0.1 访问 private 目录
- hostaccess: if(path ~ "/private/") deny "127.0.0.1"
只允许127.0.0.1 访问 .conf 结尾的文件
- hostaccess: if(path ~ "\.conf$") allow "127.0.0.1"
禁止127.0.0.1 的所有访问(这个要慎用)
- hostaccess: deny "127.0.0.1"
对 cron 任务保护,防止被外部抓取,我们将 cron 任务放在 cron 目录下(sae 中 cron 服务执行时,走的是内部网络)
- hostaccess: if(path ~ "/cron/") allow "10.0.0.0/8" 允许 10 打头的所有 IP
对于屏蔽一组IP 地址,可以写成子网掩码形式,或者将多个 IP 之间加以空格。子网掩码形式如下:
- hostaccess: if(path ~ "/cron/") deny "108.192.8.0/24" 屏蔽 108.192.8 打头的所有 IP
多个IP 形式如下:
- hostaccess: allow "108.134.13.24 108.122.122.13" 允许 108.134.13.24 和 108.122.122.13 这两个 IP
或:
- hostaccess: allow "108.134.13.24","108.122.122.13"
(ip地址需要加引号, all 代表所有 IP 地址,具体可以参考 Apache配置 . allow 是白名单方式, deny 是黑名单)
简单的认证
访问secret 目录需要密码,允许用户 test 用密码 123qwe 访问,用户 coder 用密码 123asd 访问
- passwdaccess: if(path ~ "/secret/") passwd "test:123qwe coder:123asd"
访问.text 结尾的文件需要密码,允许用户 writer 用密码 123zxc
- passwdaccess: if(path ~ "\.text$") passwd "writer:123zxc"
所有访问都要密码,允许用户writer 用密码 123zxc 访问
- passwdaccess: passwd "write:123zxc"
用户的网站后台程序都放在admin 目录下,需要对 admin 目录做密码保护
- passwdaccess: if(path ~ "/admin/") passwd "admin:admin123"
在 hostaccess 和 passwdaccess 中 single_express 表示单一的表达式,不能用 && 复合, in_header , path 都可以出现在 single_express 中,并且 op 只能是 ~ 或者 == ,即只支持正则匹配和字符串比较,并且 if 语句可以省略,表示无条件执行访问控制
备注和说明
更多例子:
目录默认页面
当访问url 没有指定文件时,返回 aaa.php ,如果其不存在,则返回 bbb.html
- directoryindex: aaa.php bbb.html
自定义错误页面
遇到 404 错误,返回 /path/404.html 文件。遇到 403 错误,返回 /path/404.html 文件
- errordoc: 404 /path/404.html
- errordoc: 403 /path/403.html
压缩
当页面内容大于 10K 字节时压缩
- compress: if(out_header["Content-Length"] >= 10240) compress
当请求 header Content-Type 中包含 text 时压缩
- compress: if(out_header["Content-Type"] ~ "text") compress
当响应 header Referer 等于 gphone 时压缩
- compress: if(in_header["Referer"] == "gphone") compress
当请求的 url 包含 “/big/” 时压缩
- compress: if(path ~ "/big/") compress
注:对所有的压缩,请求 header Accept-Encoding 包含 gzip,deflate 是题中之意。
压缩配置注意事项
通常情况,我们根据响应头Content-length ,判断是否需要压缩,例如: if(out_header["Content-Length"]>=10240) compress, 这个静态页面,如 js,css,html 都是没有问题的。
但是对php 脚本,响应 header 中,没有 Content-length 这个头,它使用 Transfer-Encoding: chunked ,这个头表示页面输出用 chunked 编码。此时要实现压缩,可以通过配置 appconfig ,同时在php 脚本中输出相应头的方式实现。
例如在 appconfig 中写 if(out_header["Use-Compress"] == "1") compress ,在需要压缩的 php 脚本中写
压缩生效检查
检查是不是输出了响应头:Content-Encoding: gzip 。
IE下的 HttpWatch 和 FireFox 下的 Firebug 都可以查看页面的响应 header 。
页面重定向
当 url 匹配 urldir/(.*) ,并且 输入 header referer 等于 sina 时,跳转至页面 /usr/$1 , $1 表示刚刚匹配的 urldir/(.*) 中的 (.*) 部分。
- rewrite: if (path ~ "urldir/(.*)" && in_header["referer"] == "sina") goto "/url/$1"
当 url 匹配 urldir/(.*) ,并且请求的是一个目录时,跳转至 /url/$1
- rewrite: if(is_dir( ) && path ~ "urldir/(.*)") goto "/url/$1"
当 url 匹配 path ,并且请求的不是一个文件时,跳转至 /url/query.php
- rewrite: if(! is_file() && path ~ "path") goto "/url/query.php"
当查询串等于so ,并且 url 以 zhaochou 结尾时,跳转至 /url/%1 , %1 表示 query_string 匹配到的部分。
- rewrite: if(query_string ~ "^(so)$" && path ~ "zhaochou$") goto "/url/%1"
当查询串不包含sohu ,并且 url 以 zhaochou 结尾时,跳转至 /url/query.php?%{QUERY_STRING} , %{QUERY_STRING} 表示查询串。
- rewrite: if(query_string !~ "sohu" && path ~ "zhaochou$") goto "/url/query.php?${QUERY_STRING}"
如果 url 既不是文件,也不是目录,跳转至 index.php?%{QUERY_STRING}
- rewrite: if( !is_file() && !is_dir()) goto "index.php?%{QUERY_STRING}"
设置响应头的mime 类型
如果 url 请求文件的扩展名是 pdf2 ,设置 Content-Type 为 application/pdf
- mime: if(path ~ "\.pdf2$") type "application/pdf"
只要请求 header referer 包含字符串 sina ,就设置 Content-Type 为 text/plain
- mime: if(in_header["referer"] ~ "sina") type "text/plain"
页面过期
如果请求 header Referer 包含 字符串 sina ,设置过期时间 10s
- expire: if(in_header["referer"] ~ "sina") time 10
如果 url 以 lib\.js 结尾,设置过期时间 100s
- expire: if(path ~ "lib\.js$") time 100
【FAQ 】 :
1. 如果有形如 path ~ "^(.*)$" 类的请求,一定要加上 is_file 或 is_dir 之类的判断,防止无穷的 rewrite 。
2. path 是用户请求的资源路径,比如请求 http://qlove.sinaapp.com/b/index.php?a=4 ,那么 path 就是 /b/index.php 。
3. 在 goto 语句中,虽然某些时候可以不以 / 开头,但是强烈建议以 / 开头。
生效检查
检查是不是输出了响应头:Cache-Control 。 IE 下的 HttpWatch 和 FireFox 下的 Firebug 都可以查看页面的响应 header 。