






















背景:
框架中的定义了一个统一参数,
public class OperationParam { String operation; ... Map<String, Object> arg; }
这个结构用于接收常规的RequestBody没有任何问题,但是我的一个新场景是需要上传文件,于是定义了一个新类继承他
public class OperationWithFileParam extends OperationParam{ /** * 文件参数 */ @NonNull MultipartFile file; }
controller如下:
@PostMapping(value = "/invoke", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public SingleResponse<Object> invokeMethodWithFile(@Validated @ModelAttribute OperationWithFileParam param) { return SingleResponse.success(service.invoke(param, false)); }
OK,前端正常传入form-data参数,这时候问题来了:
报错:无法将String转换为一个map
即便是在OperationWithFileParam 中写了
示例:
public void setArg(String arg){
setArg(JsonUtils.parseObject(arg, Map.class));
}
也不行,还没执行到这里就报错了
为什么直接使用 arg 参数会报错:
arg 参数是 String 类型(JSON 字符串)setArg(Map) 方法,认为 arg 属性应该是 Map 类型ModelAttributeMethodProcessor.resolveArgument() 时抛出 typeMismatch 异常setArg(Object) 方法有两种解决办法:
方案1.快速止血,定义一个新参数,如String argJson,
String argJson; public void setArgJson(String argJson) { if (StringUtils.hasText(argJson)) { Map<String, Object> argMap = JsonUtils.parseObject(argJson, new TypeReference<Map<String, Object>>() { }); setArg(argMap); } }
为什么 argJson 可以工作:
argJson 对应的是 String 类型的字段和 setterargJson 字段setArgJson(String) 方法内部解析 JSON 并调用父类的 setArg(Map)这个方案虽然可以工作,但是始终不是那么的优雅。
方案2:为ModelAttribute指定一个InitBinder
我用的是SpringBoot3,有两种指定方案,我选择了老的那种,看起来简单。
@PostMapping(value = "/invoke", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public SingleResponse<Object> invokeMethodWithFile(@Validated @ModelAttribute("operationInvokeFile") OperationWithFileParam param) { return SingleResponse.success(service.invoke(param, false)); } /** * 初始化参数转换器 * 看起来简洁明了 * * @param binder * 参数绑定器 */ @InitBinder("operationInvokeFile") public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(Map.class, "arg", new PropertyEditorSupport() { @Override public void setAsText(String text) throws IllegalArgumentException { if (StringUtils.hasText(text)) { setValue(JsonUtils.parseObject(text, new TypeReference<Map<String, Object>>() {})); } } }); } /** * 初始化参数转换器 * springboot3 支持的做法,看起来稍微复杂一些 * * @param binder * 参数绑定器 */ @InitBinder("operationInvokeFile") public void initBinder(WebDataBinder binder) { binder.addCustomFormatter(new ArgJsonFormatter(), "arg"); } private static class ArgJsonFormatter implements Formatter<Map<String, Object>> { @Override public Map<String, Object> parse(String text, Locale locale) { if (!StringUtils.hasText(text)) { return Collections.emptyMap(); } return JsonUtils.parseObject(text, new TypeReference<Map<String, Object>>() {}); } @Override public String print(Map<String, Object> object, Locale locale) { return JsonUtils.toJsonString(object); } }
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。