上文介绍了vertx的基本用法,并且结合kotlin的特性,为future写了几个比较有用的扩展函数,本文将主要为vertx web开发的路由进行探索。
在官方文档中,引进包 ‘io.vertx:vertx-web:3.4.2’,即可获取路由能力,从官方的例子如下代码:
router.route("/some/path/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.setChunked(true);
response.write("route1\n");
routingContext.next()
});
router.route("/some/path/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.write("route2\n");
routingContext.next()
});
router.route("/some/path/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.write("route3");
routingContext.response().end();
});
从这个例子中我们可以得出结论:在访问/some/path路径的时候,route会依次被访问,next表示下个route将被执行,直到请求end.根据router这个特性,并且换一个思路,可以将route看成http请求的拦截器,在借助反射的可以实现一个简单路由注册工厂。
这里借助一下springMVC的路由结构,首先先定义一个@Controller的注解,标明这个类是个控制器,在定义一下@Route注解,表示是一个路由。其定义如下:
@Target(AnnotationTarget.CLASS)
annotation class Controller(val path:String = "")
@Target(AnnotationTarget.FUNCTION)
annotation class Route(val path:String,
val method: HttpMethodEnum = HttpMethodEnum.POST,
// 是否授权 检查
val isAuthority:Boolean = false,
// 对上传参数进行 dto validate校验,此处填写dto 全部报名
val dto:String = "",
// 是否会堵塞 请求
val isBlock:Boolean = false,
)
在定义这两个注解后,后面去路由工厂的实现就是找Controller,以及下面的Route,其实现过程如下:
/**
* 加载包里面的路由
* @param package: cn.demo.controller.*
*/
private val router = Router.router(vertx)
suspend fun<T> registerRoute(package:String){
val last = package.lastIndexOf(".")
val path = package.substring(0,last).replace(".","/")
// 加载包路径
val root = clazz.classLoader.getResource("")?.path?:""
val paths = vertx.fileSystem().readDir(root + path).await()
if ( package.last() == '*'){
if (file.endsWith(".class") && !file.contains("$")){
var name = file.substring(file.lastIndexOf("/")+1,file.lastIndexOf("."))
if (name.contains("\\")) name = name.substringAfterLast("\\")
val clsName = path.replace("/",".")+"."+name
val newClazz = Class.forName(clsName)
val annotation = newClazz.getAnnotation(Controller::class.java)
// 获取每个route方法
val methods = newClazz.declaredMethods.filter { method -> method.annotations.map { it.annotationClass }.toList().contains(Route::class) }
methods.forEach { method ->
val obj = newClazz.newInstance()
val url = annotation?.path ?: ""
val controller = method.getAnnotation(Route::class.java) ?: throw Exception("@route not exist")
// 从这里开始为请求注册路由
if (controller.isAuthority) router.route(HttpMethod(controller.method.name),url + controller.path).handler{ handle -> RouteIntercept.auth(handle) }
if (controller.dto != "" ) router.route(HttpMethod(controller.method.name),url + controller.path).handler(RouteIntercept.validate(controller.dto))
if (controller.isBlock) router.route(HttpMethod(controller.method.name),url + controller.path).blockingHandler{context ->method.invoke(obj,context)}
else router.route(HttpMethod(controller.method.name),url + controller.path).handler{context ->method.invoke(obj,context) }
}
}
}
注意以上的 registerRoute 方法是suspend的,也就说这个方法在必须异步执行,也就是说必须在kotlin协程或者线程里面执行,这里强烈推荐com.github.isyscore:common-jvm:1.6.1包,这里面开携程只要一个go函数,就可以轻松开一个携程,因此其结果为:
go{
registerRoute("com.demo.controller.*")
}
此方法会异步注册路由,当然不要忘记将router挂到vertx的HttpServer上。
通过以上的路由工厂的注册路由,可以像springMVC一样轻松写一个控制器,例如:
@Controller
class IndexRouter {
@Route("/index", HttpMethodEnum.GET)
fun index(ctx: RoutingContext){
ctx.response().end("hello world!!")
}
}
通过访问http://127.0.0.1:8080/index 即可。
本篇通过vertx 的route注册的机制进行改造,从而实现了route注册工厂,使得将路由与注册进行了剥离,同时根据route的顺序执行性,对请求的拦截也进行了基本的实现。下文将继续对vertx数据访问进行探索。