Vert.x - vertx-web 路由讲解总结
一、vertx-web
上篇文章我们对 vertx 进行了简单的介绍,并使用 vertx-web 实践了 restFul 接口的书写,本篇文章接着上篇继续讲解 vertx-web 的路由。
对于环境的搭建请参考上篇文章的内容。
二、vertx-web 路由
1. 精确匹配
精确路径是日常开发中常用的方式,精确匹配到某个接口。
router.get("/test1").handler(ctx -> {
ctx.response().end("success");
});
2. 路径前缀的匹配
有的时候可能需要匹配以某某为开头的请求,此时可以使用路径前缀的路由,在vertx 中可以使用 *
代表路径,如:
router.get("/test/*").handler(ctx -> {
String url = ctx.request().absoluteURI();
ctx.response().end(url+"--> success");
});
还可以通过这种方式还可以对接口进行拦截或处理:
router.get("/test/*").handler(ctx -> {
String url = ctx.request().absoluteURI();
if (Objects.isNull(ctx.request().getParam("p"))){
ctx.response().end("参数为空!");
return;
}
ctx.next();
});
router.get("/test/abc").handler(ctx -> {
String url = ctx.request().absoluteURI();
ctx.response().end(url+"--> success2");
});
上面以/test
开头的必须要携带 p
参数:
3. 获取路径上的参数
在 SpringMVC
中我们可以通过 @PathVariable
获取路径上的参数,在 vertx
中同样也是可以的,可以使用 :参数名
的方式,如:
router.get("/pathParam/:name/:age").handler(ctx -> {
String name = ctx.pathParam("name");
String age = ctx.pathParam("age");
ctx.response().end(name+" "+age);
});
4. 使用正则表达式进行匹配
前面有使用 *
的方式来匹配任意后缀的请求,但使用正则的话可以满足基本任何规则的匹配,在 vertx
正则的方法都带有WithRegex
:基本上常用的请求方式都支持正则,如匹配abc
结尾的后缀:
router.getWithRegex(".*abc").handler(ctx->{
String url = ctx.request().absoluteURI();
ctx.response().end(url + "--> success");
});
5. 路由顺序
上面路径前缀的匹配说到可以利用这个进行请求的拦截,其实就是使用的 vertx 中的路由顺序,在 vertx
中,声明同一个 url
不会进行覆盖而是都会加载到该 url 请求的列表中,默认进入第一个请求,可以通过 ctx.next()
执行下一个,如:
router.get("/shunxu").handler(ctx -> {
if (Objects.isNull(ctx.request().getParam("p"))) {
ctx.response().end("没有传递参数!");
} else {
ctx.next();
}
});
router.get("/shunxu").handler(ctx -> {
ctx.response().end("参数: " + ctx.request().getParam("p"));
});
6. 限制请求的MIME类型
有时候我们的接口可能只想接收 Content-Type=application/json
的请求,其他的都不接收,可以通过 consumes
进行限制,并且在 vertx
中还提供了ctx.is
可以用来判断请求的类型是否属于某一类:如:
router.get("/mime")
.consumes("application/json")
.handler(ctx -> {
String header = ctx.request().getHeader("Content-Type");
boolean b = ctx.is("application/json");
boolean b1 = ctx.is("application/json;charset=UTF-8");
boolean b2 = ctx.is("text/json");
ctx.response().putHeader("Content-Type","text/html").end(header + " , " + b + " , " + b1 + " , " + b2);
});
可以看出限制是包含关系。
7. VirtualHost 限制
有些情况我们的接口可能只允许某个 host
的请求,此时就可以使用 VirtualHost
,如限制为127.0.0.1
则使用 localhost
则访问不同 :
router.get("/virtualHost").virtualHost("127.0.0.1").handler(ctx->{
String url = ctx.request().absoluteURI();
ctx.response().end(url + "--> success");
});
8. 上下文数据传递
在上面说请求顺序的时候,涉及到多个 请求handler , 而多个 handler 之间也是可以传递数据的,可以通过 ctx.put
进行:
router.get("/put").handler(ctx->{
String p = ctx.request().getParam("p");
ctx.put("p",p+"123");
ctx.next();
}).handler(ctx->{
Optional.ofNullable(ctx.get("p")).map(String::valueOf).ifPresent(p->{
ctx.response().end(p);
});
});
9. 重定向
在vertx 中直接使用 ctx.redirect
即可实现重定向,如:重定向到百度:
router.get("/redirect").handler(ctx -> {
ctx.redirect("http://www.baidu.com");
});
重定向到内部其他接口:
router.get("/redirect").handler(ctx -> {
ctx.redirect("/test");
});
10. 返回JSON
在 vertx
中返回 json
,可以直接使用提供的 ctx.json
即可,传入一个 JsonObject
:
router.get("/getJson").handler(ctx -> {
JsonObject jsonObject = new JsonObject().put("code", "200").put("message", "success");
ctx.json(jsonObject);
});
11. 失败处理
在逻辑执行时,如果遇到异常此时没有捕获的话就会返回 500
,对此 vertx 提供了 failureHandler
,来捕获异常,有点类似于服务的降级,failureHandler
可以全局捕获也可以针对某个请求:
router.get("/err").handler(ctx -> {
int a = 1 / 0;
}).failureHandler(ctx -> {
ctx.response().end("请求异常:" + ctx.failure().getMessage());
});
全局捕获:
router.route().failureHandler(ctx->{
ctx.response().end("请求异常:" + ctx.failure().getMessage());
});
router.get("/err").handler(ctx -> {
int a = 1 / 0;
});
12. 文件上传
vertx
的文件上传已经帮我们做了简化,直接使用BodyHandler
进行处理即可,文件默认放在当前的 file-uploads
下,并且为了防止重名,存在该目录下的并不是我们上传的文件名,而是 UUID,到我们自己的 handler
中只需使用 ctx.fileUploads
即可获取到上传的文件信息。
注意点:本篇文章采用的 vertx 版本为 4.1.8 ,如果整合 SpringBoot ,需要使用 2.2.X的版本,如果是 2.3.X 上传文件会卡住,解决办法是使用 vertx 4.2.x 的版本在 SpringBoot 2.3.X 是正常的。
router.post("/fileupload")
.handler(BodyHandler.create()
.setHandleFileUploads(true)
.setBodyLimit(5*1024*1024)
.setPreallocateBodyBuffer(true))
.handler(ctx -> {
Set<FileUpload> set = ctx.fileUploads();
System.out.println("上传文件数:"+set.size());
if (set.isEmpty()) {
ctx.response().end("文件为空!");
return;
}
set.forEach(f -> {
System.out.println("name: "+f.name());
System.out.println("fileName: "+f.fileName());
System.out.println("uploadedFileName: "+f.uploadedFileName());
});
ctx.response().end("success");
});
下面是上传到服务的文件:
13. 静态资源
Vertx 带有开箱即用的 StaticHandler 实例处理静态资源,用于处理静态Web资源,默认静态文件目录为 webroot。
router.route("/static/*").handler(StaticHandler.create());
14. 允许跨域
Vertx 中跨域也有开箱即用的 CorsHandler,首先写一个普通接口:
router.get("/cors").handler(ctx -> {
ctx.response().end("success");
});
使用 ajax 调用上面的接口:
$.ajax({
url: "http://localhost:8090/cors",
type: "get",
success: function (result) {
console.log(result)
}
});
浏览器请求:出现了跨域的错误,下面添加跨域的允许:
router.route().handler(CorsHandler.create()
.addOrigin("*")
.allowedHeader(" x-www-form-urlencoded, Content-Type,x-requested-with")
.allowedMethod(HttpMethod.GET)
.allowedMethod(HttpMethod.POST)
.allowedMethod(HttpMethod.PUT)
.allowedMethod(HttpMethod.DELETE));
再次刷新页面: