- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章SpringBoot在RequestBody中使用枚举参数案例详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前文说到 优雅的使用枚举参数 和 实现原理,本文继续说一下如何在 RequestBody 中优雅使用枚举.
本文先上实战,说一下如何实现。在 优雅的使用枚举参数 代码的基础上,我们继续实现.
需求与前文类似,只不过这里需要是在 RequestBody 中使用。与前文不同的是,这种请求是通过 Http Body 的方式传输到后端,通常是 json 或 xml 格式,Spring 默认借助 Jackson 反序列化为对象.
同样的,我们需要在枚举中定义 int 类型的 id、String 类型的 code,id 取值不限于序号(即从 0 开始的 orinal 数据),code 不限于 name。客户端请求过程中,可以传 id,可以传 code,也可以传 name。服务端只需要在对象中定义一个枚举参数,不需要额外的转换,即可得到枚举值.
好了,接下来我们定义一下枚举对象.
先定义我们的枚举类GenderIdCodeEnum,包含 id 和 code 两个属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public
enum
GenderIdCodeEnum
implements
IdCodeBaseEnum {
MALE(
1
,
"male"
),
FEMALE(
2
,
"female"
);
private
final
Integer id;
private
final
String code;
GenderIdCodeEnum(Integer id, String code) {
this
.id = id;
this
.code = code;
}
@Override
public
String getCode() {
return
code;
}
@Override
public
Integer getId() {
return
id;
}
}
|
这个枚举类的要求与前文一致,不清楚的可以再去看一下.
在定义一个包装类GenderIdCodeRequestBody,用于接收 json 数据的请求体:
1
2
3
4
5
6
|
@Data
public
class
GenderIdCodeRequestBody {
private
String name;
private
GenderIdCodeEnum gender;
private
long
timestamp;
}
|
除了GenderIdCodeEnum参数外,其他都是示例,所以随便定义一下.
前奏铺垫好,接下来入正题了。Jackson 提供了两种方案:
这种方案中,我们首先需要实现JsonDeserialize抽象类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public
class
IdCodeToEnumDeserializer
extends
JsonDeserializer<BaseEnum> {
@Override
public
BaseEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws
IOException {
final
String param = jsonParser.getText();
// 1
final
JsonStreamContext parsingContext = jsonParser.getParsingContext();
// 2
final
String currentName = parsingContext.getCurrentName();
// 3
final
Object currentValue = parsingContext.getCurrentValue();
// 4
try
{
final
Field declaredField = currentValue.getClass().getDeclaredField(currentName);
// 5
final
Class<?> targetType = declaredField.getType();
// 6
final
Method createMethod = targetType.getDeclaredMethod(
"create"
, Object.
class
);
// 7
return
(BaseEnum) createMethod.invoke(
null
, param);
// 8
}
catch
(NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException e) {
throw
new
CodeBaseException(ErrorResponseEnum.PARAMS_ENUM_NOT_MATCH,
new
Object[] {param},
""
, e);
}
}
}
|
然后在指定枚举字段上定义@JsonDeserialize注解,比如:
1
2
|
@JsonDeserialize
(using = IdCodeToEnumDeserializer.
class
)
private
GenderIdCodeEnum gender;
|
具体说一下每行的作用:
@JsonDeserialize
注解的字段,此时currentName
的值是gender
;GenderIdCodeRequestBody
对象;Class
对象,以及字段名gender
获取Field
对象,为第 5 步做准备;gender
字段对应的枚举类型,也即是GenderIdCodeEnum
。之所以这样做,是要实现一个通用的反序列化类;create
,请求参数是Object
;create
方法,将第一步获取的请求参数传入。我们来看一下枚举类中定义的create方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
static
GenderIdCodeEnum create(Object code) {
final
String stringCode = code.toString();
final
Integer intCode = BaseEnum.adapter(stringCode);
for
(GenderIdCodeEnum item : values()) {
if
(Objects.equals(stringCode, item.name())) {
return
item;
}
if
(Objects.equals(item.getCode(), stringCode)) {
return
item;
}
if
(Objects.equals(item.getId(), intCode)) {
return
item;
}
}
return
null
;
}
|
为了性能考虑,我们可以提前定义三组 map,分别以 id、code、name 为 key,以枚举值为 value,这样就可以通过 O(1) 的时间复杂度返回了。可以参考前文的Converter类的实现逻辑.
这样,我们就可以实现精准转换了.
这种方案是全范围攻击了,只要是 Jackson 参与的反序列化,只要其中有目标枚举参数,就会受到这种进入这种方案的逻辑中。这种方案是在枚举类中定义一个静态转换方法,通过@JsonCreator注解注释,Jackson 就会自动转换了.
这个方法的定义与方案一中的create方法完全一致,所以只需要在create方法上加上注解即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@JsonCreator
(mode = Mode.DELEGATING)
public
static
GenderIdCodeEnum create(Object code) {
final
String stringCode = code.toString();
final
Integer intCode = BaseEnum.adapter(stringCode);
for
(GenderIdCodeEnum item : values()) {
if
(Objects.equals(stringCode, item.name())) {
return
item;
}
if
(Objects.equals(item.getCode(), stringCode)) {
return
item;
}
if
(Objects.equals(item.getId(), intCode)) {
return
item;
}
}
return
null
;
}
|
其中Mode类有四个值:DEFAULT、DELEGATING、PROPERTIES、DISABLED,这四种的差别会在原理篇中说明。还是那句话,对于应用类技术,我们可以先知其然,再知其所以然,也一定要知其所以然.
先定义一个 controller 方法:
1
2
3
4
5
|
@PostMapping
(
"gender-id-code-request-body"
)
public
GenderIdCodeRequestBody bodyGenderIdCode(
@RequestBody
GenderIdCodeRequestBody genderRequest) {
genderRequest.setTimestamp(System.currentTimeMillis());
return
genderRequest;
}
|
然后定义测试用例,还是借助 JUnit5:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@ParameterizedTest
@ValueSource
(strings = {
"\"MALE\""
,
"\"male\""
,
"\"1\""
,
"1"
})
void
postGenderIdCode(String gender)
throws
Exception {
final
String result = mockMvc.perform(
MockMvcRequestBuilders.post(
"/echo/gender-id-code-request-body"
)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8)
.content(
"{\"gender\": "
+ gender +
", \"name\": \"看山\"}"
)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print())
.andReturn()
.getResponse()
.getContentAsString();
ObjectMapper objectMapper =
new
ObjectMapper();
final
GenderIdCodeRequestBody genderRequest = objectMapper.readValue(result, GenderIdCodeRequestBody.
class
);
Assertions.assertEquals(GenderIdCodeEnum.MALE, genderRequest.getGender());
Assertions.assertEquals(
"看山"
, genderRequest.getName());
Assertions.assertTrue(genderRequest.getTimestamp() >
0
);
}
|
本文主要说明了如何在 RequestBody 中优雅的使用枚举参数,借助了 Jackson 的反序列化扩展,可以定制类型转换逻辑。碍于文章篇幅,没有罗列大段代码。关注公号「看山的小屋」回复 spring 可以获取源码。关注我,下一篇我们进入原理篇.
到此这篇关于SpringBoot在RequestBody中使用枚举参数案例详解的文章就介绍到这了,更多相关SpringBoot在RequestBody中使用枚举参数内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.howardliu.cn/springboot-action-enum-params-in-requestbody/ 。
最后此篇关于SpringBoot在RequestBody中使用枚举参数案例详解的文章就讲到这里了,如果你想了解更多关于SpringBoot在RequestBody中使用枚举参数案例详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
这个问题在这里已经有了答案: Oracle: merging two different queries into one, LIKE & IN (1 个回答) 8年前关闭。 我有以下代码: case
我查阅过此页面:http://dev.mysql.com/doc/refman/5.1/en/case.html以及这个,但无法获得一个简单的程序来工作...... 更新:为了明确我想要做什么:我想从
有什么办法可以优化下面的查询吗? SELECT DATE_FORMAT(a.duedate,'%d-%b-%y') AS dte, duedate, SUM(CASE WHEN (typeofnoti
我进退两难,以下 SQL 查询的结果是什么以及它是如何工作的: SELECT ... CASE WHEN (a.FIELD=1 AND b.FIELD=2) THEN 1 WHEN
问题:输入年,月,打印对应年月的日历。 示例: 问题分析: 1,首先1970年是Unix系统诞生的时间,1970年成为Unix的元年,1970年1月1号是星期四,现在大多的手机的日历功能只能显
**摘要:**介绍了Angular中依赖注入是如何查找依赖,如何配置提供商,如何用限定和过滤作用的装饰器拿到想要的实例,进一步通过N个案例分析如何结合依赖注入的知识点来解决开发编程中会遇到的问题。 本
我想拥有自动伴侣类apply case 类的构造函数来为我执行隐式转换,但无法弄清楚如何这样做。我到处搜索,我能找到的最接近的答案是 this问题(我将解释为什么它不是我在下面寻找的)。 我有一个看起
您好,我已经浏览了“多列案例”问题,但没有看到与此相同的内容,所以我想我应该问一下。 基本上我有两个我想要连接的表(都是子查询的结果)。它们具有相同的列名称。如果我加入他们的 ID 和 SELECT
我发现了一些类型推断的非直觉行为。因此,语义等效代码的工作方式不同,具体取决于编译器推断出的有关函数返回类型的信息。当您在最小单元测试中重现此案例时,或多或少会清楚发生了什么。但我担心在编写框架代码时
CREATE TABLE test ( sts_id int , [status1] int , [status2] int , [status3] int , [status4] int ) INS
我有以下声明: SELECT Dag AS Dag, CASE Jaar WHEN 2013 THEN Levering END AS '2013', CASE
我想做的是为所有高于平均时间、平均时间和低于平均时间的游乐设施获取平均tip_portion。所以返回3行。当我运行它时,它显示: ERROR: missing FROM-clause entry
我正在尝试设置一个包含以下字段的报告: 非常需要报告来显示日期、该日期内的总记录(因此我按日期分组),然后按小时计算 12 小时工作日(从上午 8 点到晚上 8 点)我需要计算记录在这些时间内出现的时
我有这个查询 SELECT users.name FROM users LEFT JOIN weapon_stats ON users.id = weapon_stats.zp_id WHERE we
我正在尝试按收视率等级获取不同视频的计数。我有下表: vid_id views 1 6 1 10 1 900 2 850 2 125000
假设我有一个如下所示的 SQL 语句: select supplier, case when platform in (5,6) then 'mobile' when p
我有一个表测试 TestNumber (int primary key) InactiveBitwise (int) 我执行以下命令: UPDATE tests SET CASE WH
我有一个像这样的表(name=expense): id amount date 1 -1687 2014-01-02 00:00:00.0 2 11000 2014-01-02 0
我有一个 multimap 定义 typedef std::pair au_pair; //vertices typedef std::pair acq_pair; //ch qlty specifi
我有一个有点像枚举的类,它的每个实例都有一个唯一的 int 值,该值从 0 开始并在每个新实例时递增。 class MyEnumLikeClass { static int NextId =
我是一名优秀的程序员,十分优秀!