OSChina 是用 Java 开发的,以前用 Java 开发的 Web 项目,URL 一般都是 xxx.jsp 或者是 xxx.do 之类的,后者是因为用了 Struts 框架的缘故。后来形如 /xxxx/xxxx 这样的 RESTful 风格 URL 很流行。而 OSChina 也是采用 RESTful 风格的这种 URL,看起来比较专业,呵呵。
在 Java 里要进行自定义 URL 的处理,一般用 Servlet 或者 Filter 来处理,而 Servlet 主要用于处理有固定前缀和后缀的 URL 地址,例如 /action/xxxx 和 xxxx.do 之类的请求。但一般 Servlet 不适合处理 /* 这样的请求,因为系统中总会有某些 URL 是需要特殊处理,例如静态文件就不希望走相同的逻辑,因为它没有“链”的结构,接收了请求就必须处理,不能再返回其他 Servlet 。而 Filter 则不同,它是一种责任链的设计模式,可以将请求传递到链中的下一个处理者。
关于 Servlet 和 Filter 还可阅读我之前的一片文章:初学 Java Web 开发,请远离各种框架,从 Servlet 开发
因为二者的不同,OSChina 采用的是 Filter 实现自定义 URL 处理。
先来看 OSChina 的 URL,例如:
http://www.oschina.net/project/tag/355/html5
URL 一般包含这么几部分内容:
部分 | 对应 HttpServletRequest 方法 | 示例中的取值 |
schema | getSchema() | http |
hostname | getServerName() | www.oschina.net |
port | getServerPort() | 80 |
path | getRequestURI() | /project/tag/355/html5 |
在这里我们只关心 hostname 和 path 这两部分。
[hostname]
OSChina 上跟应用有关的有多个二级域名,分别是 www.oschina.net、my.oschina.net、m.oschina.net 和 wap.oschina.net 。这几个 hostname 对应了不同的模版路径。这些对应的路径在 web.xml 的 Filter 配置里:
<filter> <filter-name>global</filter-name> <filter-class>net.oschina.OSChinaFilter</filter-class> <init-param> <param-name>domain</param-name> <param-value>oschina.net</param-value> </init-param> <init-param> <param-name>template-path-prefix</param-name> <param-value>/WEB-INF</param-value> </init-param> <init-param> <param-name>ignore</param-name> <param-value>/action/,/uploads/,/img/,/css/,/js/,/test/</param-value> </init-param> <init-param> <param-name>ignoreExts</param-name> <param-value>ico,jpg,gif,png,bmp,doc,xls,pdf,zip,rar</param-value> </init-param> <init-param> <param-name>default</param-name> <param-value>/www</param-value> </init-param> <init-param> <param-name>m</param-name> <param-value>/templates/mobile</param-value> </init-param> <init-param> <param-name>wap</param-name> <param-value>/wap</param-value> </init-param> <init-param> <param-name>my</param-name> <param-value>/templates/home</param-value> </init-param> </filter>
在这里,每个二级域名都对应了一个模版目录,其中 default 是表示其他没有配置的主机则使用该模版目录。有了这个东西,当需要开发一种新的客户端界面的时候,就不会跟原有的页面冲突,而且 URI 还可以保持一致,如:
http://www.oschina.net/news/34327/be-nice-to-programmers
http://m.oschina.net/news/34327/be-nice-to-programmers
这是同一篇新闻在不同的终端下的版本,前者是PC上,后者是智能手机上。
具体实现的代码请看 URLMappingFilter.java
[path]
接下来才是对 URL 的解析,还是前面那个新闻的 URL 地址,我们只关注最后面一部分,那就是 /news/34327/be-nice-to-programmers 。这个字符串可以通过 request.getRequestURI() 方法来获取得到。
获取到的 Path 信息用 / 字符将它拆分成字符串数组 uri_parts,对应的值:
uri_parts[0] = 'news'
uri_parts[1] = '34327'
uri_parts[2] = 'be-nice-to-programmers'
假设当前访问的是 www.oschina.net ,对应存放模版文件的路径是 /www,那么 URLMappingFilter 会按照如下顺序来查找这个 URI 对应的模版文件:
1. /www/news/34327/be-nice-to-programmers/index.vm
2. /www/news/34327/be-nice-to-programmers.vm
3. /www/news/34327/index.vm
4. /www/news/34327.vm
5. /www/news/index.vm
6. /www/news.vm
7. /www/index.vm
大家应该能看明白上面的顺序,等于是目录的递归。对应新闻的 URL 在上述路径中的第 5 步找到了对应的 index.vm 文件,然后 URLMappingFilter 就请请求 forward 给这个 index.vm 文件,同时把后面的两个值 34327 和 be-nice-to-programmers 通过参数的方式传递给 index.vm,最终的调用请求变成
index.vm?p1=34327&p2=be-nice-to-programmers
这个过程是在服务器端实现的,浏览器并不知情。
有人会问这么遍历路径性能会不会受影响,当然会,但是微乎其微,而且 OSChina 中的路径最多也就是三级,另外我们还将这个结果缓存起来,因此不会有什么影响。
假设你打开浏览器随便访问 OSChina 的任何一个地址,例如是
http://www.oschina.net/a/b/c/d/e/f/g
其实调用的是
index.vm?p1=a&p2=b&p3=c&p4=d&p5=e&p6=f&p7=g
其对应关系非常清晰,这也是 OSChina 内部的约定,不做任何配置。
[静态文件]
我们在开发的时候,生产环境中是由 Nginx 直接处理静态文件,因此静态文件的请求不会送到后端的应用来。但是在进行开发过程中没必要按照 Nginx,只需 Tomcat 即可。导致的问题是所有的静态文件也都会经过这个 URLMappingFilter,因此我们必须在过滤器中对这些文件进行识别,不予处理。
前面的配置中有 ignore 和 ignoreExts 两项,这两项配置分别对应了要忽略的 URI 的前缀和后缀。
Filter 在执行的过程中先要判断当前请求的 URI 是否符合 ignore 和 ignoreExts 中定义的内容,如果符合则将请求返回到过滤器链中进入下一步处理。
主要就这么些内容,有不明白的请多读几遍,实在不明白咱再讨论。哈:)
完整实现请看 URLMappingFilter.java
安卓客户端看blog ,代码都是没有规律的排序的 O(∩_∩)O######回复 @红薯 : 吼吼,要不改成.exe以前这么玩的。######请问这个跟本文有何关系?######简单易懂。######支持OSC自爆######学习 学习!######学习了######支持,可惜偶不搞java######我觉得如果要像这样生成二次请求,那第一次请求的时候参数应该不要明确显示出来,至少不要那么容易辨别,而且第一次请求也太长.可以对第一次请求进行加密,也只有一级.如这样: http://www.oschina.net/d6e3n8d0g9a0######回复 @红薯 : 他的意思是说把path给进行编码了吧######不明白你的意思哦######很清晰!###### 不懂JAVA######mark
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。