Forest Forest
💒 首页
  • v1.5.30
  • v1.5.28
  • 🎄 ForestX
🌰 案例
💖 支持
🛫 更新记录
🧢 开发团队
⚒️ 参与贡献
  • MaxKey - 业界领先的身份管理和认证产品 (opens new window)
  • Snowy - 国内首个国密前后端分离快速开发平台 (opens new window)
  • Eoapi - 一个开源、可拓展的 API 工具平台 (opens new window)
  • Gitee (opens new window)
  • Github (opens new window)
💒 首页
  • v1.5.30
  • v1.5.28
  • 🎄 ForestX
🌰 案例
💖 支持
🛫 更新记录
🧢 开发团队
⚒️ 参与贡献
  • MaxKey - 业界领先的身份管理和认证产品 (opens new window)
  • Snowy - 国内首个国密前后端分离快速开发平台 (opens new window)
  • Eoapi - 一个开源、可拓展的 API 工具平台 (opens new window)
  • Gitee (opens new window)
  • Github (opens new window)
  • 序言

    • 🎁 新手介绍
    • 📖 文档
    • 🌰 使用案例
    • 🕵️‍ 关于作者
    • 👨‍🎓 贡献者列表
  • 入门

    • 🎬 安装配置说明
    • 🏹 Springboot环境安装
    • 📐 Springboot环境配置
    • 🎯 Springboot环境使用
    • 🏹 Springboot3环境安装
    • 📐 Springboot3环境配置
    • 🎯 Springboot3环境使用
    • 🏹 Spring环境安装
    • 📐 Spring环境配置
    • 🎯 Spring环境使用
    • 🏹 Solon环境安装
    • 📐 Solon环境配置
    • 🎯 Solon环境使用
    • 🏹 原生Java环境安装
    • 📐 原生Java环境配置
    • 🎯 原生Java环境使用
    • 🧬 编程式接口
  • 配置项

    • 👜 Springboot环境配置项
    • 👝 Spring环境配置项
    • 👜 Solon环境配置项
    • 🎒 原生Java环境配置项
    • 📚 配置优先级/作用域
  • 声明式接口

    • 🧱 构建接口
    • 🍀 请求方法
    • 🚚 请求地址
    • 🎈 URL 参数
    • 🍭 请求头
    • 👔 请求体
      • @Body 注解
      • 表单格式
      • JSON格式
        • @JSONBody注解修饰对象
        • @JSONBody注解修饰键值对
        • @JSONBody注解修饰集合对象
        • @JSONBody注解修饰字符串
        • @Body注解 + contentType
      • XML格式
        • @XMLBody注解修饰对象
        • @XMLBody注解修饰字符串
      • Protobuf格式
      • 二进制格式
      • data 属性
      • 请求体类型
        • @BodyType 注解
        • 可指定的请求体类型
        • 指定 Encoder
        • JSON 编码器快捷注解
      • 延迟请求体参数
    • 🍮 后端框架
    • 🧁 接口注解
    • 📬 接收数据
    • 🍛 数据转换
    • 🍓 成功/失败条件
    • 🍌 重试机制
    • 🥂 重定向
    • 🍔 Gzip解压
    • 🎂 日志管理
    • ⚽ 回调函数
    • 🍟 异步请求
    • 🛡️ HTTPS
    • 🍪 使用Cookie
    • 🛸 使用代理
    • 🍉 上传下载
    • 🚑 异常处理
  • 编程式接口

    • 请求API

      • 🚀 请求对象
      • 🚢 请求属性
      • ✨ 执行请求
      • 🎊 后端框架
      • 🎪 请求类型
      • 🔮 请求地址
      • 🧀 URL 参数
      • 🚅 请求头
      • 🚋 请求体
      • ⚓ 回调函数
      • 🚁 异步请求
      • 🥯 Cookie
      • 🍜 成功/失败条件
      • 🌶️ 重试机制
      • ⛵ 重定向
      • 🛰️ 请求代理
    • 响应API

      • 🌠 响应对象
      • ✒️ 读取数据
      • 🦋 响应状态码
      • 🏥 响应错误处理
      • 🎧 响应头
      • 🥞 Cookie
  • 模板表达式

    • 🍬 Hello World
    • 🍹 配置属性引用
    • 🍖 变量引用
    • 🥃 动态变量绑定
    • 🥗 参数序号引用
    • 🍍 引用对象属性
    • 🥝 调用对象方法
  • 高级特性

    • 🥪 拦截器
    • 🍏 自定义注解
    • 🍇 组合注解
    • 🥑 自定义转换器
  • v1.5.30文档
  • 声明式接口
公子骏
2022-07-01
目录

👔 请求体

在POST和PUT等请求方法中,通常使用 HTTP 请求体进行传输数据。在 Forest 中有多种方式设置请求体数据。

# @Body 注解

您可以使用@Body注解修饰参数的方式,将传入参数的数据绑定到 HTTP 请求体中。

@Body注解修饰的参数一定会绑定到请求体中,不用担心它会出现在其他地方

/**
 * 默认body格式为 application/x-www-form-urlencoded,即以表单形式序列化数据
 */
@Post("http://localhost:8080/user")
String sendPost(@Body("username") String username,  @Body("password") String password);
1
2
3
4
5

# 表单格式

上面使用 @Body 注解的例子用的是普通的表单格式,也就是contentType属性为application/x-www-form-urlencoded的格式,即contentType不做配置时的默认值。

表单格式的请求体以字符串 key1=value1&key2=value2&...&key{n}=value{n} 的形式进行传输数据,其中value都是已经过 URL Encode 编码过的字符串。









 



/**
 * contentType属性设置为 application/x-www-form-urlencoded 即为表单格式,
 * 当然不设置的时候默认值也为 application/x-www-form-urlencoded, 也同样是表单格式。
 * 在 @Body 注解的 value 属性中设置的名称为表单项的 key 名,
 * 而注解所修饰的参数值即为表单项的值,它可以为任何类型,不过最终都会转换为字符串进行传输。
 */
@Post(
    url = "http://localhost:8080/user",
    contentType = "application/x-www-form-urlencoded"
)
String sendPost(@Body("key1") String value1,  @Body("key2") Integer value2, @Body("key3") Long value3);
1
2
3
4
5
6
7
8
9
10
11

调用后产生的结果可能如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/x-www-form-urlencoded
BODY:
    key1=xxx&key2=1000&key3=9999

当@Body注解修饰的参数为一个对象,并注解的value属性不设置任何名称的时候,会将注解所修饰参数值对象视为一整个表单,其对象中的所有属性将按 属性名1=属性值1&属性名2=属性值2&...&属性名{n}=属性值{n} 的形式通过请求体进行传输数据。

/**
 * contentType 属性不设置默认为 application/x-www-form-urlencoded
 * 要以对象作为表达传输项时,其 @Body 注解的 value 名称不能设置
 */
@Post("http://localhost:8080/hello/user")
String send(@Body User user);
1
2
3
4
5
6

调用后产生的结果如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/x-www-form-urlencoded
BODY:
    username=foo&password=bar

# JSON格式

# @JSONBody注解修饰对象

发送JSON非常简单,只要用@JSONBody注解修饰相关参数就可以了,该注解自1.5.0-RC1版本起可以使用。 使用@JSONBody注解的同时就可以省略 contentType = "application/json"属性设置。

/**
 * 被@JSONBody注解修饰的参数会根据其类型被自定解析为JSON字符串
 * 使用@JSONBody注解时可以省略 contentType = "application/json"属性设置
 */
@Post("http://localhost:8080/hello/user")
String helloUser(@JSONBody User user);

1
2
3
4
5
6
7

调用后产生的结果如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/json
BODY:
    {"username": "foo", "password": "bar"}

# @JSONBody注解修饰键值对

@JSONBody注解可以按键值对拆分成多个参数进行传入,在发送的时候再合成一个完成的JSON字符串:


/**
 * 按键值对分别修饰不同的参数
 * 这时每个参数前的 @JSONBody 注解必须填上 value 属性或 name 属性的值,作为JSON的字段名称
 */
@Post("http://localhost:8080/hello/user")
String helloUser(@JSONBody("username") String username, @JSONBody("password") String password);
1
2
3
4
5
6
7

如调用helloUser("foo", "bar");会产生如下结果:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/json
BODY:
    {"username": "foo", "password": "bar"}

# @JSONBody注解修饰集合对象

@JSONBody注解也支持Map、List等集合类型参数

/**
 * 被@JSONBody注解修饰的Map类型参数会被自定解析为JSON字符串
 */
@Post(url = "http://localhost:8080/hello/user")
String helloUser(@JSONBody Map<String, Object> user);
1
2
3
4
5

若调用代码如下:

Map<String, Object> map = new HashMap<>();
map.put("name", "foo");
map.put("password", "bar");
client.helloUser(map);
1
2
3
4

会产生如下结果:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/json
BODY:
    {"username": "foo", "password": "bar"}

List等列表对象也同理:

/**
 * 被@JSONBody注解修饰的List类型参数会被自定解析为JSON字符串
 */
@Post(url = "http://localhost:8080/hello/user-names")
String helloUserNames(@JSONBody List<String> usernames);
1
2
3
4
5

若调用代码如下:

List<String> names = Lists.newArrayList("A", "B", "C");
client.helloUserNames(names);
1
2

会产生如下结果:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/json
BODY:
    ["A", "B", "C"]

# @JSONBody注解修饰字符串

@JSONBody注解还支持字符串类型参数,可以直接传入一个JSON字符串进行传输

/**
 * 直接修饰一个JSON字符串
 */
@Post("http://localhost:8080/hello/user")
String helloUser(@JSONBody String userJson);
1
2
3
4
5

# @Body注解 + contentType

除了@JSONBody注解,使用@Body注解也可以,只要将contentType属性或Content-Type请求头指定为application/json便可。



 



@Post(
    url = "http://localhost:8080/hello/user",
    contentType = "application/json"
)
String send(@Body User user);
1
2
3
4
5

调用后产生的结果如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/json
BODY:
    {"username": "foo", "password": "bar"}

# XML格式

# @XMLBody注解修饰对象

发送XML也非常简单,只要用@XMLBody注解修饰相关参数就可以了,该注解自1.5.0-RC1版本起可以使用。

/**
 * 被@JSONBody注解修饰的参数会根据其类型被自定解析为XML字符串
 * 其修饰的参数类型必须支持JAXB,可以使用JAXB的注解进行修饰
 * 使用@XMLBody注解时可以省略 contentType = "application/xml"属性设置
 */
@Post("http://localhost:8080/hello/user")
String sendXmlMessage(@XMLBody User user);

1
2
3
4
5
6
7
8

要注意的是,这里的User对象要绑定JAXB注解:

@XmlRootElement(name = "misc")
public class User {

    private String usrname;

    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

调用传入User对象后的结果如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/xml
BODY:
    <misc><username>foo</username><password>bar</password></misc>

# @XMLBody注解修饰字符串

@XMLBody支持的另一种形式就是直接传入一个XML字符串

/**
 * @XMLBody注解可以修饰一个字符串参数,作为要传输的XML字符串
 */
@Post("http://localhost:8080/hello/user")
String sendXmlMessage(@XMLBody String userXml);
1
2
3
4
5

# Protobuf格式

在使用 Protobuf 之前,首先需要有引入 google 的 protobuf 包依赖, 如何加相关依赖请参见《添加Protobuf框架依赖》

假设,您已经有了 protobuf 的相关依赖包,而且也有了 protobuf 生成的数据类,如 ProtobufProto.MyData

就可以直接使用 @ProtobufBody 注解修饰 protobuf 生成的数据类为类型的参数

@Post("/proto/data")
ProtobufProto.MyData protobufTest2(@ProtobufBody ProtobufProto.MyData myData);

// 调用改方法,会将 myData 数据对象自动转换为 Protobuf 格式字节流
// 并发送到服务端
// @ProtobufBody 在默认请情况下会将 Content-Type 设为
// application/x-protobuf
1
2
3
4
5
6
7

若要不想使 Content-Type 为 application/x-protobuf, 可以自行设置

@Post(url = "/proto/data", contentType = "application/octet-stream")
ProtobufProto.MyData protobufTest2(@ProtobufBody ProtobufProto.MyData myData);

// 调用改方法,会将 myData 数据对象自动转换为 Protobuf 格式字节流
// 并发送到服务端
// 且此时 Content-Type 为
// application/octet-stream
1
2
3
4
5
6
7

# 二进制格式

对于application/otect-stream等二进制形式的Body数据,直接用 @Body 注解修饰参数即可。

现在支持的二进制 Content-Type 有:

  • application/octect-stream
  • image/* (包括 image/png, image/jpeg 等)
/**
 * 发送Byte数组类型数据
 */
@Post(
        url = "/upload/${filename}",
        contentType = "application/octet-stream"
)
String sendByteArryr(@Body byte[] body, @Var("filename") String filename);

/**
 * 发送File类型数据
 */
@Post(
    url = "/upload/${filename}",
    contentType = "application/octet-stream"
)
String sendFile(@Body File file, @Var("filename") String filename);

/**
 * 发送输入流类型数据
 */
@Post(
    url = "/upload/${filename}",
    contentType = "application/octet-stream"
)
String sendInputStream(@Body InputStream inputStream, @Var("filename") String filename);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

文档导航

关于更多二进制传输以及文件上传内容,请见《上传下载》

# data 属性

您也可以通过@Request、以及@Get、@Post等请求注解的data属性把数据添加到请求体。需要注意的是只有当type为POST、PUT、PATCH这类 HTTP Method 时,data属性中的值才会绑定到请求体中,而GET请求在有些情况会绑定到url的参数中。

具体type属性和data属性数据绑定位置的具体关系如下表:

type data属性数据绑定位置 支持的contentType或Content-Type请求头
GET url参数部分 只有application/x-www-form-urlencoded
POST 请求体 任何contentType
PUT 请求体 任何contentType
PATCH 请求体 任何contentType
HEAD url参数部分 只有application/x-www-form-urlencoded
OPTIONS url参数部分 只有application/x-www-form-urlencoded
DELETE url参数部分 只有application/x-www-form-urlencoded
TRACE url参数部分 只有application/x-www-form-urlencoded

data属性在POST请求中绑定请求体

public interface MyClient {

    @Request(
            url = "http://localhost:8080/hello/user",
            type = "post",
            data = "username=foo&password=bar",
            headers = {"Accept:text/plain"}
    )
    String dataPost();
}
1
2
3
4
5
6
7
8
9
10

该接口调用后所实际产生的 HTTP 请求如下:

POST http://localhost:8080/hello/user
HEADER:
    Accept:text/plain
BODY:
    username=foo&password=bar

在data属性中进行数据绑定:

public interface MyClient {

    /**
     * 这里 data 属性中设置的字符串内容会绑定到请求体中
     * 其中 ${0} 和 ${1} 为参数序号绑定,会将序号对应的参数绑定到字符串中对应的位置
     * ${0} 会替换为 username 的值,${1} 会替换为 password 的值
     */
    @Request(
            url = "http://localhost:8080/hello/user",
            type = "post",
            data = "username=${0}&password=${1}",
            headers = {"Accept:text/plain"}
    )
    String dataPost(String username, String password);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

提示

其中${数字}的语法用到了《参数序号引用》

如果调用方代码如下所示:

myClient.dataPost("foo", "bar");
1

实际产生的 HTTP 请求如下:

POST http://localhost:8080/hello/user
HEADER:
    Accept: text/plain
BODY:
    username=foo&password=bar

您可以直接把 JSON 数据加入到请求体中,其中header设置为Content-Type: application/json

public interface MyClient {

    @Request(
            url = "http://localhost:8080/hello/user",
            type = "post",
            data = "{\"username\": \"${0}\", \"password\": \"${1}\"}",
            headers = {"Content-Type: application/json"}
    )
    String postJson(String username, String password);
}
1
2
3
4
5
6
7
8
9
10

如果调用方代码如下所示:

myClient.postJson("foo", "bar");
1

实际产生的 HTTP 请求如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/json
BODY:
    {"username": "foo", "password": "bar"}

把 XML 数据加入到请求体中,其中header设置为Content-Type: application/json

public interface MyClient {

    @Request(
            url = "http://localhost:8080/hello/user",
            type = "post",
            data = "<misc><username>${0}</username><password>${1}</password></misc>",
            headers = {"Content-Type: application/xml"}
    )
    String postXml(String username, String password);
}
1
2
3
4
5
6
7
8
9
10

如果调用方代码如下所示:

myClient.postXml("foo", "bar");
1

实际产生的 HTTP 请求如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/xml
BODY:
    <misc><username>foo</username><password>bar</password></misc>

# 请求体类型

友情提示

请求体类型的概念在v1.5.14版本后引入的,如果版本过低请使用最新版本

以上例子都是通过设置 Content-Type 头来确定请求体的类型 (也是比较推荐的方式)

但事实上,请求体的最终发送的格式是由Encoder(编码器)决定的,而一个请求用何编码器则使用请求体类型决定的,而且请求体类型在不设置的情况下可以由Content-Type推导出来!

有点绕哈~,但看下面就清楚了

请求体数据格式的推导过程:

Content-Type 头 => BodyType 请求体类型 => Encoder 编码器 => 具体的请求体数据格式
1
2
3

比如:如果设置了 Content-Type 请求头为application/json,而请求体类型没有设置,那么请求体类型就会被推导为json类型,下一步则会根据接口配置信息和全局配置信息选择一个适配的 JSON 编码器(如:Jackson 转换器)

也即是说,如果设置了请求体类型,那么 Content-Type 头就不会再影响到最终请求体的具体数据格式

同理,如果设置了Encoder,那么请求体类型就被无视了,Content-Type 头设置成什么也无关紧要

请求体数据格式相关属性优先级:

Encoder 编码器 > BodyType 请求体类型 > Content-Type 头
1
2
3

如果明白了这一点,就可以实现一些不常见的请求,如:

① 不填 Content-Type 头,而请求体是您想要的格式(如 JSON);

② Content-Type 为application/json,但请求体却是表单格式

# @BodyType 注解

请求体类型可以通过@BodyType注解来指定

/**
 * 该方法不设置 Content-Type, 也可以指定 JSON 格式请求体
 */
@Post(url = "/")
@BodyType("json")
String send(@Body("name") String name, @Body("value") Object value);
1
2
3
4
5
6

调用后产生的结果如下:

POST http://localhost:8080/hello/user
BODY:
    {"name": "foo", "value": "bar"}

可以看到,该请求中没有 Content-Type 请求头,但请求体是 JSON 数据

@BodyType注解也可用于发送请求体格式与 ContentType 头相异的数据

/**
 * 此请求Content-Type头为 application/json
 * 而请求体的格式却可以是表单格式
 */
@BodyType("form")
@Post(url = "/", contentType = "application/json")
String send(@Body("name") String name, @Body("value") Object value);
1
2
3
4
5
6
7

调用后产生的结果如下:

POST http://localhost:8080/hello/user
HEADER:
    Content-Type: application/json
BODY:
    name=foo&value=bar

该请求 Content-Type 为 application/json,但请求体却是表单格式数据

# 可指定的请求体类型

Forest有多种请求体类型可供选择,分别是:text,form, json, xml, binary, multipart, protobuf

请求体类型 描述
text 文本格式,即普通的字符串
form 表单格式,即 x-www-form-urlencoded 格式
json JSON 格式
xml XML 格式
binary 二进制格式,即二进制字节流
multipart 多部件格式,即 multipart/data-form 格式
protobuf Protobuf 格式

# 指定 Encoder

@BodyType注解同时可以指定Encoder

/**
 * 指定请求体格式为json的同时,指定Encoder为Jackson转换器
 */
@BodyType(type = "json", encoder = ForestJacksonConverter.class)
@Post(url = "/", contentType = ContentType.APPLICATION_X_WWW_FORM_URLENCODED)
String send(@Body Entry entry);
1
2
3
4
5
6

# JSON 编码器快捷注解

如果要指定特定JSON转换器为某一请求的Encoder,可以使用对应 JSON 编码器的快捷注解

/**
 * 指定 Fastjson 为 Encoder
 */
@FastjsonEncoder
@Post("/")
String sendFastjson(@Body Entry entry);

/**
 * 指定 Jackson 为 Encoder
 */
@JacksonEncoder
@Post("/")
String sendJackson(@Body Entry entry);

/**
 * 指定 Gson 为 Encoder
 */
@GsonEncoder
@Post("/")
String sendGson(@Body Entry entry);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 延迟请求体参数

延迟请求体参数 (也称作 Lambda 请求体参数),在您需要不马上求值的情况使用。

有很多情况,Body的参数值不能马上得出,而是在请求发送前的那一刻(所有请求参数都到位时)才能得出,典型的例子就是加签验证的场景(在请求体参数中添加一个参数token,而token的值是对整个body(除token之外所有参数)做加密的结果)

// 使用 Lazy 作为 Body 的参数类型
@Post("/data")
String sendData(@Body("a") String a, @Body("b") String b, @Body("token") Lazy<String> token);
1
2
3

在调用的时候,传入 Lambda

myClient.sendData("Foo", "Bar", req -> {
    // req 为请求对象
    // 返回值将作为参数值添加到请求体中
    return "";
});
1
2
3
4
5

在 Lambda 参数中可以调用req.body().encode()可以立即序列化整个请求体的数据

但会自动排除 Lambda 所对应的 Body 项(即 token 字段),而不用担心会造成死循环

// Lambda 的参数为 ForestRequest 请求对象
// 调用 req.body().encode() 可以立即序列化整个请求体的数据
myClient.sendData("Foo", "Bar", req -> Base64.encode(req.body().encode()));
1
2
3
帮助我们改善此文档 (opens new window)
上次更新: 2023/03/14, 11:24:19
🍭 请求头
🍮 后端框架

← 🍭 请求头 🍮 后端框架→

Theme by Vdoing | Copyright © 2016-2023 公子骏 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式