1.2.曲径通幽:@Path

    清晨入古寺,初日照高林。曲径通幽处,禅房花木深。山光悦鸟性,潭影空人心。万籁此都寂,但余钟磬音。——《题破山寺后禅院》

「壹」关于URI

1.1. URI/URL/URN

    最初认识RESTful,读者可能对URI/URL/URN三个概念不是很清楚,这里简单对比一下,首先读者必须清楚一件事情:URL和URN都是URI的子集 ,换言之URL和URN都是URI,但反过来说就不成立,它们的结构如下:

图片地址:https://www.cnblogs.com/taoist123/p/11196971.html

    URI的全称是Uniform Resource Identifier,它是“统一资源标识符”,在整个互联网上所有的访问地址都抽象成了资源 ,这些资源拥有唯一的标识符,我们通过该标识符来访问资源,这个标识符就是URI。而URI在表述资源时分离成了两个子类型,一个就是我们常听到的URL,而另外一个就是URN。

    URLURI名字相近,它的全称是Uniform Resource Location ,意思是:“统一资源定位符”,我们通常可以通过URL来准确定义一个资源的位置,如:http://localhost:6083/user/1。它主要包含几部分:

  • http:协议名称,在RESTful中常用的是http/https协议,而互联网上还可以使用ftpstmp等各种协议。

  • localhost:主机的域名,这个域名可以是IP地址,如:10.0.0.100这种格式。

  • 6083:端口号,如果互联网上的URL不带端口号,就使用默认端口:80,如:http://localhost/user/1,这种格式使用80端口。

  • 后边的/user/1就是当前访问资源的名称,它精确定位了所访问资源的路径。

有一点需要注意,在RESTful中通常会使用类似:/user/:id 来定义一个Api,它使用不同的参数来读取不同的用户信息,这种情况下:/user/1/user/2 表示两个URI,而不是一个,而您后端的程序逻辑可能定义的是一个。

    URN,最后谈谈URN,它的全称是Uniform Resource Name ,意思是:“统一资源名”,那么它有什么作用呢?实际上URN比URL更方便去定位资源,它只需要知道一个域名就可以了,直接隔离了资源本身所在的位置。换句话说,如果你把资源从host1切换到host2 ,这种情况下使用URL的话是需要变更访问地址的,如果是直接使用URN,那么名称不变就可以不用做任何操作,程序依旧会访问URN背后映射的真实资源地址。URN的通用语法如下:

< URN > ::= "urn:" < NID > ":" < NSS >

    其中NID是命名空间标识符,NSS则是标识命名空间的特定字符串,可以当做唯一名称。

1.2. 重复?

    既然URI表示一个资源的唯一标识,那么在开发过程中难免会遇到重复的情况,在真实场景中,这种重复规则会给开发人员带来一定的诊断难度。

定义Curl访问

/user/get

GET /user/get

/user/:id

GET /user/1

    上述两个例子实际上就出现了重复性冲突,当你发送第一个请求时,容器无法判断这里的get是表示一个固定路径,还是表示:id = get的路径。

定义Curl访问

/user/get

POST /user/get

/user/:id

GET /user/1

    而上述两个例子则不会出现重复,原因在于它们所使用的HTTP方法不同,简单说在URI的描述中,还有一个维度容易被开发人员忽略,就是HTTP方法 ,同一个路径下方法不同,那么该URI也属于不同的资源标识符。

开发过程中,开发人员一定要注意URI的重复问题,重复的URI会导致请求出现不可预知的二义性情况,而真正避免重复性URI的方式不是开发,而是设计。


「贰」@Path

    了解了基本概念后,那么接下来看看在Zero中如何定义一个RESTful的URI,Zero本身支持了JSR311的规范,所以在定义URI 时直接使用JSR311中的表述方式来定义结构中的URI地址,主要使用注解:jakarta.ws.rs.Path

    jakarta.ws.rs.Path注解在JSR311中可以使用在两个地方:

  • 直接使用在类名中对类进行注解。

  • 使用在方法名中堆方法名进行注解。

2.1. Zero中的规则

    Zero在实现JSR311时对本身被注解的对象是有一定限制的,限制规则如下:

  1. 类名中的@Path是可选的,可以使用也可以不使用,使用过后当前类中的所有方法注解对应的URI都会带上类名中的注解前缀。

  2. 当方法注解了@Path过后,必须使用@GET, @DELETE, @POST, @PUT注解对该方法进行分流,不可以定义全支持类型的RESTful Api,未使用上述四个注解的@Path会直接被忽略掉。

  3. Zero会对 @Path中的路径进行自动运算,所以开发人员不用担心在路径中是否多写了一个/,或者少写了一个/,以及是否使用了双斜杠//

2.2. 启用类注解

    在项目中书写下边代码:

package cn.vertxup.micro.path;

import io.vertx.up.annotations.EndPoint;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@EndPoint
@Path("/path")
public class ClassPathAgent {
    /*
     * 响应:
     * {
     *      "data": 100
     * }
     */
    @Path("100")
    @GET
    public Integer say100() {
        return 100;
    }

    /*
     * 响应:
     * {
     *      "data": 200
     * }
     */
    @Path("//200")
    @GET
    public Integer say200() {
        return 200;
    }
}

    书写好过后,可以从启动控制台中看到如下信息:

[ ZERO ] ( Uri Register ) "/path/100" has been deployed by ZeroHttpAgent
[ ZERO ] ( Uri Register ) "/path/200" has been deployed by ZeroHttpAgent

    启动好了过后,使用Postman发送请求就可以得到注释中的响应结果。

2.3. 不启用类注解

    在项目中书写下边代码:

package cn.vertxup.micro.path;

import io.vertx.up.annotations.EndPoint;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@EndPoint
public class MethodPathAgent {
    /*
     * 响应:
     * {
     *      "data": 300
     * }
     */
    @Path("path/300")
    @GET
    public Integer say300() {
        return 300;
    }

    /*
     * 响应:
     * {
     *      "data": 400
     * }
     */
    @Path("/path/400")
    @GET
    public Integer say400() {
        return 400;
    }
}

    书写好过后,可以从启动控制台中看到如下信息:

[ ZERO ] ( Uri Register ) "/path/300" has been deployed by ZeroHttpAgent, 
[ ZERO ] ( Uri Register ) "/path/400" has been deployed by ZeroHttpAgent, 

    启动好了过后,使用Postman发送请求就可以得到注释中的响应结果。

2.4. 重复?

    再谈重复是因为在前边提到过,一个URI地址是不允许重复的,而Zero模型支持优先级注解模式,简单说,当容器发现两个优先级不同的URI时,会使用优先级高的@Path 而忽略优先级低的,这种方式目前在zero-crud(Zero Extension扩展)中使用很多。zero-crud 提供了一个模块中常用的添加、删除、查询、修改等15个基本的Api,这些Api有时候会无法满足我们的需求——这种情况下,可采用优先级模式进行重写,重写过后会执行开发人员的代码逻辑,而不是使用原始zero-crud的代码逻辑。

    在项目中书写下边代码:

package cn.vertxup.micro.path;

import io.vertx.up.annotations.EndPoint;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@EndPoint
@Path("/path")
public class DuplicatedAgent {

    /*
     * 响应:
     * {
     *      "data": false
     * }
     */
    @Path("500")
    @GET
    public boolean sayFalse() {
        return false;
    }

    /*
     * 响应:
     * {
     *      "data": "Hello World"
     * }
     */
    @Path("500")
    @GET
    public String sayHello() {
        return "Hello World";
    }
}

    然后读者会发现,有时候启动返回方法一的响应,而有时候会返回方法二的响应,那么这种情况下,/path/500就出现了二义性,也就是前文提到的重复,那么将上述代码修改成:

package cn.vertxup.micro.path;

import io.vertx.up.annotations.Adjust;
import io.vertx.up.annotations.EndPoint;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@EndPoint
@Path("/path")
public class DuplicatedAgent {

    /*
     * 响应:
     * {
     *      "data": false
     * }
     */
    @Path("500")
    @GET
    @Adjust(4_999_999)
    public boolean sayFalse() {
        return false;
    }

    /*
     * 响应:
     * {
     *      "data": "Hello World"
     * }
     */
    @Path("500")
    @GET
    public String sayHello() {
        return "Hello World";
    }
}

    这种情况下在控制台可以看到两个URI被注册:

[ ZERO ] ( Uri Register ) "/path/500" has been deployed by ZeroHttpAgent, ... order = 5000000
[ ZERO ] ( Uri Register ) "/path/500" has been deployed by ZeroHttpAgent, ... order = 4999999

    此时任何时候发请求都会得到响应:

{
    "data": false
}

虽然Zero支持优先级模式,但是依旧不推荐在项目设计中使用重复的URI,优先级模式存在的目的是为了让Zero Extension中的 zero-crud,静态URI,动态URI可共存并且相互之间实现重写,并不是为开发人员在设计缺陷中找借口。

「叁」关于Order

    前边一个章节使用了路由编排功能,并且使用了Zero中的特殊注解:io.vertx.up.annotations.Adjust,该注解中的值对应到vertx-webRoute所需的order属性,order值越小,优先级越高,这种模式仅在重复URI中生效。参考下表看看Zero中定义的默认order值(io.vertx.up.eon.Orders中定义):

变量名含义

MONITOR

1_000_000

监控专用处理器

CORS

1_100_000

跨域访问专用处理器

COOKIE

1_200_000

Cookie专用处理器

BODY

1_300_000

读取Http Body专用处理器

CONTENT

1_400_000

路径解析器专用处理器

SESSION

1_600_000

会话管理器专用处理器

FILTER

1_800_000

JSR340拦截过滤器专用处理器

SECURE

1_900_000

安全管理器专用处理器

SIGN

3_000_000

接口数字签名专用处理器

EVENT

5_000_000

编程模式专用处理器(开发人员专用)

DYNAMIC

6_000_000

动态路由专用处理器,zero-jet 专用

MODULE

10_000_000

扩展模块专用处理器,zero-crud 专用

    从表格中的路由编排可以看到,如果开发人员需要使用io.vertx.up.annotations.Adjust注解进行路由优先级编排时,推荐设置的值范围在SIGNEVENT 之间,这也是为什么在例子中使用4_999_999的原因,至于order本身的教程,读者可以参考vert.x的官方文档讲解vertx-web部分的内容。

「肆」总结

    本章节我们学习了Zero中如何定义不同的URI,这也是开发RESTful接口的基础,对于重复URI@Adjust 注解的使用,作者还是推荐从设计层面解决这个问题,而不是在一个项目中使用大量的重复URI造成二义性问题。Zero支持JSR311 规范来开发标准的RESTful接口,也是希望开发人员减少学习曲线,到目前为止只出现了三个Zero中定义的注解:

  • io.vertx.up.annotations.Up:只会在启动器中使用,一次性的。

  • io.vertx.up.annotations.EndPoint:每个Api定义中必须包含该注解,否则Zero会忽略。

  • io.vertx.up.annotations.Adjust:处理优先级专用注解,和vertx-web中的order属性绑定实现URI的优先访问。

@Path 本名翻译为:“路径”,按照URI规范,我们在寻找资源时会按资源路径进行“定位”,所谓曲径通幽,本意为:弯曲的小路通到风景美丽的地方,当然通过这个章节我们也敲开了Zero的大门。   

Last updated