Zero冥思录
  • README
  • 1.零之遥(Απόλλων)
    • 1.1.启航:Zero
    • 1.2.曲径通幽:@Path
    • 1.3.四叶葎:Http方法
    • 1.4.思无邪:入参
    • 1.5.孤城暗雪:验证
    • 1.6.潘多拉魔盒:异步
    • 1.7.珠玉在侧:容错
    • 1.8.铁马冰河:细谈Worker
    • 1.9.虚之墙:安全
    • 1.10.珷玞:Jooq
    • 1.11.笙歌散:JSR330/JSR340
    • 1.12.阡陌:Ux编排
    • 1.13.始源之地:Vert.x集成
    • 1.14.俯瞰:配置地图
    • 1.15.鼚乎:Shell
    • 1.16.二向箔:Excel
    • 1.17.隐匿者:Job
    • 1.18.天下:集成
    • 1.19.冰刀:部署
  • 3.氷る世界(Ψυχρός κόσμος)
    • 3.1.贪婪岛:Aeon(K8S 1.25 + CRI-O)
    • 3.2.盖亚:Ceph
    • 3.3.零步进:Harbor
    • 3.4.执子之手:Ceph RBD
Powered by GitBook
On this page
  • 「壹」浅谈RESTful
  • 1.1. SOAP vs RESTful
  • 1.2. 幂等性
  • 「贰」Zero中的Http方法
  • 2.1. 初识
  • 2.2. 完整例子
  • 2.3. 关于GET/DELETE的Body问题
  • 「叁」常用错误响应表
  • 404
  • 405
  • 400
  • 500
  • 「肆」总结
  1. 1.零之遥(Απόλλων)

1.3.四叶葎:Http方法

Previous1.2.曲径通幽:@PathNext1.4.思无邪:入参

Last updated 2 years ago

    东风袅袅泛崇光,香雾空蒙月转廊。只恐夜深花睡去,故烧高烛照红妆。——苏轼《海棠》

  • 项目地址:(子项目:up-apollo)

「壹」浅谈RESTful

    首先,读者需要知道:RESTful是一种架构风格,又称为架构指导规范,不是标准——注意最后一句话,它和我们熟知的XML、SOAP 标准不可同日而语。简单说,RESTful不是标准,它只是一种架构风格,也是随着最近几年的SOA架构流行而风靡起来的。

1.1. SOAP vs RESTful

    RESTful作为一种新的架构风格,它和传统意义上常用的SOAP有什么区别呢?首先读者需要排除一些误区:

  1. 使用了HTTP方法中的PUT/DELETE就叫做RESTful(错的)。

  2. 使用了Json数据格式就是RESTful,而Xml数据格式就是SOAP(错的)。

    SOAP全称为Simple Object Access Protocol——简单对象访问协议,它是用来描述Web服务消息格式的一种规范,基于XML标准定制,在这种Web服务中,所有的通信格式都是XML的。

    RESTful又称为REST、全称为Representational State Transfer——表述性状态传递,它是Roy Fielding博士在2000年中的博士论文中提出来的一种软件架构风格,相比于SOAP,它可以降低开发复杂度,提高系统的可伸缩性。

    简单说,二者真正的区别在——是否对HTTP协议进行了语义上的表述。HTTP协议是应用层协议,设计它的最初目的就是面向应用的标准网络协议。SOAP在表述语义上,并没有使用HTTP协议的语义,而是定义了自己的SOAPHeader、SOAPBody、Envelop 等节点,而这种情况下,SOAP把HTTP协议当做了:“传输层”协议在使用,“应用语义”却隐藏于SOAP规范之中。相反REST回归到HTTP协议的本源,它完全围绕着HTTP协议本身来设计,而不是重新封装,包括设计时去考虑客户端偏好、服务端偏好、条件查询、查询缓存,HTTP幂等性等——这都是REST应用设计对开发人员的挑战。

1.2. 幂等性

    HTTP协议的幂等性(Idempotence)在原生定义中如下:

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request.

    读者需要理解两点:

  • HTTP协议的幂等性原本不是为了REST的Web服务设计的,而是为了分布式计算设计的。

  • 它定义了这样一种特性:针对某一个API,一次请求和多次(n)请求应该具有同样的副作用和结果。

    HTTP协议的原生方法对幂等性的描述如下:

方法名
幂等性

GET

是

HEAD

是

OPTIONS

是

DELETE

是

PUT

是

POST

否

    简单说在HTTP常用的方法中,只有POST方法天生就不具有幂等性,而我们可以在实现上采取另外的技巧去解决它的这个缺陷。

    关于幂等性的场景,举个例子——当你发送请求:GET http://www.bank.com/account/123456,该请求不会改变资源状态,调用一次和调用N次的结果相同,所以是没有副作用的。——这里Todd Wei也强调了,调用N次不影响不是说每次GET的数据结果相同(123456这条记录的数据有可能更新过),而是资源的语义, 账号=133456 的账号信息,不论一次还是多次,它的这种对状态的表述语义不发生变化。


「贰」Zero中的Http方法

2.1. 初识

    Zero是如何实现RESTful中不同的HTTP方法的呢?先看下边代码:

package cn.vertxup.micro.method;

import io.vertx.core.json.JsonObject;
import io.vertx.up.annotations.EndPoint;

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

@EndPoint
public class GetAgent {
    /*
     * 返回值:
     * {
     *     "data": {
     *         "name": ???
     *     }
     * }
     */
    @GET
    @Path("/hi/method/get-first")
    public JsonObject sayGet(@QueryParam("name") String name){
        final JsonObject response = new JsonObject();
        response.put("name", name);
        return response;
    }
}

    上边代码实现了Http方法中的GET方法,当您使用工具postman测试的时候,就可以得到对应的响应结果,并且在启动zero容器时可以看到如下输出:

[ μηδέν ] ( Uri Register ) "/hi/method/get" has been deployed by ZeroHttpAgent, Options = ...

    注意上边的方法定义中是可以传入name参数的,如果发送请求地址改成:/hi/method/get-first?name=Lang,那么将会得到如下输出:

{
    "data": {
        "name": "Lang"
    }
}

    @QueryParam是JSR311中的标准注解,zero对标准注解的使用方法是完全支持的,不仅如此,还扩展了原生注解用于更多业务类场景的数据规范,这些内容将在参数章节单独介绍。

2.2. 完整例子

再看一个比较完整的例子:

package cn.vertxup.micro.method;

import io.vertx.core.json.JsonObject;
import io.vertx.up.annotations.EndPoint;

import jakarta.ws.rs.*;

@EndPoint
public class FourAgent {
    /*
     * 返回值:
     * {
     *     "data": {
     *         "name": ???
     *     }
     * }
     */
    @GET
    @Path("/hi/method/get")
    public JsonObject sayGet(@QueryParam("name") final String name) {
        final JsonObject response = new JsonObject();
        response.put("name", name);
        return response;
    }

    /*
     * 返回值:
     * {
     *     "data": {
     *         ???
     *     }
     * }
     */
    @POST
    @Path("/hi/method/post")
    public JsonObject sayPost(@BodyParam final JsonObject body) {
        final JsonObject response = new JsonObject();
        response.mergeIn(body, true);
        return response;
    }

    /*
     * 返回值:
     * {
     *     "data": {
     *         "key": "???",
     *         ???
     *     }
     * }
     */
    @PUT
    @Path("/hi/method/put/:id")
    public JsonObject sayPut(@BodyParam final JsonObject body,
                             @PathParam("id") final String id) {
        final JsonObject response = new JsonObject();
        response.mergeIn(body, true);
        response.put("key", id);
        return response;
    }

    /*
     * 返回值:
     * {
     *     "data": {
     *         "deleted": ???
     *     }
     * }
     */
    @DELETE
    @Path("/hi/method/delete/:id")
    public JsonObject sayDelete(@PathParam("id") final String id) {
        final JsonObject response = new JsonObject();
        response.put("deleted", id);
        return response;
    }
}

    开发了上述接口过后,可以使用Postman对每个接口进行单独测试,分别得到对应注释中的响应结果,注意此处对于PUT/DELETE使用了@PathParam ,该注解也属于JSR311,唯一不同的是:@BodyParam并不是JSR311中的注解,它是zero框架中的扩展注解,除开这些注释外zero还扩展了更多JSR311中的注解,形成统一规范的“框架协议” ——通常Body参数只在POST和PUT方法中使用,而不在GET/DELETE中使用。

2.3. 关于GET/DELETE的Body问题

    通常来讲,HTTP协议中的GET/DELETE是不推荐带Body的,但是为了满足业务场景的需求,zero定义了两种特殊的注解扩展JSR311 ,在zero中,不论是GET/DELETE还是其他方法,只要使用了下边介绍的注解,都可以直接读取到Http中的Body数据(再强调一遍,下边的方法在JSR311规范中是不存在的)。

  • jakarta.ws.rs.BodyParam:处理字符流请求体数据。

  • jakarta.ws.rs.StreamParam:处理字节流请求体数据(一般用于上传下载)。

zero中对于第三方开源项目的扩展,全部写在第三方的原始包中,这样可能会对读者产生误解,但这种方式更容易说明框架内部某些第三方插件的源头,如扩展的JSR311在javax.ws.rs包中,扩展的Jackson在com.fasterxml.jackson.databind包中。

参考下边代码:

package cn.vertxup.micro.method;

import io.vertx.core.json.JsonObject;
import io.vertx.up.annotations.EndPoint;

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

@EndPoint
public class BodyAgent {
    /*
     * 返回值:
     * {
     *     "data": {
     *         ???
     *     }
     * }
     */
    @GET
    @Path("/hi/body/get")
    public JsonObject sayPost(@BodyParam final JsonObject body) {
        final JsonObject response = new JsonObject();
        response.mergeIn(body, true);
        return response;
    }

    /*
     * 返回值:
     * {
     *     "data": {
     *         ???
     *     }
     * }
     */
    @DELETE
    @Path("/hi/body/delete")
    public JsonObject sayDelete(@BodyParam final JsonObject body) {
        final JsonObject response = new JsonObject();
        response.mergeIn(body, true);
        return response;
    }
}

    通过Postman发送请求则可以得到注解中的响应数据。

「叁」常用错误响应表

    zero 框架在处理一些常见的通用请求时,对容错系统进行了重新设计,如何处理容错信息我会在后边的教程中逐步说明,但是 zero 针对通常会出现的业务场景错误信息会有统一的数据规范。

404

    zero 没有对默认的 404 定制过,如果发送请求,该资源不存在,那么会直接使用 vert.x 中的默认定义,返回下边的HTML片段:

<html>

<body>
	<h1>Resource not found</h1>
</body>

</html>

405

    如果请求资源有数据,但使用的方法不当,那么会直接返回下边信息:

  1. 状态代码 405

  2. 状态信息 Method Not Allowed

  3. 返回的 Http 响应体为空

400

    如果请求格式和定义格式不相同,那么会直接返回下边内容:

  1. 状态代码 400

  2. 状态信息 Bad Request

    返回的Http响应体为:

{
    "code": -60004,
    "message": "[ERR-60004] (JsonObjectSaber) Web Exception occus: (400) - Zero system detect conversation from \"\" to type \"class io.vertx.core.json.JsonObject\", but its conflict."
}

500

    如果出现了常用的 500 异常,则直接返回下边内容:

  1. 状态代码 500

  2. 状态信息 Internal Server Error

    返回的Http响应体为:

{
    "code": -60007,
    "message": "[ERR-60007] (Envelop) Web Exception occus: (500) - The system detected internal error, contact administrator and check details = For input string: \"HX\"."
}

注,message中包含了异常的详细信息,而且在后端日志中可以看到对应的异常堆栈日志。

「肆」总结

    本章我们主要学习 zero 中如何处理RESTful常用的四种基本请求:@GET, @POST, @DELETE, @PUT,这是很多开发人员的常用需求,并且简单谈到了 RESTful 的 Web服务的基本概念。

四叶葎别称四角金,叶四片轮生,影射到四种 Http 方法中,这四种方法也是 RESTful 开发中常用的方法。

https://github.com/silentbalanceyh/vertx-zero-example/