Code coverage report for thinkjs/lib/Lib/Behavior/CheckRouteBehavior.js

Statements: 100% (98 / 98)      Branches: 100% (64 / 64)      Functions: 100% (11 / 11)      Lines: 100% (98 / 98)      Ignored: none     

All files » thinkjs/lib/Lib/Behavior/ » CheckRouteBehavior.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201          2 2   2   2           49 1   48 48 48 2   46 46 46 46 46   46 28 28 27 27 26         18 18 10       10                   10 10 2   8 8 8 8 8 16 16 6 4   2         8 3 3     8 5   8 8                 18 18 18 33 9 5 5 5   2 2   3 3   5 2       24 24 6     25                 34 34 30 54 48         34 34 34 34               37   3   3 1     2   34   34 21 21 21 13     21 21   13                   27 27 1       26 15   26 26   26 2 3     26 26      
/**
 * 检测路由行为
 * 通过自定义路由识别到对应的URL上
 * @return {[type]} [description]
 */
var url = require('url');
var Dispatcher = thinkRequire('Dispatcher');
 
module.exports = Behavior(function(){
  'use strict';
  return {
    options: {
      'url_route_on': false, //是否开启自定义URL路由
      'url_route_rules': [] //自定义URL路由规则
    },
    run: function(){
      if (!this.options.url_route_on) {
        return false;
      }
      var routes = this.options.url_route_rules;
      var length = routes.length;
      if (length === 0) {
        return false;
      }
      var pathname = this.http.pathname;
      var match;
      for(var i = 0; i < length; i++){
        var route = routes[i];
        var rule = route[0];
        //正则路由
        if (isRegexp(rule)) {
          match = pathname.match(rule);
          if (match) {
            var result = this.parseRegExp(match, route[1], pathname);
            if (result) {
              return result;
            }
          }
        }else{
          //字符串路由
          match = this.checkUrlMatch(pathname, rule);
          if (match) {
            return this.parseRule(rule, route[1], pathname);
          }
        }
      }
      return false;
    },
    /**
     * 解析字符串路由
     * @param  {[type]} rule     [description]
     * @param  {[type]} route    [description]
     * @param  {[type]} pathname [description]
     * @return {[type]}          [description]
     */
    parseRule: function(rule, route, pathname){
      route = this.getRoute(route);
      if (!route) {
        return false;
      }
      pathname = Dispatcher.splitPathName(pathname);
      rule = Dispatcher.splitPathName(rule);
      var matches = [];
      var self = this;
      rule.forEach(function(item){
        var pathitem = pathname.shift();
        if (item.indexOf(':') === 0) {
          if (item.indexOf('\\') === -1) {
            self.http.get[item.substr(1)] = pathitem;
          }else{
            matches.push(pathitem);
          }
        }
      });
      //将剩余的pathname分割为querystring
      if (pathname.length) {
        for(var i = 0,length = Math.ceil(pathname.length)/2; i < length; i++){
          this.http.get[pathname[i * 2]] = pathname[i * 2 + 1] || '';
        }
      }
      route = route.replace(/:(\d+)/g, function(a, b){
        return matches[b - 1] || '';
      });
      this.parseUrl(route);
      return true;
    },
    /**
     * 检测URL是否匹配
     * @param  {[type]} pathname [description]
     * @param  {[type]} rule     [description]
     * @return {[type]}          [description]
     */
    checkUrlMatch: function(pathname, rule){
      pathname = Dispatcher.splitPathName(pathname);
      rule = Dispatcher.splitPathName(rule);
      return rule.every(function(item, i){
        if (item.indexOf(':') === 0) {
          if (item.indexOf('\\') > -1) {
            var type = item.substr(-1);
            var reg;
            switch(type){
              case 'd':
                reg = /^\d+$/;
                break;
              case 'w':
                reg = /^\w+$/
                break;
            }
            if (reg && !reg.test(pathname[i])) {
              return false;
            }
          }
        }else{
          var pitem = pathname[i] || '';
          if (pitem.toLowerCase() !== item.toLowerCase()) {
            return false;
          }
        }
        return true;
      });
    },
    /**
     * 解析转化后的url
     * @param  {[type]} urlInfo [description]
     * @return {[type]}         [description]
     */
    parseUrl: function(urlInfo){
      urlInfo = url.parse(urlInfo, true);
      if (!isEmpty(urlInfo.query)) {
        for(var key in urlInfo.query){
          if (urlInfo.query[key] || !(key in this.http.get)) {
            this.http.get[key] = urlInfo.query[key];
          }
        }
      }
      // 过滤调用pathname最后有/的情况
      var pathname = Dispatcher.splitPathName(urlInfo.pathname);
      this.http.action = Dispatcher.getAction(pathname.pop());
      this.http.controller = Dispatcher.getController(pathname.pop());
      this.http.group = Dispatcher.getGroup(pathname.pop());
    },
    /**
     * 获取route
     * @param  {[type]} route [description]
     * @return {[type]}       [description]
     */
    getRoute: function(route, matches){
      if (isObject(route)) {
        //对应的请求类型
        for(var method in route){
          //由于请求类型没有包含关系,这里可以直接用indexOf判断
          if (method.toUpperCase().indexOf(this.http.method) > -1) {
            return route[method];
          }
        }
        return;
      }
      var routeUpper = route.toUpperCase();
      //RESTFUL API
      if (routeUpper === 'RESTFUL' || routeUpper.indexOf('RESTFUL:') === 0) {
        var group = route.split(':')[1] || C('restful_group');
        route = group + '/' + matches[1] + '/' + this.http.method.toLowerCase() + '?resource=' + matches[1];
        if (matches[2]) {
          route += '&id=' + matches[2];
        }
        //设置变量到http对象上,方便后续使用
        this.http.isRestful = true;
        return route;
      }
      return route;
    },
    /**
     * 正则匹配路由
     * @param  {[type]} matches  [description]
     * @param  {[type]} route    [description]
     * @param  {[type]} pathname [description]
     * @return {[type]}          [description]
     */
    parseRegExp: function(matches, route, pathname){
      route = this.getRoute(route, matches);
      if (!route) {
        return false;
      }
      //替换路由字符串里的:1, :2 匹配都的值
      //如:group/detail?date=:1&groupId=:2&page=:3
      route = route.replace(/:(\d+)/g, function(a, b){
        return matches[b] || '';
      });
      pathname = pathname.replace(matches[0], '');
      pathname = Dispatcher.splitPathName(pathname);
      //将剩余的pathname分割为querystring
      if (pathname.length) {
        for(var i = 0,length = Math.ceil(pathname.length)/2; i < length; i++){
          this.http.get[pathname[i * 2]] = pathname[i * 2 + 1] || '';
        }
      }
      this.parseUrl(route);
      return true;
    }
  };
});