本来,几个演示系统发布在一台服务器上,运行很正常。可是最近合作伙伴说租用的服务器不想续费了,将那几个项目搬到他们公司内部的服务器上。好吧,搬就搬,本身也费不了多少事,无法就是重新发布、重新设置而已。

OK,搬完了,用新地址访问一下试试,结果,问题来了,情况远比我想象的要复杂。

首先,用原来的url不能访问系统了,会跳转到一个什么身份认证系统,合作伙伴说是防火墙的原因,要加端口号,好吧,然后在url中加了端口号8071,这下首页是能加载了,登录也能登录了,但是发现有几个模块不能打开,然后一看浏览器控制台,发现报错:

  1. OPTIONShttp://58.215.219.74/fsjd_service/api/users Origin http://58.215.219.74:8071 is not allowed by Access-Control-Allow-Origin. 
  2. XMLHttpRequest cannot load http://58.215.219.74/fsjd_service/api/users. Origin http://58.215.219.74:8071 is not allowed by Access-Control-Allow-Origin.

是跨域访问的问题了,先看看首页是哪个地址:

url中增加端口号之后提示Origin  is not allowed by Access-Control-Allow-Origin.  Angular JS_AngularJS

并且,其实在js文件中配置的url也是带有端口号的:

baseResourceURL: "http://58.215.219.74:8071/fsjd_service"

但是很显然,在访问个别内容(比如用户管理)的时候,这个配置好的url中的端口号8071没了:

http://58.215.219.74/fsjd_service/api/users

问题找到了,接下来就是该弄清楚这个端口号是什么时候被弄没了。

说明一下,Web前端用到了Angular JS,后面的内容和这个插件密切相关。

先来看看这个关于users的请求在javascript中是是怎么写的:

fs.services.factory('User', ['$resource', '$http', function ($resource, $http) {
	console.log("user的url: "+fs.common.baseResourceURL);
    return $resource(fs.common.baseResourceURL + '/api/users/:id', {id: '@id'});
}]);

fs.services.factory('MultiUserLoader', ['User', '$q', function (User, $q) {
    return function () {
        var delay = $q.defer();
		console.log("开始茶人。。。");
        User.query(function (users) {
            delay.resolve(users);
        }, function () {
            delay.reject('Unable to fetch users');
        });

        return delay.promise;
    }
}]);


首先看这一句:

 return $resource(fs.common.baseResourceURL + '/api/users/:id', {id: '@id'});
定义了名为User的一个资源,注意看其中的url部分,是有一个":id"作为参数的,不管是请求一个user还是全部user。

然后下面的MultiUserLoader中用User.query()方法发送请求。好吧,那么就来看看这个query方法里面到底发生了什么,在angular.js中找了几遍都没有找到query方法,忽然想到“资源”这个概念,应该和resource相关,于是打开angular-resource.js看看,果然找到了:

angular.module('ngResource', ['ng']).
  factory('$resource', ['$http', '$parse', function($http, $parse) {
    var DEFAULT_ACTIONS = {
      'get':    {method:'GET'},
      'save':   {method:'POST'},
      'query':  {method:'GET', isArray:true},
      'remove': {method:'DELETE'},
      'delete': {method:'DELETE'}
    };

既然找到了query方法是在这里的,那就再找找关于url被改动的线索吧,最终,经过各种测试,发现url端口号被拿走的原因了:

          if (angular.isDefined(val) && val !== null) {
		  console.log("if calse...");
            encodedVal = encodeUriSegment(val);
            url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), encodedVal + "$1");
          } else {
		  console.log("else case...");
            url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
                leadingSlashes, tail) {
              if (tail.charAt(0) == '/') {
                return tail;
              } else {
                return leadingSlashes + tail;
              }
            });
          }
		  console.log("New Url2: "+url);

这块代码是在Route.prototype中的,if和else分支中都用replace替换函数对url进行了处理,实测是进了else分支的,从else分支中的正则表达式来看,其中出现了冒号,看来是要做的事情就是替换掉冒号和它后面的内容。这时再联想到前面url中最后的那个“:id”就不难理解了。以下是控制台的输出:

url中增加端口号之后提示Origin  is not allowed by Access-Control-Allow-Origin.  Angular JS_AngularJS_02

可以看到,else分支进了两次,第一次是把“:8071”拿掉了,第二次是把“:id”拿掉了,好了,至此,url被改动的原因找到了,这是angularjs自己的一种处理GET请求参数的机制,只不过,我们的端口号中枪了。。。当然,换个角度想,也可以算是angularjs设计时没有考虑到这种情况吧。

******************************************************************告一段落的分割线*********************************************************************************

那该怎么解决呢?试过修改apache监听端口号之类的方法,但好像问题也比较多,最后没办法,只能考虑修改这个angular-resource.js。

上文提到,else这个分支进了两次,那为什么是两次呢?很显然,是angular针对它认为是参数的两个家伙(:8071和:id)在做循环。那么好了,让我们来看看这个两个循环中的成员:

forEach(self.urlParams, function(_, urlParam){
          val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
可以看出,是在对两个urlParam在做循环,那么这两个urlParam具体是什么内容呢?是“:8071”、“:id”还是“8071”、“id”呢?实测是后者,见控制台输出:
url中增加端口号之后提示Origin  is not allowed by Access-Control-Allow-Origin.  Angular JS_AngularJS_03

好了,那就可以从这个urlParam 下手,思路:如果urlParam是这种几个数字的端口号的格式,那就不做处理,跳出这次循环,进入下一个。然后来看看修改之后的代码:

if(!(/\d{4}/.test(urlParam))){
          val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
	  ...
其中用了4个数字的正则表达式,如果有其他情况,道理是差不多的,修改一下正则表达式即可。

这样修改之后在做前端应用的时候要注意,url中的参数名称不能是4位数字,不然就不会被正确处理,不过话说回来,一般也不会有人用4为数字来给参数起名字吧。


然后测试,OK,不报错了!

总结:虽然正则表达式平时好像用的不多,但它确确实实在很多我们察觉不到的地方发挥着作用。