Forest Forest
首页
  • 当前版本

    • v1.6.x
  • 历史版本

    • v1.5.36
    • v1.5.35
    • v1.5.33
    • v1.5.30 ~ v1.5.32
    • v1.5.0 ~ v1.5.28
  • 插件

    • ForestX
  • 问答

    • 常见问题
    • 更新记录
案例
  • 加入群聊
  • 赞助我们
  • 参与贡献
  • 贡献者们
  • 项目介绍
  • 开发团队
  • 关于作者
  • MaxKey - 业界领先的身份管理和认证产品 (opens new window)
  • Snowy - 国内首个国密前后端分离快速开发平台 (opens new window)
  • Eoapi - 一个开源、可拓展的 API 工具平台 (opens new window)
  • Fast Request - IDEA版Postman,为简化API调试而生 (opens new window)
  • Bean Searcher - 专注高级查询的只读 ORM (opens new window)
  • zyplayer-doc - 可私有化部署的文档与知识库管理平台 (opens new window)
  • frSimple - 中后台脚手架/小程序商城 (opens new window)
  • Gitee (opens new window)
  • Github (opens new window)
  • GitCode (opens new window)
首页
  • 当前版本

    • v1.6.x
  • 历史版本

    • v1.5.36
    • v1.5.35
    • v1.5.33
    • v1.5.30 ~ v1.5.32
    • v1.5.0 ~ v1.5.28
  • 插件

    • ForestX
  • 问答

    • 常见问题
    • 更新记录
案例
  • 加入群聊
  • 赞助我们
  • 参与贡献
  • 贡献者们
  • 项目介绍
  • 开发团队
  • 关于作者
  • MaxKey - 业界领先的身份管理和认证产品 (opens new window)
  • Snowy - 国内首个国密前后端分离快速开发平台 (opens new window)
  • Eoapi - 一个开源、可拓展的 API 工具平台 (opens new window)
  • Fast Request - IDEA版Postman,为简化API调试而生 (opens new window)
  • Bean Searcher - 专注高级查询的只读 ORM (opens new window)
  • zyplayer-doc - 可私有化部署的文档与知识库管理平台 (opens new window)
  • frSimple - 中后台脚手架/小程序商城 (opens new window)
  • Gitee (opens new window)
  • Github (opens new window)
  • GitCode (opens new window)
  • 入门

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

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

    • 🧱 构建接口
    • 🍀 请求方法
    • 🚚 请求地址
    • 🎈 URL 参数
    • 🍭 请求头
    • 👔 请求体
    • 🍮 后端框架
    • 🧁 接口注解
    • 📬 接收数据
    • 🍛 数据转换
    • 🎀 对象字段
      • 🍓 成功/失败条件
      • 🍌 重试机制
      • 🥂 重定向
      • 🍔 Gzip解压
      • 🎂 日志管理
      • ⚽ 回调函数
      • 🍟 异步请求
      • 🛡️ HTTPS
      • 🍪 使用Cookie
      • 🛸 使用代理
      • 🍉 上传下载
      • 🚑 异常处理
    • 编程式接口

      • 请求API

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

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

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

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

    🎀 对象字段

    # 封装字段

    我们在之前的《URL 参数》、《请求头》和《请求体》等章节中学习过,URL 参数、请求头参数、请求体中的数据字段,乃至用于接受服务端返回的响应数据,都可以封装到自定义的对象中,如下所示:

    
    public class MyUser {
        
        private String userId;
        
        private String userName;
        
        // Getters, Setters
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    该自定义的MyUser类型,可以作为 Query 参数,请求头参数,以及请求体数据的字段

    // 作为 Query 参数
    @Get("http://localhost:8080/user/info")
    String getUserInfo(@Query MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user/info?userId=xx&userName=xxx
    // Query 参数名和 Java 字段名一致
    
    
    // 作为请求头
    @Get("http://localhost:8080/user/info2")
    String getUserInfo2(@Header MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user/info2
    // Headers:
    //    userId: xx
    //    userName: xxx
    // 请求头参数名和 Java 字段名一致
    
    
    // 作为 Body 数据
    @Post(url = "http://localhost:8080/user", contentType="application/json")
    String submitUserInfo(@Body MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user
    // Body: {"userName": "xxx", "userId": "xx"}
    // JSON Body 字段名和 Java 字段名一致
    // 注:这里可能乱序了
    
    
    // 乃至其他数据格式的的 Body 数据
    @Post(url = "http://localhost:8080/user", contentType="application/x-www-form-urlencoded")
    String submitUserInfo(@Body MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user
    // Body: userName=xxx&userId=xx
    // Form Body 字段名和 Java 字段名一致
    // 注:这里可能乱序了
    
    
    // 以及接受响应数据
    @Post(url = "http://localhost:8080/user/{0}")
    MyUser getUser(String userId);
    // 响应 Response Body: 
    // {"userId": "xx", "userName": "xxx"}
    
    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44

    # 两大问题

    以上代码,都可以毫无问题的顺利执行,但如果发送和接受的数据字段名和自定义类型中的字段名不同呢?该如何处理?(比如: 要发送的请求参数为 user_id 和 user_name 这种 snake_case 风格)

    当然,您可以把 Java 类型中的字段名也改成这种下划线蛇形风格,但这不符合 Java 的命名规范,强行蛇形会显得十分违和。况且除了蛇形,还有 Pascal 命名、纯大写命名、以及古早的匈牙利命名等等。总不能有多少风格就定义多少 Java 类吧?

    此外,还有另一大麻烦,就是乱序问题,您在 MyUser 中定义的顺序是: 先 userId, 再 userName,可发送出去的却是: {"userName": "xxx", "userId": "xx"},巧的是此时正好用了加密或签名算法,此类算法又恰好对字段顺序敏感,字段顺序稍微变化以下,产生的签名就大为不同。而它能不能按您设想的顺序纯粹靠运气,那这样一来岂不是自定义 Java 类就不能用了?

    对于这两大问题,在 Forest 中虽然也没有直接提供任何 api 或注解,但能借助 JSON 框架来解决此类问题。

    # 配置 JSON 框架

    第一步,先要确认在项目中,Forest 使用的是哪个 JSON 框架作为请求的数据转换器,确认的方法如下:

    1. 如果在项目中, Fastjson2、Fastjson、Jackson、Gson 这四个框架中只依赖了其中一个,那就会以这个框架为 JSON 转换器
    2. 如果在项目中,同时依赖了其中多个框架,那么按 Fastjson2 > Fastjson > Jackson > Gson 这样的优先级来判断,Forest 会以优先级最高的框架作为 JSON 转换器
    3. 如果在项目中明确配置了全局 JSON 转换器,那就一定以这个框架为准

    推荐使用 第 3 种方法,因为现实世界的项目中,往往会依赖很多种 JSON 框架,很多情况下是通过不同的第三方依赖传递过来的,比如 springboot 就会传递依赖 Jackson。时间长了,很难搞清楚到底依赖了哪个 JSON 框架。

    但通过全局配置,就能很清楚地排除掉这种不确定性。具体如果配置请参见《配置全局转换器》

    # 指定字段别名

    完成上面步骤后,就可以使用 当前 JSON 转换器 的 JSON 框架所提供的 注解 来指定别名了

    这里以 Fastjson2 / Fastjson 为例进行说明 (与 Jackson 和 Gson 的用法类似):

    // 使用 Fastjson2 / Fastjson 框架注解来指定字段别名
    // 若 Forest 的 JSON 转换器是其他 JSON 框架,请使用相应框架的注解
    public class MyUser {
        
        @JSONField(name = "user_id")
        private String userId;
        
        @JSONField(name = "user_name")
        private String userName;
        
        // Getters, Setters
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    此时,再用 MyUser 类型作为参数就会以 别名 进行序列化和反序列化了,不管是作为 URL 参数、请求头参数、Body 数据字段,还是用于接受 Response 数据,它都能 Work

    // 作为 Query 参数
    @Get("http://localhost:8080/user/info")
    String getUserInfo(@Query MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user/info?user_name=xxx&user_id=xx
    // 按 snake_case 进行命名 query 参数
    // 注:这里可能乱序了
    
    
    // 作为请求头
    @Get("http://localhost:8080/user/info2")
    String getUserInfo2(@Header MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user/info2
    // Headers:
    //    user_name: xxx
    //    user_id: xx
    // 按 snake_case 进行命名请求头参数
    // 注:这里可能乱序了
    
    
    // 作为 Body 数据
    @Post(url = "http://localhost:8080/user", contentType="application/json")
    String submitUserInfo(@Body MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user
    // Body: {"user_name": "xxx", "user_id": "xx"}
    // 按 snake_case 进行命名 JSON Body 字段名
    // 注:这里可能乱序了
    
    
    // 乃至其他数据格式的的 Body 数据
    @Post(url = "http://localhost:8080/user", contentType="application/x-www-form-urlencoded")
    String submitUserInfo(@Body MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user
    // Body: user_name=xxx&user_id=xx
    // 按 snake_case 进行命名 Form Body 字段名
    // 注:这里可能乱序了
    
    
    // 以及接受响应数据
    @Post(url = "http://localhost:8080/user/{0}")
    MyUser getUser(String userId);
    // 响应 Response Body: 
    // {"user_id": "xx", "user_name": "xxx"}
    
    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46

    # 指定顺序

    JSON 框架的注解除了能指定别名,同样能指定顺序:

    // 使用 Fastjson2 / Fastjson 框架注解来指定字段顺序
    public class MyUser {
        
        @JSONField(name = "user_id", ordinal = 1)
        private String userId;
        
        @JSONField(name = "user_name", ordinal = 2)
        private String userName;
        
        // Getters, Setters
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    此时进行数据的序列化,就不会出现乱序问题

    // 作为 Query 参数
    @Get("http://localhost:8080/user/info")
    String getUserInfo(@Query MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user/info?user_id=xx&user_name=xxx
    // 按 snake_case 进行命名 query 参数
    // 没有乱序
    
    
    // 作为请求头
    @Get("http://localhost:8080/user/info2")
    String getUserInfo2(@Header MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user/info2
    // Headers:
    //    user_id: xx
    //    user_name: xxx
    // 按 snake_case 进行命名请求头参数
    // 没有乱序
    
    
    // 作为 Body 数据
    @Post(url = "http://localhost:8080/user", contentType="application/json")
    String submitUserInfo(@Body MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user
    // Body: {"user_id": "xx", "user_name": "xxx"}
    // 按 snake_case 进行命名 JSON Body 字段名
    // 没有乱序
    
    
    // 乃至其他数据格式的的 Body 数据
    @Post(url = "http://localhost:8080/user", contentType="application/x-www-form-urlencoded")
    String submitUserInfo(@Body MyUser myUser);
    // 产生请求: 
    // http://localhost:8080/user
    // Body: user_id=xx&user_name=xxx
    // 按 snake_case 进行命名 Form Body 字段名
    // 没有乱序
    
    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
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39

    对于 Fastjson2 / Fastjson 注解的其他用法,以及其他 JSON 框架的用法,可以阅读相关框架的官网文档

    帮助我们改善此文档 (opens new window)
    上次更新: 2024/12/26, 12:59:11
    🍛 数据转换
    🍓 成功/失败条件

    ← 🍛 数据转换 🍓 成功/失败条件→

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