Spring中@RequestBody接收两类无共同字段对象的规范实现方案咨询
可行的实现方案
优先推荐从路由层做区分,其次是优化序列化逻辑,不建议使用异常捕获判断类型的临时方案。
方案1:通过请求头/请求参数路由到不同接口(最符合REST规范)
无需拆分接口路径,利用Spring MVC原生路由规则,根据请求头或请求参数的标记自动匹配对应处理方法,完全符合REST开发规范,也不需要手动处理类型转换。
示例代码如下:
// 处理Foobject类型请求,要求请求头携带X-Request-Type=FOO @PostMapping(path = "/", headers = "X-Request-Type=FOO") public String handleFoo(@RequestBody Foobject foo) { // 处理Foobject业务逻辑 } // 处理Barobject类型请求,要求请求头携带X-Request-Type=BAR @PostMapping(path = "/", headers = "X-Request-Type=BAR") public String handleBar(@RequestBody Barobject bar) { // 处理Barobject业务逻辑 }
如果不方便新增请求头,也可以使用URL参数作为区分规则,将headers属性替换为params即可,例如params = "type=FOO",调用时请求路径拼接?type=FOO即可。
方案2:自定义Jackson反序列化器(无感知适配类型)
如果无法协调调用方新增请求头/参数,可以通过自定义Jackson反序列化器的方式封装类型判断逻辑,上层业务代码无需感知转换过程。
- 先定义标记接口,所有允许的请求类型都实现该接口:
@JsonDeserialize(using = FooBarDeserializer.class) public interface FooBarRequest {} public class Foobject implements FooBarRequest { // 原有字段逻辑不变 } public class Barobject implements FooBarRequest { // 原有字段逻辑不变 }
- 实现自定义反序列化器,根据两个类独有的字段做类型判断:
public class FooBarDeserializer extends StdDeserializer<FooBarRequest> { public FooBarDeserializer() { super(FooBarRequest.class); } @Override public FooBarRequest deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); // 根据两类对象独有的字段做判断,示例为Foobject独有fooField,Barobject独有barField if (node.has("fooField")) { return p.getCodec().treeToValue(node, Foobject.class); } else if (node.has("barField")) { return p.getCodec().treeToValue(node, Barobject.class); } throw new IllegalArgumentException("无法识别的请求参数类型"); } }
- 控制器直接使用标记接口作为入参类型即可:
@PostMapping("/") public String doSomething(@RequestBody FooBarRequest request) { if (request instanceof Foobject foo) { // 处理Foobject逻辑 } else if (request instanceof Barobject bar) { // 处理Barobject逻辑 } }
方案3:现有逻辑优化(改动最小)
如果不想改动原有结构,也可以对现有临时方案做优化,避免异常捕获带来的性能损耗和逻辑误判,同时复用Spring容器托管的ObjectMapper实例:
@Autowired private ObjectMapper objectMapper; @PostMapping("/") public String doSomething(@RequestBody JsonNode jsonNode) throws JsonProcessingException { if (jsonNode.has("fooField")) { Foobject foo = objectMapper.treeToValue(jsonNode, Foobject.class); // 处理Foobject逻辑 } else if (jsonNode.has("barField")) { Barobject bar = objectMapper.treeToValue(jsonNode, Barobject.class); // 处理Barobject逻辑 } else { throw new IllegalArgumentException("不支持的请求类型"); } }
方案选择优先级
如果能协调调用方做少量调整,优先选择方案1,是最符合REST规范的实现;如果无法协调调用方,优先选择方案2,代码可维护性更高;如果只是临时优化不想改太多代码,选择方案3即可。
内容的提问来源于stack exchange,提问作者Jonh Snow
相关产品推荐
相关产品推荐

