记一次 Nginx URI rewrite 优化

1. 背景

既上一篇文章记录了组内单页引擎升级路由为 History API 方式,考虑到不支持该方式的浏览器/WebView,需要多页降级。如果不对 Nginx 进行配置,就会出现404,因为多数通过pushState得到的 URL 并没有真实对应的资源。

2. URI 形式

采用 History API 方案的 URL 格式如下:

http[s]://hostname/resource/project/page[/view]?arg1=value1

其中 resource 为资源目录,该目录下放置各个不同的项目文件夹,每个项目对应一个 project 目录。一个 project 中可能会有多个单页应用,每个单页应用对应 URL 中的一个 page。一个单页应用下的不同视图分别对应一个 view。因为采用的单页引擎支持默认路由,所以 view 并非必需。

比如 A 项目下有一个 refund 的单页应用,对应的 HTML 资源为 refund.html,为了 URL 的美观,在 URL 中去掉 html 后缀名。refund 管理两个视图 detail 和 help,且默认视图为 detail。对应该情况,URI 有以下形式:

  • /resource/A/refund
  • /resource/A/refund/detail
  • /resource/A/refund/help

其中第1个 URI 和第2个相同,只是采用了默认视图。这三种 URL 都需要 map 到 refund.html 这个实际存在的 HTML 资源。除去 map HTML 资源,还需要 map 页面请求的各种 JS 和 image 资源。这两种资源都是在页面中通过相对地址引用的,对应的 URL 分别为:

  • ./page/refund.js
  • ./img/icon.png

这两种资源 URL 和上面的 URL 资源组合起来,一共有两种形式的 URL:

  • /resource/A/(page|img)/(refund.js|icon.png)
  • /resource/A/refund/(page|img)/(refund.js|icon.png)

3. 方案

3.1 初始

项目中的实际目录结构如下:

resource_static

--resource

--A

--refund.html

--page

--refund.js

--img

--icon.png

刚开始考虑针对两种类型的资源(HTML 和其他)分别做 rewrite,这样就得到下面的匹配规则:

# match img resource
location ~* ^/resource/([\w-.]+)/([\w-.]+)/(img|page)/(.*)$ {
  rewrite ^/resource/([\w-.]+)/([\w-.]+)/(img|page)/(.*)$ /resource/$1/$3/$4 last;
}
# match HTML resource
location ~* ^/resource/([\w-.]+)/.*$ {
  rewrite ^/resource/([\w-.]+)/([\w-.]+)$ /resource/$1/$2.html last;
  rewrite ^/resource/([\w-.]+)/([\w-.]+)/([\w-.]+)$ /resource/$1/$2.html last;
}
location / {
  root /resource_static/;
  index index.html index;
}

这就是最初也是根据上一步的 URI 分类得出的最直观版本。

3.2 优化

针对3.1中的方案,可以保证新 URI 中所有情况都可以找到对应的资源。但如果单页要引入项目下一个新文件夹中的资源,这时候就要修改上述第1条配置,添加对应目录名,这样扩展性就非常差。

换个角度考虑,其实 rewrite 的目的只是为了去掉由于 URI 中 view 对应的新增虚拟目录导致静态资源寻找多了 page 这一层。所以只要去掉包含这一层的 URL 即可。考虑采用 try_files 直接实现:

location ~* ^/resource/([\w-.]+)/([\w-.]+)(/.*)?$ {
  root /resource_static/;
  try_files /resource/$1/$2/$3 /resource/$1/$3 /resource/$1/$2 /resource/$1/$2.html =404;
}

上面这条配置中,location 后对应的 URI 可以匹配上述所有静态资源的 URI,并且没有写死任何目录名称。try_files 后第1、2个参数分别用来匹配默认视图和指定视图两种 URI 下的静态资源 URI,第3、4个参数用来匹配 HTML 或其它根目录资源,最后一个参数表示如果上述都不匹配,则直接返回404。这样。该条配置同样可以匹配目录下其它子目录或多级子目录的资源匹配。本质做的事情包括两点:针对目录下的资源请求,去掉 URI 中的 page 层;针对单页,添加 html 后缀名。

Nginx 中可以实现重定向的主要有3条指令:return,rewrite 和 try_files。关于3者分别的用法和他们之间的使用场景,可以先去google,本号后面的文章中也会陆续解释。还有一点要注意的是,在[]中写特殊字符,如.时,不需要转义。

4. 总结

综上,其实还是遇到问题不能只做到满足要求即可,而是要进一步深入看有么有更好的办法来解决当前解决方案中的一些瑕疵或者缺陷。借此机会,自己也对 Nginx 配置有了进一步了解。

原文发布于微信公众号 - nginx(nginx-study)

原文发表时间:2018-05-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Linux驱动

27.Linux-DM9000C网卡移植(详解)

上一节 我们学习了:   网卡驱动介绍以及制作虚拟网卡驱动 接下来本节,学习网卡芯片DM9000C,如何编写移植DM9000C网卡驱动程序。 1.首先来看DM9...

2065
来自专栏张戈的专栏

启用WP Super Cache纯代码版本之后的一些优化措施

张戈博客在上个月 28 号启用了 WP Super Cache 代码版,几天下来,虽然小问题不断,但是总体感觉非常不错!不管是前台还是后台,速度都有质的提升,着...

3437
来自专栏你不就像风一样

深入理解跨域SSO原理与技术

​ SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登...

1221
来自专栏王磊的博客

WebClient 访问间歇性返回403解决方案

说明:前段时间做的一个项目莫名的返回403的错误,这种情况也多大是程序员最不喜欢的了,没办法先来分析一下错误信息。之前的代码如下: WebClient webc...

44411
来自专栏草根专栏

Identity Server 4 - Hybrid Flow - Claims

前一篇 Identity Server 4 - Hybrid Flow - MVC客户端身份验证: https://www.cnblogs.com/cgzl/p...

1023
来自专栏Pythonista

Django之COOKIE与SESSION

1、cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要‘保持状态’,因此cookie就是在这个场景下诞生。

1102
来自专栏java学习

面试题13(一个具有生命的线程有哪些状态)

考点:考察求职者对线程的理解 出现频率:★★★ 【面试题解析】线程的状态表示线程在某时间段内进行的活动和将要进行的任务程有创建、就绪、运行、阻塞、死亡5种状态。...

3205
来自专栏与神兽党一起成长

我的解决stackoverflow无法正常登陆问题过程

1.从stackoverflow.com中点击login链接,查看network情况,可以发现几处错误。

862
来自专栏noteless

springmvc 项目完整示例08 前台页面以及知识点总结

<%@ page language="java" contentType="text/html; charset=UTF-8"

530
来自专栏Python

web框架

http协议 HTTP简介 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World ...

3886

扫码关注云+社区