PathVariable及路由设计

项目源码

@PathVariable

在web后端框架中,路由设计是很重要的一步。
各个框架对接口设计的支持大多差不多,其中必然有

1
2
@GET("/users/{id}/")   
@GET("/users/:id/")

这样的标准。

其中{id}:id表示是一个不确定的值,这个随着rustful的兴起也变得常用起来
比如我想要查看id = 1的用户的信息,那么我的请求便是/users/1
如果查看id = 321的用户信息,请求的便是/users/321

那么这个id便是一个不确定量。

于是对于这个方法而言这个id值便是参数,在我们调用时需要传进去。

Spring MVC中,完整的方法是这样

1
2
3
4
@GetMapping("/employer/{phoneNumber}")  
public ResponseEntity<?> findByPhoneNumber(@PathVariable String phoneNumber) {

}

所以这就要求我们在设计框架时还需要利用反射把请求的uri的特定路径值作为参数传进方法中。

route设计

正常我们进行反射调用时,像这样

1
method().invoke(object(), paramters());

首先route中需要有这个对应的Method,其次对应的对象也应该在其中,然后是参数。

于是我设计为

1
2
3
4
5
6
7
8
9
10
11
12
class Route {
//对应的uri
private String path;
//method所在的对象
private Object object;
//对应的路由method
private Method method;
//对应的方法,比如GET,POST等
private HttpMethod httpMethod;
//对应的参数
private Object[] paramters;
}

路由匹配

那么问题来了,对于不同的uri,我们应该怎么匹配路由呢。

我想到的是用正则
设计一个这样的Map

1
Map<Pattern, Route> routeMap = new HashMap<>();

对于/users/{id}/这样的字符串,我们需要产生一个pattern出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//根据参数进行正则替换
public static Pattern pathCompiler(String path, Method method) {
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
if (parameter.getAnnotations() == null) {
continue;
}
Annotation annotation = parameter.getAnnotations()[0];
if (annotation instanceof PathVariable) {
//如果是字符串
if (parameter.getType() == String.class) {
path = path.replace("{" + parameter.getName()+"}","[0-9\\d\\D]*");
}
//如果是数字
else if (parameter.getType() == Integer.class
|| parameter.getType() == Long.class) {

path = path.replace("{" + parameter.getName()+"}","[0-9]*");
}

}
}
return Pattern.compile(path);
}

键为urlPattern,那么在匹配时只要遍历一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//保存着所有的路由
Map<Pattern, Route> routeMap = new HashMap<>();

public static Route findRoute(String path, HttpMethod method) {
for (Pattern pattern : routeMap.keySet()) {
if (pattern.matcher(path).matches()) {
if (routeMap.get(pattern).getHttpMethod().equals(method)) {
/* 进行参数的赋值 */
return routeMap.get(pattern);
}
}
}
return null;
}