# 路由設計
現代 Web 應用的 URL 十分優雅,易于人們辨識記憶。 路由的表現形式如下:
```
/resources/:resource/actions/:action
http://bladejava.com
http://bladejava.com/docs/modules/route
```
那么我們在java語言中將他定義一個 `Route` 類, 用于封裝一個請求的最小單元,
在Mario中我們設計一個路由的對象如下:
```java
/**
* 路由
* @author biezhi
*/
public class Route {
/**
* 路由path
*/
private String path;
/**
* 執行路由的方法
*/
private Method action;
/**
* 路由所在的控制器
*/
private Object controller;
public Route() {
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Method getAction() {
return action;
}
public void setAction(Method action) {
this.action = action;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
}
```
所有的請求在程序中是一個路由,匹配在 `path` 上,執行靠 `action`,處于 `controller` 中。
Mario使用一個Filter接收所有請求,因為從Filter過來的請求有無數,如何知道哪一個請求對應哪一個路由呢?
這時候需要設計一個路由匹配器去查找路由處理我們配置的請求,
有了路由匹配器還不夠,這么多的路由我們如何管理呢?再來一個路由管理器吧,下面就創建路由匹配器和管理器2個類:
```java
/**
* 路由管理器,存放所有路由的
* @author biezhi
*/
public class Routers {
private static final Logger LOGGER = Logger.getLogger(Routers.class.getName());
private List<Route> routes = new ArrayList<Route>();
public Routers() {
}
public void addRoute(List<Route> routes){
routes.addAll(routes);
}
public void addRoute(Route route){
routes.add(route);
}
public void removeRoute(Route route){
routes.remove(route);
}
public void addRoute(String path, Method action, Object controller){
Route route = new Route();
route.setPath(path);
route.setAction(action);
route.setController(controller);
routes.add(route);
LOGGER.info("Add Route:[" + path + "]");
}
public List<Route> getRoutes() {
return routes;
}
public void setRoutes(List<Route> routes) {
this.routes = routes;
}
}
```
這里的代碼很簡單,這個管理器里用List存儲所有路由,公有的 `addRoute` 方法是給外部調用的。
```java
/**
* 路由匹配器,用于匹配路由
* @author biezhi
*/
public class RouteMatcher {
private List<Route> routes;
public RouteMatcher(List<Route> routes) {
this.routes = routes;
}
public void setRoutes(List<Route> routes) {
this.routes = routes;
}
/**
* 根據path查找路由
* @param path 請求地址
* @return 返回查詢到的路由
*/
public Route findRoute(String path) {
String cleanPath = parsePath(path);
List<Route> matchRoutes = new ArrayList<Route>();
for (Route route : this.routes) {
if (matchesPath(route.getPath(), cleanPath)) {
matchRoutes.add(route);
}
}
// 優先匹配原則
giveMatch(path, matchRoutes);
return matchRoutes.size() > 0 ? matchRoutes.get(0) : null;
}
private void giveMatch(final String uri, List<Route> routes) {
Collections.sort(routes, new Comparator<Route>() {
@Override
public int compare(Route o1, Route o2) {
if (o2.getPath().equals(uri)) {
return o2.getPath().indexOf(uri);
}
return -1;
}
});
}
private boolean matchesPath(String routePath, String pathToMatch) {
routePath = routePath.replaceAll(PathUtil.VAR_REGEXP, PathUtil.VAR_REPLACE);
return pathToMatch.matches("(?i)" + routePath);
}
private String parsePath(String path) {
path = PathUtil.fixPath(path);
try {
URI uri = new URI(path);
return uri.getPath();
} catch (URISyntaxException e) {
return null;
}
}
}
```
路由匹配器使用了正則去遍歷路由列表,匹配合適的路由。當然我不認為這是最好的方法,
因為路由的量很大之后遍歷的效率會降低,但這樣是可以實現的,如果你有更好的方法可以告訴我 :)
在下一章節我們需要對請求處理做設計了~