跳转到内容

用 Router 组织服务端路由

这篇教程的目标很明确:在你已经能启动 Feat HTTP 服务的前提下,进一步学会把一个处理器扩展成一组可维护的路由规则。

  • 创建一个 Router
  • 注册静态路由、路径参数路由和按 HTTP 方法区分的路由
  • 使用 Session 保存简单状态
  • 为指定路径添加拦截器

下面的例子把多个请求路径交给 Router 统一管理:

RouterDemo.java
import tech.smartboot.feat.Feat;
import tech.smartboot.feat.router.Router;
public class RouterDemo {
public static void main(String[] args) {
Router router = new Router();
router.route("/", ctx -> {
ctx.Response.write("root: " + ctx.Request.getRequestURI());
});
router.route("/users/:id", ctx -> {
ctx.Response.write("userId: " + ctx.pathParam("id"));
});
router.route("/articles", "GET", ctx -> {
ctx.Response.write("list articles");
});
router.route("/articles", "POST", ctx -> {
ctx.Response.write("create article");
});
Feat.httpServer().httpHandler(router).listen(8080);
}
}

这里有四个关键信息:

  • new Router() 创建路由容器
  • route("/path", handler) 注册一个路径
  • route("/path", "GET", handler) 可以按 HTTP 方法注册不同处理器
  • Feat.httpServer().httpHandler(router) 让 Router 接管请求分发

启动程序后,依次访问下面几个请求:

Terminal window
curl http://localhost:8080/
curl http://localhost:8080/users/42
curl -X GET http://localhost:8080/articles
curl -X POST http://localhost:8080/articles

预期结果:

root: /
userId: 42
list articles
create article

到这里你已经验证了 Router 的三个最核心能力:

  • 路径分发
  • 路径参数提取
  • 同一路径下的多 HTTP 方法分发

在 Feat Router 里,最常用的是三种模式:

模式含义示例
/users精确匹配只匹配 /users
/users/:id路径参数匹配 /users/42
/static/*通配符匹配 /static/js/app.js

Router 内置了会话能力。最简单的用法是先写入 Session,再从另一个接口读取:

RouterSessionDemo.java
import tech.smartboot.feat.Feat;
import tech.smartboot.feat.core.server.Session;
import tech.smartboot.feat.router.Router;
public class RouterSessionDemo {
public static void main(String[] args) {
Router router = new Router();
router.route("/createSession", ctx -> {
ctx.session().put("flag", "ready");
ctx.Response.write("session created");
});
router.route("/session/check", ctx -> {
Session session = ctx.session();
ctx.Response.write("flag: " + session.get("flag"));
});
Feat.httpServer().httpHandler(router).listen(8080);
}
}

你可以按下面顺序验证:

Terminal window
curl -i http://localhost:8080/createSession
curl -i http://localhost:8080/session/check

在真实浏览器里验证会更直观,因为浏览器会自动带上 Cookie。命令行测试时,如果你要跨请求保留 Cookie,需要自己维护 Cookie。

拦截器适合做鉴权、日志、前置检查这类横切逻辑。

RouterInterceptorDemo.java
import tech.smartboot.feat.Feat;
import tech.smartboot.feat.router.Router;
public class RouterInterceptorDemo {
public static void main(String[] args) {
Router router = new Router();
router.addInterceptor("/admin/*", (context, completableFuture, chain) -> {
String token = context.Request.getHeader("X-Token");
if (!"feat".equals(token)) {
context.Response.write("forbidden");
completableFuture.complete(null);
return;
}
chain.proceed(context, completableFuture);
});
router.route("/admin/ping", ctx -> {
ctx.Response.write("ok");
});
Feat.httpServer().httpHandler(router).listen(8080);
}
}

验证:

Terminal window
curl http://localhost:8080/admin/ping
curl -H 'X-Token: feat' http://localhost:8080/admin/ping

预期第一条返回 forbidden,第二条返回 ok

优先检查:

  1. 你是否真的把 router 传给了 httpHandler(...)
  2. 请求路径是否与注册路径完全一致
  3. 路径参数是不是写成了 :id 而不是别的形式

同一路径注册多个处理器,结果不符合预期

Section titled “同一路径注册多个处理器,结果不符合预期”

如果你需要区分请求方法,显式写上 "GET""POST" 这类 method 参数,不要只注册一个通用路径。

浏览器会自动维护 Cookie,但 curl 默认不会跨请求保存 Cookie。排查 Session 问题时,优先用浏览器,或者显式处理 Cookie。