meituan/WMRouter

pathHandle怎么处理/a/b/:id这样的注册呢?

Opened this issue · 4 comments

很常见的一个场景是注册的uri为:/a/b/:id,最终能匹配到/a/b/523525之类的path并且将id=523525传递给handle。这样的需求只能通过regxhandle来满足吗?

目前没有这样的功能,需要在现在的基础上开发UriHandler(欢迎提PR~)。可以参考一些后台路由框架的设计,例如express.js,路由匹配用的是 path-to-regexp。当然对于客户端也要考虑下性能问题。

嗯,美团这个路由相对更侧重这种混合交互的场景,我也觉得这种需求很常见。现在我是魔改了regex相关,来满足自己的需求,可以参考下我的魔改版本;
使用上,依旧使用RouterRegex,然后路由地址 http[s]?:/xxx.xxxx.com/video/{id} 使用{id}包裹路径参数,获取参数可以在intent.getStringExtra("{id}")。
我这种做法,就是把xxx/yyy/{id}转换成xxx/yyy/([^/?]*),多个参数也行。
弊端挺明显,所以我称之为魔改,最大的缺陷就是你的路由不能带(xxxxx)这种子表达式,会影响参数顺序。
增加类:
com.sankuai.waimai.router.regex.PathRegexBean

package com.sankuai.waimai.router.regex;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class PathRegexBean {
    private String pathRegex = "";
    private List<String> params = new ArrayList<>();
    private Pattern pattern;

    public String getPathRegex() {
        return pathRegex;
    }
    public void setPathRegex(String pathRegex) {
        this.pathRegex = pathRegex;
        pattern = Pattern.compile(pathRegex);
    }

    public Pattern getPattern() {
        return pattern;
    }

    public void addParams (String paramsName){
        params.add(paramsName);
    }
    public List<String> getParams() {
        return params;
    }

}

魔改以下类
com.sankuai.waimai.router.regex.RegexAnnotationHandler

public class RegexAnnotationHandler extends ChainedHandler {
   // private static Pattern sPathParamsRegex = Pattern.compile("/\\{(.*?)\\}");
    private static Pattern sPathParamsRegex = Pattern.compile("/\{(\D[^/?]*?)\}");
//省略一大堆
    public void register(String regex, Object target, boolean exported, int priority,
                         UriInterceptor... interceptors) {
        PathRegexBean regex2 = converPathParams(regex);
        if (regex2.getPattern() != null) {
            UriHandler innerHandler = UriTargetTools.parse(target, exported, interceptors);
            if (innerHandler != null) {
                RegexWrapperHandler handler = new RegexWrapperHandler(regex2, priority,
                        innerHandler);
                addChildHandler(handler, priority);
            }
        }
    }

    private PathRegexBean converPathParams(String regex) {
        PathRegexBean bean = new PathRegexBean();
        Matcher matcher = sPathParamsRegex.matcher(regex);
        while (matcher.find()){
            String waitReplace = matcher.group();
            bean.addParams(waitReplace.substring(1));
        }
        String realRegex = regex;
        for (String s : bean.getParams()) {
            realRegex = realRegex.replace(s,"([^/?]*)");
        }
        bean.setPathRegex(realRegex);
        return bean;
    }

}

com.sankuai.waimai.router.regex.RegexWrapperHandler

package com.sankuai.waimai.router.regex;

import android.os.Bundle;
import android.support.annotation.NonNull;

import com.sankuai.waimai.router.common.WrapperHandler;
import com.sankuai.waimai.router.components.ActivityLauncher;
import com.sankuai.waimai.router.core.UriHandler;
import com.sankuai.waimai.router.core.UriRequest;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by jzj on 2018/3/26.
 */

public class RegexWrapperHandler extends WrapperHandler {
  //省略.........

    @Override
    protected boolean shouldHandle(@NonNull UriRequest request) {
        Matcher matcher = mPathRegexBean.getPattern().matcher(request.getUri().toString());
        if (matcher.matches()){
            Bundle bundle = extraFill(request);
            for (int i = 0; i < mPathRegexBean.getParams().size(); i++) {
                String key = mPathRegexBean.getParams().get(i);
                request.putField(key,matcher.group(i+1));
                bundle.putString(key,request.getStringField(key));
            }
            return true;
        }
        return false;
    }

    private Bundle extraFill(@NonNull UriRequest request) {
        Bundle extra = request.getField(Bundle.class, ActivityLauncher.FIELD_INTENT_EXTRA, null);
        if (extra == null) {
            extra = new Bundle();
            request.putField(ActivityLauncher.FIELD_INTENT_EXTRA, extra);
        }
        return extra;
    }
}

写的挺好的,主要问题在于你这里是直接在原有的regex字段上增加了你设定的 “{}” 这种非标准语法,和正则表达式自身语法冲突了,这样会有两个问题,一个就是你说的问题,正则表达式不能用小括号,会导致参数匹配不对;另一个问题就是正则表达式也不能用大括号,可能会被错误的识别成参数。

path-to-regexp的实现是定义了一个专用的path语法,统一转成正则,这样就不会和正则表达式自身的语法冲突了。所以还是得新增注解或者新增注解参数,自己定义语法,才能比较好的解决问题,不过实现有点费劲……

嗯,对于破坏原有的{}问题,我简单修改了下:
sPathParamsRegex = Pattern.compile("/\{(\D[^/?]*?)\}");
因为修改过后,路径参数就规定为非数字开头的命名,也符合预期。
数字开头的就保留,不做替换,就不会破坏原有的{1},{1,2},{2,}这种玩法了
@jzj1993