接口说明:
@Tag(name = "标签管理")
@RestController
@RequestMapping("/admin/label")
public class LabelController {
@Autowired
private LabelInfoService service;
}
一、根据类型查询标签全部列表接口
接口名称:labelList
请求方式:Get
请求路径:/admin/label/list
请求参数:type
根据type类型使用条件查询:
@Operation(summary = "(根据类型)查询标签列表")
@GetMapping("list")
public Result<List<LabelInfo>> labelList(@RequestParam(required = false) ItemType type) {
LambdaQueryWrapper<LabelInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(type!=null,LabelInfo::getType,type); //当type不等于null时进行条件查询
List<LabelInfo> list = service.list(queryWrapper);
return Result.ok(list);
}
错误反思❌:根据业务流程来说,上述代码是没有问题的,但是经过测试之后,发现当传入type之后,无法传回来数据。日志显示报类型转换异常。

原因
首先我们要搞清楚具体的转换流程:
- 请求流程:前端传入type参数经过SpringMVC的WebDataBinder组件负责将HTTP的请求参数绑定到Controller方法的参数,并实现参数类型的转换 ,此时参数经历第一次类型转换;接着经过MybatisPlus的TypeHandler,用于处理Java中的实体对象与数据库之间的数据类型转换 ,此时经过第二次类型转换。

- 响应流程:SpringMVC中的
HTTPMessageConverter组件负责将Controller方法的返回值(Java对象)转换为HTTP响应体中的JSON字符串,或者将请求体中的JSON字符串转换为Controller方法中的参数(Java对象)。

WebDataBinder默认进行自动类型转换,例如
String到Integer、String到Date,String到Boolean等等,而且其中也包括了String到枚举类型(Enum)。但是!String到枚举类型的默认规则只是根据实例名称转换为枚举对象实例,比如“APARTMENT”只能转换成“ItemType.APARTMENT”,并不能映射到该属性code,相反亦是一样。
👍解决方法:
由于WebDataBinder依赖于Converter实现类型转换,我们便可以根据实际情况自定义Converter去实现类型转换。
/**
* 解决枚举类型不匹配的问题,自定义Converter
*/
@Component
public class StringToItemTypeConverter implements Converter<String, ItemType> {
@Override
public ItemType convert(String code) {
// ItemType.class.getEnumConstants(); //方法一:利用反射获取枚举类型对象
ItemType[] itemTypes = ItemType.values(); //方法二:使用values方法获取枚举类型对象
for (ItemType itemType : itemTypes) {
if (itemType.getCode().equals(Integer.valueOf(code))){ //使用getCode()方法判断与传入的code是否相等
return itemType;
}
}
throw new IllegalArgumentException("code"+code+"非法");
}
}
接着要去配置类添加自定义的Converter。
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer { //实现WebMvcConfigurer,并实现addFormatters方法。
@Autowired
private StringToItemTypeConverter stringToItemTypeConverter;
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(this.stringToItemTypeConverter); //添加自定义的Converter
}
}
接着启动项目测试:

优化方法👍:因为我们有着很多的枚举类,因此如果像上述那样每个都需要写一个自定义的Converter,而且每个Converter实现的代码都大同小异,工作量会变得很大。所以我们使用ConverterFactory更加合适,这个接口可以将同一个转换逻辑应用到一个接口的所有实现类。因此只需要定义一个Converter就可以应用于该接口的使用实现类。
@Component
public class StringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {
@Override
public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
return new Converter<String, T>() {
@Override
public T convert(String code) {
T[] typeEnumConstants = targetType.getEnumConstants();
for (T typeEnumConstant : typeEnumConstants) {
if (typeEnumConstant.equals(Integer.valueOf(code))){
return typeEnumConstant;
}
}
throw new IllegalArgumentException("code"+code+"错误");
}
};
}
}
接着也需要到配置类里添加ConverterFactory。
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private StringToBaseEnumConverterFactory stringToBaseEnumConverterFactory;
/**
* 增加一个自定义的Converter去解决枚举类型参数类型不匹配的问题
* @param registry
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(stringToBaseEnumConverterFactory);
}
}
二、保存或修改标签信息接口
接口名称:saveOrUpdate
请求方式:Post
请求路径:/admin/label/saveOrUpdate
请求参数:{
"id": 0,
"type": "",
"name": ""
}
@Operation(summary = "新增或修改标签信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdateLabel(@RequestBody LabelInfo labelInfo) {
service.saveOrUpdate(labelInfo);
return Result.ok();
}
三、根据ID删除标签接口
接口名称:deleteById
请求方式:Delete
请求路径:/admin/label/deleteById
请求参数:id
@Operation(summary = "根据id删除标签信息")
@DeleteMapping("deleteById")
public Result deleteLabelById(@RequestParam Long id) {
service.removeById(id);
return Result.ok();
}
1575

被折叠的 条评论
为什么被折叠?



